Заметки об изучении виртуальной машины Java

Java JVM

Эта статья в основном организована изалхимия в золотоУчебные материалы JVM и «Углубленное понимание виртуальной машины Java», часть материалов собрана из Интернета, источник неизвестен

1. Спецификация JVM

1.1 Битовые операции

1.1.1 Целочисленный тип int

  • Исходный код: первый бит знака, 0 положительный, 1 отрицательный
  • Инверсный код: бит знака не перемещается, исходный код инвертируется
  • дополнять
    • Дополнение положительного числа: такое же, как исходный код
    • Отрицательное дополнение: знаковый бит не меняется, инверсный код добавляется на 1

example

-6
原码: 10000110
反码: 11111001
补码: 11111010
  • Зачем использовать дополнение до двух
    • может представлять 0 однозначно

Не используйте дополнение до двух, рассматривайте 0 как

正数:0000 0000  
负数:1000 0000  

непоследовательный
Используйте дополнение до двух:

负数:1000 0000  
反码:1111 111
补码:0000 0000 = 正数 

Использование дополнения до двух для положительных и отрицательных чисел эквивалентно использованию сложения
Вычисление выполняется с использованием дополнения до двух

补码示例

1.1.2 Одинарная точность с плавающей запятой

  • Представление
    单精度

когда экспонента

  • Все 0, дополнительные биты мантиссы равны 0
  • Если не все 0, дополнительные биты мантиссы равны 1
    Таким образом, биты мантиссы составляют 24 бита.

РасчетS*M*2^(e-127)например: представление с одинарной точностью -5
1 10000001 01000000000000000000000Его знаковый бит S равен 1, что указывает на отрицательное число -1.
Бит экспоненты E: 10000001, e = 129
Дополнительные биты мантиссы: если биты экспоненты не все равны 0, это 1
Мантисса М:1+2^-2;(-2, вторая цифра справа налево от мантиссы)
результат:-1 * ( 1+2^-2) * 2^( 129 - 127) = -5

2. Рабочий механизм JVM

2.1 Процесс запуска JVM

JVM启动流程

2.2 Базовая структура JVM

JVM基本结构

Область методов физически существует в куче и находится в постоянном создании кучи, но логически область методов и куча независимы. область методаmethod areaЭто просто концепция, определенная в спецификации JVM, которая используется для хранения данных, таких как информация о классе, постоянный пул, статические переменные и код, скомпилированный JIT.Где его разместить, разные реализации могут быть размещены в разных местах. Постоянная генерация - это концепция, уникальная для виртуальной машины Hotspot. Это реализация области методов. Ни одна другая JVM не имеет этого.

После определенной версии java 8 и java 7 perm gen был удален и заменен на metaspace.

Разница в том, что perm gen содержит метаданные класса, статические переменные класса и интернированную строку.
Метапространство содержит только метаданные класса, а статические переменные класса и интернированные строки перемещаются в кучу java (поэтому куча java должна быть больше).

JVM в основном управляет двумя типами памяти: кучей и некучей. Проще говоря, куча — это память, доступная для Java-кода, которая зарезервирована для разработчиков; не-куча зарезервирована для JVM для собственного использования. Таким образом, область методов, память, необходимая для внутренней обработки или оптимизации JVM (например, кеш скомпилированного кода JIT), структура каждого класса (например, пул констант времени выполнения, данные полей и методов) и код для методов и конструкторов — все это находится вне кучи в Память.

2.2.1 Регистр ПК

  1. Каждый поток имеет регистр ПК
  2. Создано во время создания потока
  3. указать на следующую инструкцию
  4. При выполнении собственного метода значение ПК не определено?

2.2.2 Область метода

  1. Сохранить загруженную информацию о классе: поля, информацию о методе, байт-код метода
  2. Обычно ассоциируется с постоянной территорией (пермь)

2.2.3 Куча Java

  1. Объекты хранятся в куче
  2. Все потоки совместно используют кучу Java
  3. рабочее пространство ГХ
    GC工作空间

2.2.4 Стек Java

  1. приватный поток

  2. Стек состоит из серии фреймов (поэтому его также называют стеком фреймов).

  3. Фрейм содержит таблицу локальных переменных каждого метода, стек операндов, указатель константного пула, программный счетчик.

  4. Каждый вызов метода создает фрейм и помещает его в стек.

  5. В фрейме есть таблица локальных переменных

    static方法

    no static方法

  6. стек операндов
    В Java нет регистров, для передачи всех параметров используется стек операндов.

    操作数栈

    Выделение места в стеке

  7. Небольшие объекты (десятки байт) могут быть размещены непосредственно в стеке без экранирования.

  8. Непосредственно размещенный в стеке, он может быть автоматически переработан, что снижает нагрузку на сборщик мусора.

  9. Большие или экранированные объекты не могут быть размещены в стеке
    Экранированный объект: на объект в стеке ссылается внешний объект, и его область действия находится вне текущего стека методов.

public class AppMain {
	//运行时, jvm 把appmain的信息都放入方法区  
	public static void main(String[] args) {
		//main 方法本身放入方法区。  
		Sample test1 = new Sample( " 测试1 " );
		//test1是引用,所以放到栈区里, Sample是自定义对象应该放到堆里面 
		Sample test2 = new Sample( " 测试2 " );  
		test1.printName(); 
		test2.printName(); 
	}
}

public class Sample {
	//运行时, jvm 把appmain的信息都放入方法区  
	private name;
	//new Sample实例后, name 引用放入栈区里, name 对象放入堆里  
	public Sample(String name) { 
		this .name = name; 
	} 
	//print方法本身放入 方法区里
	public void printName()  { 
		System.out.println(name);
	}
}

堆栈方法区交互

3. Модель памяти

Каждый поток имеет отдельную рабочую память и основную память Рабочая память хранит значения и копии переменных в основной памяти

内存模型
内存模型
Для обычных переменных значение, обновленное в одном потоке, не может быть немедленно отражено в других переменных. Если вам нужна немедленная видимость в других потоках, вам нужно использовать ключевое слово volatile

3.1 Особенности модели памяти

  • Видимость: Когда поток изменяет переменную, другие потоки могут немедленно узнать об этом.
  • Способы гарантировать видимость:
  1. volatile
  2. синхронизированный (перед разблокировкой записать значение переменной обратно в основную память)
  3. final (после завершения инициализации становится видимым для других потоков)
  • упорядоченность Внутри этого потока операции упорядочены Наблюдается вне потока, операция выходит из строя (переупорядочение инструкций или отложенная синхронизация между основной памятью и памятью потока)
  • перестановка инструкций Для повышения эффективности работы программы отрегулируйте порядок выполнения инструкций Инструкции, расположенные рядом с записью, нельзя переупорядочить: запись после чтения, чтение после записи, запись после записи. Компилятор не учитывает семантику между потоками
  • Переупорядочивание инструкций - разрушает порядок между потоками
class OrderExample {
    int a = 0;
    boolean flag = false;
    public void writer() {
        a = 1;                   
        flag = true;           
    }
    public void reader() {
        if (flag) {                
            int i =  a +1;      
            ……
        }
    }
}

Поток A выполняется первымwriter()метод Затем выполняется поток потока B.reader()метод Поток B находится вint i=a+1данеуверенныйВы можете видеть, что a было присвоено значение 1

线程重排
У писателя порядок двух предложений может быть не в порядке

  • Изменение порядка инструкций — способ обеспечить порядок Добавьте синхронизированное ключевое слово synchronized к методу
  • Основные принципы изменения порядка инструкций
    1. Принцип порядка программ: гарантированная семантическая последовательность в потоке
    2. Правило volatile: запись volatile переменной происходит раньше, чем чтение
    3. Правила блокировки: разблокировка должна произойти до последующей блокировки
    4. Транзитивность: А предшествует В, В предшествует С, тогда А должно предшествовать С
    5. Метод запуска потока предшествует каждому из его действий и/или методов.
    6. Все операции потока предшествуют завершению потокаThread.join(), наконец закончилось
    7. прерывание потокаinterrupt()Перед кодом прерванного потока прерывание немедленно останавливается
    8. Выполнение конструктора объекта завершается доfinalize()метод

3.2 Конфигурация общих параметров JVM

  • Параметры трассировки тракта
    -XX:+TraceClassLoading: контролировать загрузку класса
    -XX:+PrintClassHistogram: после нажатия Ctrl+Break распечатать информацию о классе
  • параметры выделения кучи
    XX:+HeapDumpOnOutOfMemoryError: Экспортировать кучу в файл, когда OOM
    -XX:OnOutOfMemoryError: В OOM выполнить скрипт
    Официально рекомендуется, чтобы на новое поколение приходилось 3/8 кучи
    Выжившее поколение составляет 1/10 молодого поколения.
  • параметры выделения стека
    Xss
    • Обычно всего несколько сотен тысяч
    • Определяет глубину вызовов функций
    • Каждый поток имеет собственное пространство стека
    • Локальные переменные и параметры размещаются в стеке

4. Алгоритмы и типы GC

4.1 Алгоритм сборки мусора

  1. Подсчет ссылок: не используется в java
  2. Метка Очистить: Старость
  3. Сжатие тегов: старое поколение
  4. Алгоритм репликации: молодое поколение
  5. Поколенческое мышление
    • Классифицировать по жизненному циклу объектов, короткоживущие объекты классифицируются как объекты нового поколения, долгоживущие объекты классифицируются как объекты старого поколения.
    • В соответствии с особенностями разных поколений выберите подходящий алгоритм сбора
      • Выживает небольшое количество объектов, подходящих для алгоритмов репликации
      • Выживает большое количество объектов, подходящих для очистки или сжатия меток.

4.2 Доступность

  1. доступный
  • Этот объект доступен из корневого узла
  • Корень: (относится к стеку области метода)
    • Объекты, на которые есть ссылки в стеке
    • Объекты, на которые ссылаются статические члены или константы в области методов (глобальные объекты)
    • Ссылочные объекты в стеке методов JNI
  1. воскрешаемый
  • После того, как все ссылки будут освобождены, он может быть восстановлен, т.е. недоступен.
  • Но объект может быть воскрешён в finalize()
  1. неприкасаемый
    • Может войти в неприкасаемое состояние после finalize()
    • Неприкасаемые объекты не могут быть воскрешены
    • может быть переработан
public class CanReliveObj {
    public static CanReliveObj obj;
    public static void main(String[] args) throws InterruptedException{
        obj=new CanReliveObj();
    	obj=null;   //可复活
    	System.gc();
    	Thread.sleep(1000);
    	if(obj==null){
    	   System.out.println("obj 是 null");
    	}else{
    	   System.out.println("obj 可用");
    	}
    	System.out.println("第二次gc");
    	obj=null;    //不可复活
    	System.gc();
    	Thread.sleep(1000);
    	if(obj==null){
    		System.out.println("obj 是 null");
    	}else{
    		System.out.println("obj 可用");
    	}
    }
    @Override
    //重写析构方法
    protected void finalize() throws Throwable {
        super.finalize();
        System.out.println("CanReliveObj finalize called");
        obj=this;
    }
    @Override
    public String toString(){
        return "I am CanReliveObj";
    }
}
  1. Избегайте использования методов finalize
  2. Объект можно вызвать только один раз, неосторожное обращение может привести к ошибкам
  3. Низкий приоритет, неопределенный, когда вызывать, неопределенный, когда происходит GC
  4. Вместо этого вы можете использовать try-catch-finally

Для объектов, которые не могут быть найдены с помощью анализа достижимости, сборщик мусора не обязательно восстанавливает объект. Чтобы полностью переработать объект, он должен пройти как минимум два процесса маркировки.
Первая отметка: Для объекта без других ссылок отфильтровать, нужно ли выполнять метод finalize() на объекте, если нет, то это означает, что он может быть переработан напрямую. (Основа проверки: был ли метод finalize() переопределен или выполнен, потому что метод finalize может быть выполнен только один раз).
Вторая отметка: если необходимо выполнить отфильтрованный оценочный бит, он будет помещен в очередь FQueue, и для выполнения операции освобождения будет автоматически создан поток завершения с низким приоритетом. Если на объект ссылаются другие объекты до того, как он будет выпущен, объект будет удален из очереди FQueue.

4.3 Stop-The-World

Феномен глобальной паузы в Java
Глобальная пауза, весь Java-код останавливается, нативный код может выполняться, но не может взаимодействовать с JVM
В основном из-за GC, это также может быть поток дампа, проверка взаимоблокировки, дамп кучи

4.4 Последовательный коллектор

  1. самый старый и самый стабильный
  2. эффективный
  3. Может вызывать длительные паузы
  4. Подходит для небольших приложений с небольшим объемом данных и без требований к времени отклика.
  5. -XX:+UseSerialGC
    • Новое поколение и старое поколение используют серийную переработку
    • Алгоритм репликации нового поколения
    • Маркер старости — компрессия
      串行搜集器

4.5 Параллельные коллекторы

Он подходит для высоких требований к пропускной способности, нескольких процессоров,Нет требований к времени отклика приложениясредних и крупных приложений.
Примеры: фоновая обработка, научные вычисления.吞吐量:=运行用户代码时间/(运行用户代码时间+GC时间)

  • Параллельно Параллельно:попеременноВозможность делать разные вещи, программа пользователя не может быть приостановлена, не обязательно параллельна, но может выполняться поочередно
  • Параллельно Параллельно:в то же времяВозможность делать разные вещи, потоки сборки мусора работают параллельно, но приложение ждет паузы

4.5.1 ParNew

  • -XX:+UseParNewGC
    • Кайнозойский параллелизм
    • старый сериал
  • Параллельная версия серийного коллекционера молодого поколения
  • алгоритм репликации
  • Многопоточность, требуется многоядерная поддержка
  • -XX:ParallelGCThreadsОграничьте количество потоков
    ParNew
0.834: [GC 0.834: [ParNew: 13184K->1600K(14784K), 0.0092203 secs] 13184K->1921K(63936K), 0.0093401 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]

4.5.2 Параллельный коллектор (настраиваемый, гибкий)

  1. Похоже на: ParNew
  2. Алгоритм репликации нового поколения
  3. сжатие тегов старого поколения
  4. Больше внимания к пропускной способности
  5. -XX:+UseAdaptiveSizePolicyСтратегия адаптивного регулирования — важное отличие Parallel от ParNew
  6. -XX:+UseParallelGC
    • В новом поколении используется коллектор Parallel + серийный номер старого поколения.
  7. -XX:+UseParallelOldGC
    • В новом поколении используется коллектор Parallel + в старом поколении используется параллельный

Старость бывает разной

1.500: [Full GC [PSYounhttps://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2017/12/3/1601bd5a57d6924fen~tplv-t2oaga2asx-image.image: 2682K->0K(19136K)] [ParOldGen: 28035K->30437K(43712K)] 30717K->30437K(62848K) [PSPermGen: 10943K->10928K(32768K)], 0.2902791 secs] [Times: user=1.44 sys=0.03, real=0.30 secs]
  1. специальные параметры
    • -XX:MaxGCPauseMills
      • Максимальное время паузы, в миллисекундах
      • GC делает все возможное, чтобы время сбора не превышало установленное значение
    • -XX:GCTimeRatio
      • Диапазон значений 0-100
      • Время сбора мусора в процентах от общего времени
      • Значение по умолчанию — 99, то есть максимально допустимый 1% времени для выполнения GC.
    • Эти два параметра противоречивы. Поскольку время паузы и пропускная способность не могут быть настроены одновременно

4.6 Параллельный сборщик CMS

применять кВысокие требования к времени отклика, средние и крупные приложения с несколькими процессорами и высокими требованиями к времени отклика приложений.
Примеры: веб-сервер/сервер приложений, телекоммуникационная биржа, интегрированная среда разработки.

  • характеристика
    Concurrent Mark Sweep параллелизмmark-sweep (выполняется с пользовательским потоком)
    отметка-ясноАлгоритм (не сжатие разметки)
    Параллельные этапы снижают пропускную способность (?)
    Только для коллекционеров старого поколения (новое поколение использует ParNew/или серийный номер)
    -XX:+UseConcMarkSweepGC

  • рабочий процесс

    1. начальная отметка
      Объект, с которым корень может быть напрямую связан
      высокоскоростной
      Эксклюзивный процессор, глобальная пауза
    2. Маркировка параллелизма (с пользовательскими потоками)
      Основной процесс маркировки, маркировка всех объектов
    3. перемаркировать
      знак повторной коррекции
      Эксклюзивный процессор, глобальная пауза
    4. Параллельная очистка (с пользовательскими потоками)
      Основываясь на отмеченном результате, очистите объект напрямую
      CMS并发收集器
  • превосходно:
    Минимизируйте простои, насколько это возможно, не требуется никаких глобальных простоев во время одновременной маркировки.

  • ниже:

  1. Влияет на общую пропускную способность и производительность системы
    • Например, при выполнении пользовательского потока половина ЦП используется для GC, а производительность системы находится в стадии GC, а скорость отклика снижается вдвое.
  2. Неполная очистка
    • На этапе очистки пользовательский поток все еще работает, и будет сгенерирован новый мусор, который не может быть очищен, поскольку он выполняется вместе с пользовательским потоком, и его нельзя очистить, когда пространство почти заполнено.
    • -XX:CMSInitiatingOccupancyFractionУстановите порог для срабатывания GC
    • Если зарезервированного места в памяти, к сожалению, недостаточно, это приведет к сбою параллельного режима.В это время следует использовать последовательный коллектор в качестве резервной копии.Из-за нехватки места время паузы обычно больше.
  • Проблемы фрагментации
    CMS использует алгоритм маркировки-развертки.После очистки эффективные адреса объектов кучи памяти являются прерывистыми, и есть фрагменты памяти.Поэтому можно установить сжатие памяти для сортировки фрагментов памяти. То есть CMS используется на старости лет из соображений производительности.отметка-ясноалгоритм, но все еще может быть настроен на использование алгоритма сжатия тегов
  1. -XX:+ UseCMSCompactAtFullCollectionПосле полного GC выполните чистовую обработку.
    • Процесс отделки является эксклюзивным и вызывает более длительные паузы
  2. -XX:+CMSFullGCsBeforeCompaction
    • После настройки для выполнения нескольких полных сборок мусора выполните дефрагментацию.
  3. -XX:ParallelCMSThreads
    • Установите количество потоков CMS, как правило, количество ядер процессора, определение по умолчанию (количество процессоров + 3)/4, что составляет не менее 25%

4.7 Расположение параметров GC

4.7.1 Распределение памяти

имя параметра имея в виду Примечание
-Xms начальный размер кучи Когда свободная память кучи по умолчанию меньше 40%, JVM будет увеличивать кучу до максимального предела -Xmx.
-Xmx максимальный размер кучи По умолчанию (параметр MaxHeapFreeRatio можно настроить), когда свободная память кучи больше 70%, JVM уменьшит кучу до минимального предела -Xms
-Xmn размер молодого поколения eden+ 2 Survivor Space, после увеличения молодого поколения размер старого поколения уменьшится.Sun официально рекомендует конфигурацию 3/8 от всей кучи.
-XX:PermSize Установите начальное значение постоянной генерации (perm gen) Постоянная генерация является реализацией области метода
-XX:MaxPermSize Установить максимальное постоянное поколение
-Xss размер стека на поток После JDK5.0 размер стека каждого потока составляет 1 М. Чем больше стек, тем меньше потоков и больше глубина стека.
-XX:NewRatio Отношение молодого поколения (включая Эдем и два региона Выживших) к старому поколению (исключая постоянное поколение) -XX:NewRatio=4 указывает, что соотношение между молодым поколением и старым поколением составляет 1:4, а на молодое поколение приходится 1/5 всего стека.При установке Xms=Xmx и Xmn этот параметр не необходимо установить.
-XX:SurvivorRatio Отношение размера области Эдема к площади Выжившего Если установлено значение 8, соотношение двух областей Выживших к одной области Эдема составляет 2:8, а на одну область Выживших приходится 1/10 всего молодого поколения.
-XX:MaxTenuringThreshold Максимальный возраст мусора Этот параметр действителен только во время последовательного ГХ.
-XX:PretenureSizeThreshold Объекты по размеру выделяются непосредственно в старом поколении Единица байта Новое поколение недопустимо при использовании Parallel Scavenge GC.Другая ситуация, которая напрямую выделяется в старом поколении, — это большой объект массива, и в массиве нет объекта внешней ссылки.

4.7.2 Параметры параллельного коллектора

имя параметра имея в виду Примечание
-XX:+UseParallelGC В новом поколении используется коллектор Parallel + серийный номер старого поколения.
-XX:+UseParNewGC Использование параллельных коллекторов в Young Generation
-XX:ParallelGCThreads Количество потоков для параллельных коллекторов Это значение лучше всего настроить равным количеству процессоров. Также применимо к CMS.
-XX:+UseParallelOldGC В новом поколении используется коллектор Parallel + в старом поколении используется параллельный
-XX:MaxGCPauseMillis Максимальное время для каждой сборки мусора молодого поколения (максимальное время паузы) Если это время не может быть соблюдено, JVM автоматически скорректирует размер молодого поколения, чтобы оно соответствовало этому значению.
-XX:+UseAdaptiveSizePolicy Автоматически выбирать размер области молодого поколения и соответствующий коэффициент площади выживших. После установки этого параметра параллельный сборщик автоматически выберет размер области молодого поколения и соответствующий коэффициент площади оставшихся в живых для достижения минимального соответствующего времени или частоты сбора, указанных целевой системой.

4.7.3 Параметры параллелизма CMS

имя параметра имея в виду Примечание
-XX:+UseConcMarkSweepGC Использовать коллекцию памяти CMS В новом поколении используется параллельный коллектор ParNew, а в старом поколении — CMS+последовательный коллектор.
-XX:CMSFullGCsBeforeCompaction Сколько раз выполнять сжатие памяти Поскольку параллельный сборщик не сжимает и не организует пространство памяти, он будет генерировать «фрагментацию» после запуска в течение определенного периода времени, что снижает эффективность работы.Это значение устанавливает, сколько раз пространство памяти сжимается и организуется после запуска GC .
-XX+UseCMSCompactAtFullCollection Сжатие старого поколения при FULL GC CMS не перемещает память, поэтому она очень подвержена фрагментации, что приводит к нехватке памяти, поэтому в это время будет включено сжатие памяти. Хорошей практикой является увеличение этого параметра. Может повлиять на производительность, но может устранить фрагментацию
-XX:CMSInitiatingPermOccupancyFraction Когда постоянная занятость площади достигнет этого процента, начните переработку CMS.

4.7.4 Вспомогательная информация

имя параметра имея в виду
-XX:+PrintGC
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-XX:+PrintGCApplicationStoppedTime Вывести время, когда программа была приостановлена ​​во время сборки мусора.Можно смешать с приведенным выше.
-XX:+PrintHeapAtGC Печать подробной информации о стеке до и после GC
--Xloggc:filename Записывать соответствующую информацию журнала в файл для анализа
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath
-XX:+PrintCommandLineFlags Распечатайте имена и значения подробных параметров XX, которые были установлены

4.8 Сводка по настройке

проект приоритет времени отклика приоритет пропускной способности
молодое поколение -Xmn как можно больше, пока не приблизится к минимальному пределу времени отклика системы -XX:MaxGCPauseMillis, уменьшить GC молодого поколения, уменьшить доступ к объектам старого поколения -Xmn как можно больше
Сборщик мусора молодого поколения параллельный коллектор Параллельный коллектор
старое поколение Если параметр кучи мал, это может привести к фрагментации памяти, высокой частоте сбора данных и приостановке работы приложения, а также к использованию традиционного метода пометки и очистки; если куча большая, сбор данных займет много времени.
Обратитесь к времени и времени сбора мусора молодого и старого поколения. -XX:NewRatio Старое поколение установлено меньшим, так что большинство краткосрочных объектов можно максимально переработать, уменьшив среднесрочные объекты, а старое поколение может хранить долгосрочные уцелевшие объекты.
Сборщик мусора старого поколения Старое поколение использует параллельные сборщики Поскольку время отклика не требуется, сборка мусора может выполняться параллельно или последовательно.

Типичная конфигурация

  • Параллельный коллектор с первой пропускной способностью
    Параллельный коллектор в основном предназначен для достижения определенной пропускной способности и подходит для науки и техники, фоновой обработки и т. Д.
    Молодое поколение использует параллельный коллектор, а старому поколению он не нужен
    Молодое поколение использует параллельный сбор, в то время как старшее поколение по-прежнему использует последовательный сбор.
-Xmx3800m -Xms3800m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:ParallelGCThreads=20

параллелизм старого поколения

-Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:ParallelGCThreads=20 -XX:+UseParallelOldGC

Установите максимальное время для каждой сборки мусора молодого поколения. Если это время не может быть соблюдено, JVM автоматически отрегулирует размер молодого поколения, чтобы соответствовать этому значению.

-Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseParallelGC  -XX:MaxGCPauseMillis=100
  • Параллельный сборщик с приоритетом времени отклика
    Параллельный сборщик предназначен в основном для обеспечения времени отклика системы и сокращения времени паузы при сборке мусора. Подходит для серверов приложений, телекоммуникационных областей и т. д.
-Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:ParallelGCThreads=20 -XX:+UseConcMarkSweepGC -XX:+UseParNewGC

-XX:+UseConcMarkSweepGC: установить для старого поколения одновременный сбор-XX:+UseParNewGC: установить молодое поколение для параллельного сбора

-Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseConcMarkSweepGC -XX:CMSFullGCsBeforeCompaction=5
-XX:+UseCMSCompactAtFullCollection

-XX:CMSFullGCsBeforeCompaction: Поскольку параллельный сборщик не сжимает и не организует пространство памяти, он будет генерировать «фрагментацию» после работы в течение определенного периода времени, что снижает эффективность работы. Это значение определяет, сколько раз сжимать и упорядочивать пространство памяти после запуска GC.
-XX:+UseCMSCompactAtFullCollection: Включить сжатие для старого поколения. Может повлиять на производительность, но может устранить фрагментацию

4.9 Журнал GC

5.617: [GC 5.617: [ParNew: 43296K->7006K(47808K), 0.0136826 secs] 44992K->8702K(252608K), 0.0137904 secs] 
[Times: user=0.03 sys=0.00, real=0.02 secs]  

объяснять

5.617(时间戳): [GC(Young GC) 5.617(时间戳): 
[ParNew(使用ParNew作为年轻代的垃圾回收器): 43296K(年轻代垃圾回收前的大小)->7006K(年轻代垃圾回收以后的大小)(47808K)(年轻代的总大小), 0.0136826 secs(回收时间)]
44992K(堆区垃圾回收前的大小)->8702K(堆区垃圾回收后的大小)(252608K)(堆区总大小), 0.0137904 secs(回收时间)] 
[Times: user=0.03(Young GC用户耗时) sys=0.00(Young GC系统耗时), real=0.02 secs(Young GC实际耗时)]  
[GC [DefNew: 3468K->150K(9216K), 0.0028638 secs][Tenured:
  1562K->1712K(10240K), 0.0084220 secs] 3468K->1712K(19456K),
  [Perm : 377K->377K(12288K)],
  0.0113816 secs] [Times: user=0.02 sys=0.00, real=0.01 secs]

Стаж: постоянное поколение/старое поколение
Серийный коллектор:
DefNew: использовать-XX:+UseSerialGC(Новое поколение и старое поколение используют сборщик серийной коллекции).
Параллельный коллектор:
ParNew: использовать-XX:+UseParNewGC(Молодое поколение использует параллельный сборщик, а старшее поколение использует последовательный сборщик) или-XX:+UseConcMarkSweepGC(Молодое поколение использует параллельный сборщик, а старое поколение использует CMS).
PSYoungGen: да использовать-XX:+UseParallelOldGC(Новое поколение, старое поколение использует сборщик параллельных коллекций) или-XX:+UseParallelGC(Новое поколение использует сборщик параллельных коллекций, а старое поколение использует последовательный сборщик)
мусорная куча: да использовать-XX:+UseG1GC(коллектор G1)

4.10 Условия запуска GC

Условием срабатывания является то, что соответствующая область алгоритма GC заполнена, или предсказание почти заполнено (например, коэффициент использования области достигает определенного процента — для параллелизма/конкурентности или недостаточного продвижения)

4.10.1 Классификация ГХ

Для реализации HotSpot VM в ней всего две точные классификации GC:

  • Частичный GC: режим, который не собирает всю кучу GC
    Young GC: собирайте только GC молодого поколения
    Старый сборщик мусора: собирайте сборщик мусора только старого поколения. В этом режиме работает только параллельная коллекция CMS.
    Смешанный GC: собирает GC всего молодого поколения и части старого поколения. ТолькоG1есть этот режим
  • Full GC
    Собрать всю кучу, включая паттерны для всех частей юнг ген, олд ген, перм ген (если он есть). Коллекция собирается целиком, неважно, старую вы сначала или молодую собираете. Маркировка делается вместе как единое целое, а затем производится уплотнение сначала старым поколением, а затем молодым поколением.

4.10.2 Последовательный сборщик мусора HotSpot VM

Основной сборщик мусора обычно эквивалентен полному сборщику мусора, собирающему всю кучу сборщика мусора. Простейшая стратегия генерации GC, разработаннаяСерийный GC для HotSpot VMС точки зрения реализации условия срабатывания таковы:

  • молодой GC: срабатывает, когда область Эдема в молодом поколении заполнена. Обратите внимание, что некоторые выжившие объекты будут переведены в старое поколение в молодом GC, поэтому занятость старого поколения обычно увеличивается после молодого GC.
  • full GC
    1. При подготовке к запуску молодого GC, если статистика говорит, что средний размер продвижения предыдущего молодого GC больше, чем текущее оставшееся пространство старого поколения, молодой GC не будет запущен, а будет запущен полный GC (потому что GC виртуальной машины HotSpot находится в , в дополнение к параллельному сбору CMS, другие GC, которые могут собирать old gen, будут одновременно собирать всю кучу GC, включая молодое gen, поэтому нет необходимости запускать отдельный молодой ГК заранее);
    2. Если есть perm gen, когда perm gen выделяет место, но его недостаточно, также должен быть запущен полный GC;
    3. System.gc(), дамп кучи с сборщиком мусора, также по умолчанию запускает полный сборщик мусора.

4.10.3 Непараллельный сборщик мусора HotSpot VM (параллельный сборщик мусора)

Условия срабатывания немного сложнее, но общий принцип такой же, как у последовательного GC.
Исключение: Параллельная уборка (-XX:+UseParallelGCВ рамках нового поколения, использующего сборщик Parallel), по умолчанию выполняется молодой GC перед запуском полного GC, и приложение может немного работать между двумя GC, чтобы сократить время паузы полного GC (потому что молодой GC попытается очистить мертвые объекты молодого поколения и уменьшить нагрузку на полный GC). Параметр VM, управляющий этим поведением,-XX:+ScavengeBeforeFullGC

4.10.4 Параллельный сборщик мусора HotSpot VM

Условия срабатывания параллельного GC отличаются. Взяв в качестве примера CMS GC, в основном нужно регулярно проверять использование старого поколения.Когда использование превышает пороговое значение, будет запущен CMS GC, и будет выполняться параллельный сбор старого поколения.

-XX:CMSInitiatingOccupancyFraction=80 // old达到80%收集

Или во время процесса сборки мусора, поскольку зарезервированная память не может удовлетворить потребности программы, происходит сбой параллельного режима, и старый серийный номер временно используется для полной сборки мусора.

4.10.5 Коллекция HotSpot VM G1

Триггерное условие для начальной маркировки G1 GC:HeapКогда коэффициент использования превышает определенное значение, сбор основан на приоритете значения восстановления, а не в соответствии с областью «молодые старые».

G1 GC: Молодой GC + смешанный GC (новое поколение, плюс немного старого поколения) + Full GC для алгоритма G1 GC (справляться с алгоритмом G1 GC иногда не спеша, стоимость очень высока);

5. Загрузчик классов

5.1 Процесс проверки загрузки класса

5.1.1 Загрузка

Преобразование в структуру данных области метода Создайте соответствующий объект java.lang.Class в куче Java.

  • Загрузчик классовЗагрузчик классов
  • ClassLoader — абстрактный класс.
    • Экземпляр ClassLoader прочитает байт-код Java, чтобы загрузить класс в JVM.
    • ClassLoader можно настроить для соответствия различным методам получения потока байт-кода (например, по сети).

jdk默认类加载过程

tomcat и OSGi внесли изменения

пример: классы загружаются сверху вниз
Добавьте A.java в каталог проекта, чтобы автоматически скомпилировать и сгенерировать A.class.
Также укажите путь к корневому каталогу загрузки, -Xbootclasspath/a:path, и поместите новый A.class с тем же именем.
В это время файл класса в указанном корневом каталоге загрузки будет загружен.

Примечание. Выше приведен режим загрузки классов по умолчанию для jdk, но у tomcat и OSGi есть свои собственные методы загрузки.
Tomcat: Tomcat WebappClassLoader сначала загрузит свой собственный класс, а затем делегирует родителя, если он не может его найти.
OSGi ClassLoader формирует сетчатую структуру, свободно загружая классы по мере необходимости.

5.1.2 Ссылки

  • проверять Цель: убедиться в правильности формата потока Class.
    1. Проверка форматов файлов
      • Он начинается с 0xCAFEBABE?
      • Является ли номер версии разумным: какая версия jdk скомпилирована и сгенерирована файлом класса, и совместима ли она с jdk, который выполняет класс
    2. Проверка метаданных (проверка базовой информации)
      • Есть ли родительский класс: в классе указан родительский класс, проверьте, существует ли родительский класс
      • Унаследовал последний класс?
      • Неабстрактный класс реализует все абстрактные методы.
    3. Проверка байт-кода (сложная)
      • запустить проверку
      • Тип данных стека соответствует параметру данных кода операции.
      • Инструкции по прыжкам назначаются в разумных местах
    4. Проверка символьной ссылки
      • Существует ли описываемый класс в пуле констант: указанный класс должен существовать
      • Существует ли метод или поле, к которому осуществляется доступ, и имеют ли они достаточные разрешения: частный…
  • Подготовить
    1. Выделить памятьи установите начальное значение для класса (в области метода)
      • public static int v=1;
      • На этапе подготовки v будет установлено на 0
      • Он будет установлен в 1 во время инициализации
      • для статикиfinalтип, ему будет присвоено правильное значение на этапе подготовки - перед инициализацией
      • public static final int v=1;
  • Разобрать Символическая ссылка заменяется прямой ссылкой: то есть приложение имени класса напрямую заменяется указателем адреса памяти.

5.1.3 Инициализация

  • Выполнить конструктор класса
    • Оператор присваивания статической переменной: обратите внимание, что static final был назначен на этапе подготовки.
    • статический {} оператор
  • Убедитесь, что родительский класс вызывается до вызова подкласса
  • является потокобезопасным, т. е. однопоточным выполнением

6. Анализ производительности

6.1 Собственные инструменты анализа производительности Java

Введите команду непосредственно в консоли и используйте команду -help для определенных параметров.

6.1.1 jps

Обычно это первый шаг, который удобен для последующих вызовов других команд.
список java-процессов, аналогично команде ps
Параметр -q может указать, что jps выводит только идентификатор процесса и не выводит короткое имя класса.
Параметр -m может использоваться для вывода параметров, переданных процессу Java (основная функция).
Параметр -l может использоваться для вывода полного пути к основной функции.
Параметр -v может отображать параметры, переданные JVM.

jps命令

6.1.2 jinfo

Просмотр параметров процессаЕго можно использовать для просмотра расширенных параметров запущенного Java-приложения и даже для поддержки изменения некоторых параметров во время выполнения.
-flag идентификатор процесса: вывести значение параметра указанной JVM
-flag [+|-] идентификатор процесса: установить логическое значение указанного параметра JVM
-flag=идентификатор процесса: установить значение указанного параметра JVM

jinfo

6.1.3 jmap

Создание моментальных снимков кучи и статистики объектов для приложений Java.

jmap

num     #instances         #bytes  class name
----------------------------------------------
   1:        370469       32727816  [C
   2:        223476       26486384  <constMethodKlass>
   3:        260199       20815920  java.lang.reflect.Method
	…..
8067:             1              8  sun.reflect.GeneratedMethodAccessor35
Total       4431459      255496024

6.1.4 jstack

распечатать дамп потока
-lРаспечатать информацию о блокировке
-mРаспечатать информацию о кадре java и нативного
-FПринудительный дамп, используемый, когда jstack не отвечает
Версия Jdk1.6 имеет только параметр -l

jstack

6.1.5 JConsole

Графический инструмент мониторинга
Вы можете просмотреть текущий обзор приложений Java, отслеживать информацию о куче, постоянное использование области, загрузку классов и т. д.

JConsole

6.1.6 Visual VM

Visual VM — это мощный универсальный инструмент визуализации для устранения неполадок и мониторинга производительности.

Visual VM

6.1.7 MAT

MAT

6.2 Анализ кучи Java

  • Причины нехватки памяти OOM
    Диапазон памяти JVM: куча, постоянная область, стек потоков, прямая память
    堆+线程栈 +直接内存<= 操作系统可分配空间
  1. переполнение кучи
    Занимает много места в куче и напрямую переполняется
public static void main(String args[]){
    ArrayList<byte[]> list=new ArrayList<byte[]>();
    for(int i=0;i<1024;i++){
        list.add(new byte[1024*1024]);
    }
}
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at geym.jvm.ch8.oom.SimpleHeapOOM.main(SimpleHeapOOM.java:14)

Решение: увеличить место в куче, вовремя освободить память, пакетный процесс

  1. Постоянное переполнение области
//生成大量的类
public static void main(String[] args) {
    for(int i=0;i<100000;i++){
        CglibBean bean = new CglibBean("geym.jvm.ch3.perm.bean"+i,new HashMap());
    }
}
Caused by: java.lang.OutOfMemoryError: 【PermGen space】
[Full GC[Tenured: 2523K->2523K(10944K), 0.0125610 secs] 2523K->2523K(15936K), 
[Perm : 【4095K->4095K(4096K)】], 0.0125868 secs] [Times: user=0.02 sys=0.00, real=0.01 secs] 
Heap
 def new generation   total 4992K, used 89K [0x28280000, 0x287e0000, 0x2d7d0000)
  eden space 4480K,   2% used [0x28280000, 0x282966d0, 0x286e0000)
  from space 512K,   0% used [0x286e0000, 0x286e0000, 0x28760000)
  to   space 512K,   0% used [0x28760000, 0x28760000, 0x287e0000)
 tenured generation   total 10944K, used 2523K [0x2d7d0000, 0x2e280000, 0x38280000)
   the space 10944K,  23% used [0x2d7d0000, 0x2da46cf0, 0x2da46e00, 0x2e280000)
 compacting perm gen  total 4096K, used 4095K [0x38280000, 0x38680000, 0x38680000)
   the space 4096K,  【99%】 used [0x38280000, 0x3867fff0, 0x38680000, 0x38680000)
    ro space 10240K,  44% used [0x38680000, 0x38af73f0, 0x38af7400, 0x39080000)
    rw space 12288K,  52% used [0x39080000, 0x396cdd28, 0x396cde00, 0x39c80000)

Решение: избегайте динамического создания классов, увеличьте площадь Perm и разрешите повторное использование классов.

  1. Переполнение стека Java
    -Xmx1g -Xss1m
public static class SleepThread implements Runnable{
    public void run(){
        try {
            Thread.sleep(10000000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

public static void main(String args[]){
    for(int i=0;i<1000;i++){
        new Thread(new SleepThread(),"Thread"+i).start();
        System.out.println("Thread"+i+" created");
    }
}
Exception in thread "main" java.lang.OutOfMemoryError: 
unable to create new native thread

Переполнение стека здесь означает, что при создании потока необходимо выделить пространство стека для потока.Это пространство стека запрашивается у операционной системы.Если операционная система не может предоставить достаточно места, будет выброшен OOM
например: пространство кучи 1G, пространство стека каждого потока 1 м

Примечание: куча + стек потоков + прямая память

  1. прямое переполнение памяти
    ByteBuffer.allocateDirect(): применить для прямой памяти вне кучи
    Прямая память также может быть восстановлена ​​сборщиком мусора.
    -Xmx1g -XX:+PrintGCDetails
//会抛出oom,但堆内存空间充足
for(int i=0;i<1024;i++){
    ByteBuffer.allocateDirect(1024*1024);
    System.out.println(i);
      System.gc();
}

7. Блокировка

7.1 Безопасность потоков

public static List<Integer> numberList =new ArrayList<Integer>();
public static class AddToList implements Runnable{
	int startnum=0;
	public AddToList(int startnumber){
		startnum=startnumber;
	}
	@Override
	public void run() {
		int count=0;
		while(count<1000000){
			numberList.add(startnum);
			startnum+=2;
			count++;
		}
	}
}

public static void main(String[] args) throws InterruptedException {
	Thread t1=new Thread(new AddToList(0));
	Thread t2=new Thread(new AddToList(1));
	t1.start();
	t2.start();
	while(t1.isAlive() || t2.isAlive()){
		Thread.sleep(1);
	}
	System.out.println(numberList.size());
}
Exception in thread "Thread-0" java.lang.ArrayIndexOutOfBoundsException: 73
	at java.util.ArrayList.add(Unknown Source)
	at simpleTest.TestSome$AddToList.run(TestSome.java:27)
	at java.lang.Thread.run(Unknown Source)
1000005

ArrayList не является потокобезопасным объектом коллекции. В процессе добавления элементов двумя потоками, когда массив заполнен и расширяется автоматически, другой поток все еще добавляет элементы. Нижний слой ArrayList представляет собой неизменяемый массив. Следующая таблица исключение за пределами границ

7.2 Заголовок объекта Метка

В виртуальной машине HotSpot расположение объектов, хранящихся в памяти, можно разделить на три области: заголовок объекта (Header), данные экземпляра (Instance Data) и заполнение выравнивания (Padding).

对象内存结构
Mark Word 32bit

7.3 Блокировка смещения

По мере того, как замки конкурируют, замки могут быть модернизированы от замков со смещением до облегченных замков, а затем модернизированы до тяжелых замков (ноПовышение блокировки является односторонним, то есть его можно только прокачать с низкого на высокий, и деградации блокировки не будет)
В большинстве случаев блокировки не только не конкурируют с несколькими потоками, но и всегда запрашиваются одним и тем же потоком несколько раз.Смещенные блокировки вводятся для того, чтобы снизить стоимость получения блокировок потоком.Предвзятые блокировки работают только в одном потоке.
Смещенная блокировка имеет поле ThreadId в заголовке объекта блокировки.Если это поле пустое, при первом получении блокировки он запишет свой собственный ThreadId в поле ThreadId блокировки и проверит, смещение в заголовке блокировки является смещенным. Статус блокировки равен 1. Таким образом, когда блокировка будет получена в следующий раз, будет непосредственно проверено, согласуется ли ThreadId с собственным идентификатором потока. Если это то же самое, считается, что текущий поток захватил блокировку, поэтому нет необходимости получать блокировку снова, пропуская облегченную фазу блокировки для блокировок и тяжелых блокировок. Повышенная эффективность.

  1. В большинстве случаев конкуренция отсутствует, поэтому можно предвзято относиться к повышению производительности.
  2. Так называемое смещение — это эксцентриситет, то есть замок будет смещен в сторону резьбы, которая в данный момент владеет замком.
  3. Установите отметку заголовка объекта Mark на смещение и напишите ID потока в заголовок объекта Mark
  4. Пока нет конкуренции, поток, получивший смещенную блокировку, в будущем войдет в синхронизированный блок без синхронизации.
  5. Когда другие потоки запрашивают ту же блокировку, режим смещения завершается, блокировка смещения отменяется в глобальной безопасной точке (в которой байт-код не выполняется) и принимаются другие блокировки.
  6. -XX:+UseBiasedLocking
    • включено по умолчанию
    • В случае жесткой конкуренции предвзятая блокировка увеличит нагрузку на систему.
      Включите блокировку смещения-XX:+UseBiasedLocking -XX:BiasedLockingStartupDelay=0После запуска системы блокировка смещения не будет включена сразу, а будет с задержкой.Вы можете установить время задержки на 0
      偏向锁的获得和撤销流程

7.4 Легкие замки

Обычная производительность обработки блокировки не идеальна, облегченная блокировка — это быстрый метод блокировки.
Облегченные блокировки предназначены для повышения производительности, когда потоки попеременно выполняют синхронизированные блоки.

  • если объект не заблокирован
    Сохраните указатель Mark заголовка объекта на объект блокировки
    Установите заголовок объекта на указатель для блокировки (в пространстве стека потока)
    То есть и объекты, и блокировки содержат ссылки друг на друга.

    Легкий замковый замок
    Прежде чем поток выполнит синхронизированный блок, JVM сначала создаст пространство для хранения записи блокировки в кадре стека текущего потока и скопирует Mark Word в заголовке объекта в запись блокировки, которая официально называется Displaced Mark Word. .
    Затем поток пытается использовать CAS для замены Mark Word в заголовке объекта указателем на запись блокировки. Если это удается, текущий поток получает блокировку, если это не удается, это означает, что другие потоки конкурируют за блокировку, и текущий поток пытается использовать вращение, чтобы получить блокировку.

    Легкий замок для разблокировки
    При облегченной разблокировке используется атомарная операция CAS для замены Displaced Mark Word обратно на заголовок объекта, в случае успеха это означает, что конкуренции не происходит.
    Если это не удается, это означает, что текущая блокировка конкурирует, и блокировка расширится до тяжеловесной блокировки.

  • блокировка находится в стеке потоков
    Из вышеизложенного видно, что для определения того, удерживает ли поток облегченную блокировку, необходимо только определить, находится ли указатель заголовка объекта в пространстве стека потока.

  • характеристика

    • Если облегченная блокировка выходит из строя, это указывает на то, что существует конкуренция, и она обновляется до тяжелой блокировки (обычная блокировка, операционная система, уровень процесса).
    • В условиях отсутствия конкуренции блокировок уменьшите потери производительности, вызванные традиционными блокировками, с помощью мьютексов ОС.
    • В условиях жесткой конкуренции облегченные блокировки будут выполнять множество дополнительных операций, что приведет к снижению производительности.
      偏向锁升级进入轻量级锁
      Запись блокировки в слове метки указывает на запись блокировки ближайшего потока в стеке, который на самом деле является облегченной блокировкой в ​​соответствии с режимом «первым пришел — первым обслужен».
      轻量级锁及膨胀流程

7.5 блокировка спина блокировка спина

  • Сведение к минимуму зависаний потока на системном уровне
    • Когда существует конкуренция, если поток может быстро получить блокировку, тогда поток не может быть приостановлен на уровне ОС, и поток может выполнить несколько бездействий (прокрутки), чтобы дождаться получения блокировки.
    • В JDK1.6-XX:+UseSpinningвключи
    • В JDK1.7 удалите этот параметр и измените его на встроенную реализацию.
    • Если блок синхронизации очень длинный и спин не удается, то производительность системы будет снижаться - будет занята операция потока, и, наконец, она будет приостановлена ​​на уровне ОС, а спин-блокировка будет потреблять ресурсы.
    • Если блок синхронизации очень короткий, цикл завершится успешно, экономя время переключения приостановки потока и повышая производительность системы.

Когда возникает конфликт, если поток-владелец может снять блокировку за очень короткое время, эти конкурирующие потоки (не заблокированные) могут немного подождать (вращаться), после того как поток-владелец снимает блокировку, конкурирующий поток может немедленно получить блокировку, чтобы избежать блокировки потока

7.6 Смещенные блокировки, облегченные блокировки и спин-блокировки

  • Не метод оптимизации блокировки на уровне языка Java
  • Оптимизированный метод получения блокировок, встроенный в JVM, и шаги для получения блокировок.
    • Доступна блокировка смещения. Сначала попробуем блокировку смещения.
    • Доступны облегченные замки. Облегченные замки будут опробованы в первую очередь.
    • Все вышеперечисленное не работает, попробуйте спинлок
    • Если это снова не удается, попробуйте обычную блокировку (тяжеловесную блокировку), используйте мьютекс ОС для приостановки на уровне операционной системы.
Замок преимущество недостаток Применимая сцена
Блокировка смещения Блокировка и разблокировка не требуют дополнительного потребления, а разрыв составляет всего наносекунду по сравнению с выполнением асинхронных методов. Если между потоками существует конкуренция за блокировку, это приведет к дополнительному потреблению отзыва блокировки. Применяется только кОдинСценарий синхронизированного блока доступа к потоку.
Легкий замок Конкурирующие потоки не будут блокироваться, что повышает скорость отклика программы. Если поток, который никогда не получает блокировку, конкурирует, используйтевращениеБудет потреблять процессор. медленнее, чем тяжеловесные замки при соперничестве Соблюдайте время отклика. Синхронизированные блоки выполняются очень быстро.
тяжелый замок Конкуренция потоков не использует вращение и не потребляет ресурсы ЦП. Поток заблокирован, и время ответа медленное. Следите за пропускной способностью. Синхронизированные блоки выполняются дольше.

Разница между предвзятой блокировкой и концепцией облегченной блокировки:
Облегченные блокировки: используйте операции CAS для устранения синхронно используемых мьютексов без конкуренции.
Блокировка смещения: устраняет всю синхронизацию без конфликтов

Даже не выполняя операции CAS?

7.7 Оптимизация блокировок на уровне языка Java

7.7.1 Сокращение времени удержания блокировки

Уменьшение диапазона синхронизации

7.7.2 Уменьшение детализации блокировки

Разделите большие объекты на маленькие, чтобы увеличить параллелизм и уменьшить конкуренцию замков.
Повышается вероятность успеха предвзятых и облегченных замков — высокая степень детализации, жесткая конкуренция, предвзятые замки и высокая вероятность отказа облегченных замков.

  • ConcurrentHashMap
    Несколько сегментов:Segment<K,V>[] segments
    Техническое обслуживание в сегментеHashEntry<K,V>
    Во время операции наложения
    Сначала найдите сегмент, заблокируйте сегмент и выполните команду put
    После уменьшения детализации блокировки ConcurrentHashMap позволяет нескольким потокам входить одновременно.

7.7.3 Разделение замка

  • ReadWriteLock
тип замка блокировка чтения блокировка записи
блокировка чтения доступный не доступный
блокировка записи не доступный не доступный
  • LinkedBlockingQueue Замки можно снимать, пока операции не мешают друг другу
    队列

7.7.4 Огрубление блокировки

Если одна и та же блокировка постоянно запрашивается, синхронизируется и освобождается, она потребляет драгоценные ресурсы самой системы, что не способствует оптимизации производительности.

  • Example1:
public void demoMethod(){
	synchronized(lock){
		//do sth.
	}
	//做其他不需要的同步的工作,但能很快执行完毕
	synchronized(lock){
		//do sth.
	}
}

прямое расширение

public void demoMethod(){
	//整合成一次锁请求
	synchronized(lock){
		//do sth.
		//做其他不需要的同步的工作,但能很快执行完毕
	}
}
  • Example2
for(int i=0;i<CIRCLE;i++){
	synchronized(lock){
		
	}
}

//锁粗化
synchronized(lock){
for(int i=0;i<CIRCLE;i++){
		
	}
}

7.7.5 Устранение блокировки

В JIT-компиляторах, если вы обнаружите объекты, которые не могут быть разделены, вы можете исключить операцию блокировки для этих объектов.
Блокировки не вводятся программистами Некоторые библиотеки, поставляемые с JDK, могут иметь встроенные блокировки.
Объекты в стеке не будут доступны глобально, поэтому нет необходимости их блокировать.

  • Example
public static void main(String args[]) throws InterruptedException {
	long start = System.currentTimeMillis();
	for (int i = 0; i < CIRCLE; i++) {
		craeteStringBuffer("JVM", "Diagnosis");
	}
	long bufferCost = System.currentTimeMillis() - start;
	System.out.println("craeteStringBuffer: " + bufferCost + " ms");
}
public static String craeteStringBuffer(String s1, String s2) {
//StringBuffer线程安全对象,内置锁
StringBuffer sb = new StringBuffer(); 
	sb.append(s1);
	sb.append(s2);
	return sb.toString();
}

-server -XX:+DoEscapeAnalysis -XX:+EliminateLocks

  • Объекты в стеке (локальные переменные метода) не будут доступны глобально, поэтому нет необходимости их блокировать.

7.7.6 Без блокировки

Реализация без блокировкиCAS(Compare And Swap)
неблокирующая синхронизация

CAS(V,E,N):if V==E then V=N

Процесс алгоритма CAS: CAS(V,E,N). V представляет переменную для обновления, E представляет ожидаемое значение, а N представляет новое значение. Значение V будет установлено в N только тогда, когда значение V равно значению E. Если значения V и E отличаются, это означает, что другие потоки уже сделали обновления, а текущий поток ничего не делает.
Наконец, CAS возвращает текущее истинное значение V.
Операция CAS выполняется с оптимизмом, она всегда верит, что сможет успешно завершить операцию. Когда несколько потоков используют CAS для одновременного управления переменной, только один выиграет и успешно обновится, а остальные потерпят неудачу. Неудавшийся поток не приостанавливается, он просто уведомляется о сбое и может повторить попытку, и, конечно же, отказавший поток может прервать операцию. Основываясь на этом принципе, даже если в операции CAS нет блокировки, он также может обнаружить вмешательство других потоков в текущий поток и соответствующим образом обработать его.

Пакет java.util.concurrent.atomic использует реализацию без блокировки, и производительность выше, чем у обычной операции блокировки.

7.8 Состояния потока и переходы

Когда несколько потоков одновременно запрашивают монитор объекта, монитор объекта устанавливает несколько состояний, чтобы различать запрашивающий поток:

  • Список состязаний: все потоки, запрашивающие блокировки, будут сначала помещены в очередь состязания.
  • Список участников: те потоки в списке конфликтов, которые квалифицируются как кандидаты, перемещаются в список участников.
  • Набор ожидания: потоки, заблокированные вызовом метода ожидания, помещаются в набор ожидания.
  • OnDeck: в любой момент времени за блокировку конкурирует не более одного потока, который называется OnDeck.
  • Владелец: поток, который получил блокировку, называется владельцем.
  • !Владелец: поток, снявший блокировку
    线程状态转换
    Эти потоки в ContetionList, EntryList и WaitSet находятся в состоянии блокировки, и операция блокировки завершается операционной системой (через функцию pthread_mutex_lock в Linxu).
    После того, как поток заблокирован, он переходит в состояние планирования ядра (Linux), что заставит систему переключаться между пользовательским режимом и режимом ядра, серьезно влияя на производительность блокировки.
  • Синхронизированный замок
    Когда каждый поток готовится получить общий ресурс:
  1. Проверьте, есть ли у MarkWord собственный ThreadId, если да, то это означает, что текущий поток находится в «предвзятой блокировке».
  2. Если MarkWord не является собственным ThreadId, блокировка обновляется. В это время для переключения используется CAS. Новый поток информирует предыдущий поток о приостановке в соответствии с существующим ThreadId в MarkWord, а предыдущий поток устанавливает содержимое Маркворд должен быть пустым.
  3. Оба потока копируют HashCode объекта в свое вновь созданное пространство записей для хранения блокировок, а затем начинают конкурировать за MarkWord, изменяя содержимое MarKword общего объекта на адрес их вновь созданного пространства записей с помощью операции CAS.
  4. На третьем шаге CAS успешно выполняется для получения ресурсов, а если не получается, введитевращение
  5. В процессе вращения вращающийся поток успешно получает ресурс (то есть выполняется поток ранее полученного ресурса и освобождается общий ресурс), все состояние все еще находится в состоянии облегченной блокировки, если вращение завершается неудачно.
  6. Войдите в состояние тяжеловесной блокировки.В это время вращающийся поток заблокирован, ожидая завершения и пробуждения предыдущего потока.

Структура файла Eight.Class

U4: целое число без знака, 4 байта

Типы название количество Примечание
u4 magic 1 0xCAFEBABE: указывает тип файла класса Java.
u2 minor_version 1 Скомпилированная версия JDK
u2 major_version 1 Скомпилированная версия JDK
u2 constant_pool_count 1
cp_info constant_pool constant_pool_count - 1 Связанные ссылочные типы-примитивы — везде ссылки — минус 1
u2 access_flags 1 модификатор доступа и тип класса
u2 this_class 1 класс, указывающий на постоянный пул
u2 super_class 1 класс, указывающий на постоянный пул
u2 interfaces_count 1
u2 interfaces interfaces_count Каждый интерфейс указывает на константный пул индекса CONSTANT_Class.
u2 fields_count 1
field_info fields fields_count access_flags,name_index ,descriptor_index ,attributes_count,attribute_info attributes[attributes_count]
u2 methods_count 1
method_info methods methods_count
u2 attribute_count 1
attribute_info attributes attributes_count

Class文件结构

9. Выполнение байт-кода JVM

9.1 javap

Данные в стеке кадров потока:

  • Счетчик программ: по одному на каждый поток, используемый для указания местоположения инструкций, выполняемых текущим потоком.
  • таблица локальных переменных
  • стек операндов

9.2 JIT и связанные с ней параметры

  • JIT Just-In-Time
    Производительность выполнения байт-кода низкая, поэтому Hot Spot Code может быть скомпилирован в машинный код, а затем выполнен, скомпилирован во время выполнения.
    Когда виртуальная машина обнаруживает, что определенный метод или блок кода запускается очень часто, она идентифицирует эти коды как «горячий код» (горячий код). Код компилируется в машинный код, соответствующий собственной платформе)
  • Определение горячих кодов
    Счетчик вызовов метода: количество вызовов метода
    Счетчик заднего края: количество циклов в методе, которое может быть напрямую заменено машинным кодом в стеке.
  • Настройки сборки
    -XX:CompileThreshold=1000: Выполнять более тысячи раз — это горячий код
    -XX:+PrintCompilation: печатать код, скомпилированный в машинный код
    -Xint: интерпретировать выполнение
    -Xcomp: Все скомпилировано и выполнено
    -Xmixed: по умолчанию, смешанный