Эта статья в основном организована изалхимия в золотоУчебные материалы 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
2.2 Базовая структура 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 Регистр ПК
- Каждый поток имеет регистр ПК
- Создано во время создания потока
- указать на следующую инструкцию
- При выполнении собственного метода значение ПК не определено?
2.2.2 Область метода
- Сохранить загруженную информацию о классе: поля, информацию о методе, байт-код метода
- Обычно ассоциируется с постоянной территорией (пермь)
2.2.3 Куча Java
- Объекты хранятся в куче
- Все потоки совместно используют кучу Java
- рабочее пространство ГХ
2.2.4 Стек Java
-
приватный поток
-
Стек состоит из серии фреймов (поэтому его также называют стеком фреймов).
-
Фрейм содержит таблицу локальных переменных каждого метода, стек операндов, указатель константного пула, программный счетчик.
-
Каждый вызов метода создает фрейм и помещает его в стек.
-
В фрейме есть таблица локальных переменных
-
стек операндов
В Java нет регистров, для передачи всех параметров используется стек операндов.
Выделение места в стеке -
Небольшие объекты (десятки байт) могут быть размещены непосредственно в стеке без экранирования.
-
Непосредственно размещенный в стеке, он может быть автоматически переработан, что снижает нагрузку на сборщик мусора.
-
Большие или экранированные объекты не могут быть размещены в стеке
Экранированный объект: на объект в стеке ссылается внешний объект, и его область действия находится вне текущего стека методов.
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. Модель памяти
Каждый поток имеет отдельную рабочую память и основную память Рабочая память хранит значения и копии переменных в основной памяти
Для обычных переменных значение, обновленное в одном потоке, не может быть немедленно отражено в других переменных. Если вам нужна немедленная видимость в других потоках, вам нужно использовать ключевое слово volatile3.1 Особенности модели памяти
- Видимость: Когда поток изменяет переменную, другие потоки могут немедленно узнать об этом.
- Способы гарантировать видимость:
- volatile
- синхронизированный (перед разблокировкой записать значение переменной обратно в основную память)
- 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 к методу
- Основные принципы изменения порядка инструкций
- Принцип порядка программ: гарантированная семантическая последовательность в потоке
- Правило volatile: запись volatile переменной происходит раньше, чем чтение
- Правила блокировки: разблокировка должна произойти до последующей блокировки
- Транзитивность: А предшествует В, В предшествует С, тогда А должно предшествовать С
- Метод запуска потока предшествует каждому из его действий и/или методов.
- Все операции потока предшествуют завершению потока
Thread.join()
, наконец закончилось - прерывание потока
interrupt()
Перед кодом прерванного потока прерывание немедленно останавливается - Выполнение конструктора объекта завершается до
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 Алгоритм сборки мусора
- Подсчет ссылок: не используется в java
- Метка Очистить: Старость
- Сжатие тегов: старое поколение
- Алгоритм репликации: молодое поколение
- Поколенческое мышление
- Классифицировать по жизненному циклу объектов, короткоживущие объекты классифицируются как объекты нового поколения, долгоживущие объекты классифицируются как объекты старого поколения.
- В соответствии с особенностями разных поколений выберите подходящий алгоритм сбора
- Выживает небольшое количество объектов, подходящих для алгоритмов репликации
- Выживает большое количество объектов, подходящих для очистки или сжатия меток.
4.2 Доступность
- доступный
- Этот объект доступен из корневого узла
- Корень: (относится к стеку области метода)
- Объекты, на которые есть ссылки в стеке
- Объекты, на которые ссылаются статические члены или константы в области методов (глобальные объекты)
- Ссылочные объекты в стеке методов JNI
- воскрешаемый
- После того, как все ссылки будут освобождены, он может быть восстановлен, т.е. недоступен.
- Но объект может быть воскрешён в finalize()
- неприкасаемый
- Может войти в неприкасаемое состояние после 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";
}
}
- Избегайте использования методов finalize
- Объект можно вызвать только один раз, неосторожное обращение может привести к ошибкам
- Низкий приоритет, неопределенный, когда вызывать, неопределенный, когда происходит GC
- Вместо этого вы можете использовать try-catch-finally
Для объектов, которые не могут быть найдены с помощью анализа достижимости, сборщик мусора не обязательно восстанавливает объект. Чтобы полностью переработать объект, он должен пройти как минимум два процесса маркировки.
Первая отметка: Для объекта без других ссылок отфильтровать, нужно ли выполнять метод finalize() на объекте, если нет, то это означает, что он может быть переработан напрямую. (Основа проверки: был ли метод finalize() переопределен или выполнен, потому что метод finalize может быть выполнен только один раз).
Вторая отметка: если необходимо выполнить отфильтрованный оценочный бит, он будет помещен в очередь FQueue, и для выполнения операции освобождения будет автоматически создан поток завершения с низким приоритетом. Если на объект ссылаются другие объекты до того, как он будет выпущен, объект будет удален из очереди FQueue.
4.3 Stop-The-World
Феномен глобальной паузы в Java
Глобальная пауза, весь Java-код останавливается, нативный код может выполняться, но не может взаимодействовать с JVM
В основном из-за GC, это также может быть поток дампа, проверка взаимоблокировки, дамп кучи
4.4 Последовательный коллектор
- самый старый и самый стабильный
- эффективный
- Может вызывать длительные паузы
- Подходит для небольших приложений с небольшим объемом данных и без требований к времени отклика.
- -XX:+UseSerialGC
- Новое поколение и старое поколение используют серийную переработку
- Алгоритм репликации нового поколения
- Маркер старости — компрессия
4.5 Параллельные коллекторы
Он подходит для высоких требований к пропускной способности, нескольких процессоров,Нет требований к времени отклика приложениясредних и крупных приложений.
Примеры: фоновая обработка, научные вычисления.吞吐量:=运行用户代码时间/(运行用户代码时间+GC时间)
- Параллельно Параллельно:попеременноВозможность делать разные вещи, программа пользователя не может быть приостановлена, не обязательно параллельна, но может выполняться поочередно
- Параллельно Параллельно:в то же времяВозможность делать разные вещи, потоки сборки мусора работают параллельно, но приложение ждет паузы
4.5.1 ParNew
-
-XX:+UseParNewGC
- Кайнозойский параллелизм
- старый сериал
- Параллельная версия серийного коллекционера молодого поколения
- алгоритм репликации
- Многопоточность, требуется многоядерная поддержка
-
-XX:ParallelGCThreads
Ограничьте количество потоков
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 Параллельный коллектор (настраиваемый, гибкий)
- Похоже на: ParNew
- Алгоритм репликации нового поколения
- сжатие тегов старого поколения
- Больше внимания к пропускной способности
-
-XX:+UseAdaptiveSizePolicy
Стратегия адаптивного регулирования — важное отличие Parallel от ParNew -
-XX:+UseParallelGC
- В новом поколении используется коллектор Parallel + серийный номер старого поколения.
-
-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]
- специальные параметры
-
-XX:MaxGCPauseMills
- Максимальное время паузы, в миллисекундах
- GC делает все возможное, чтобы время сбора не превышало установленное значение
-
-XX:GCTimeRatio
- Диапазон значений 0-100
- Время сбора мусора в процентах от общего времени
- Значение по умолчанию — 99, то есть максимально допустимый 1% времени для выполнения GC.
- Эти два параметра противоречивы. Поскольку время паузы и пропускная способность не могут быть настроены одновременно
-
4.6 Параллельный сборщик CMS
применять кВысокие требования к времени отклика, средние и крупные приложения с несколькими процессорами и высокими требованиями к времени отклика приложений.
Примеры: веб-сервер/сервер приложений, телекоммуникационная биржа, интегрированная среда разработки.
-
характеристика
Concurrent Mark Sweep параллелизмmark-sweep (выполняется с пользовательским потоком)
отметка-ясноАлгоритм (не сжатие разметки)
Параллельные этапы снижают пропускную способность (?)
Только для коллекционеров старого поколения (новое поколение использует ParNew/или серийный номер)
-XX:+UseConcMarkSweepGC
-
рабочий процесс
- начальная отметка
Объект, с которым корень может быть напрямую связан
высокоскоростной
Эксклюзивный процессор, глобальная пауза - Маркировка параллелизма (с пользовательскими потоками)
Основной процесс маркировки, маркировка всех объектов - перемаркировать
знак повторной коррекции
Эксклюзивный процессор, глобальная пауза - Параллельная очистка (с пользовательскими потоками)
Основываясь на отмеченном результате, очистите объект напрямую
- начальная отметка
-
превосходно:
Минимизируйте простои, насколько это возможно, не требуется никаких глобальных простоев во время одновременной маркировки. -
ниже:
- Влияет на общую пропускную способность и производительность системы
- Например, при выполнении пользовательского потока половина ЦП используется для GC, а производительность системы находится в стадии GC, а скорость отклика снижается вдвое.
- Неполная очистка
- На этапе очистки пользовательский поток все еще работает, и будет сгенерирован новый мусор, который не может быть очищен, поскольку он выполняется вместе с пользовательским потоком, и его нельзя очистить, когда пространство почти заполнено.
-
-XX:CMSInitiatingOccupancyFraction
Установите порог для срабатывания GC - Если зарезервированного места в памяти, к сожалению, недостаточно, это приведет к сбою параллельного режима.В это время следует использовать последовательный коллектор в качестве резервной копии.Из-за нехватки места время паузы обычно больше.
- Проблемы фрагментации
CMS использует алгоритм маркировки-развертки.После очистки эффективные адреса объектов кучи памяти являются прерывистыми, и есть фрагменты памяти.Поэтому можно установить сжатие памяти для сортировки фрагментов памяти. То есть CMS используется на старости лет из соображений производительности.отметка-ясноалгоритм, но все еще может быть настроен на использование алгоритма сжатия тегов
-
-XX:+ UseCMSCompactAtFullCollection
После полного GC выполните чистовую обработку.- Процесс отделки является эксклюзивным и вызывает более длительные паузы
-
-XX:+CMSFullGCsBeforeCompaction
- После настройки для выполнения нескольких полных сборок мусора выполните дефрагментацию.
-
-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
- При подготовке к запуску молодого GC, если статистика говорит, что средний размер продвижения предыдущего молодого GC больше, чем текущее оставшееся пространство старого поколения, молодой GC не будет запущен, а будет запущен полный GC (потому что GC виртуальной машины HotSpot находится в , в дополнение к параллельному сбору CMS, другие GC, которые могут собирать old gen, будут одновременно собирать всю кучу GC, включая молодое gen, поэтому нет необходимости запускать отдельный молодой ГК заранее);
- Если есть perm gen, когда perm gen выделяет место, но его недостаточно, также должен быть запущен полный GC;
- 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 можно настроить для соответствия различным методам получения потока байт-кода (например, по сети).
tomcat и OSGi внесли изменения
пример: классы загружаются сверху вниз
Добавьте A.java в каталог проекта, чтобы автоматически скомпилировать и сгенерировать A.class.
Также укажите путь к корневому каталогу загрузки, -Xbootclasspath/a:path, и поместите новый A.class с тем же именем.
В это время файл класса в указанном корневом каталоге загрузки будет загружен.
Примечание. Выше приведен режим загрузки классов по умолчанию для jdk, но у tomcat и OSGi есть свои собственные методы загрузки.
Tomcat: Tomcat WebappClassLoader сначала загрузит свой собственный класс, а затем делегирует родителя, если он не может его найти.
OSGi ClassLoader формирует сетчатую структуру, свободно загружая классы по мере необходимости.
5.1.2 Ссылки
- проверять
Цель: убедиться в правильности формата потока Class.
- Проверка форматов файлов
- Он начинается с 0xCAFEBABE?
- Является ли номер версии разумным: какая версия jdk скомпилирована и сгенерирована файлом класса, и совместима ли она с jdk, который выполняет класс
- Проверка метаданных (проверка базовой информации)
- Есть ли родительский класс: в классе указан родительский класс, проверьте, существует ли родительский класс
- Унаследовал последний класс?
- Неабстрактный класс реализует все абстрактные методы.
- Проверка байт-кода (сложная)
- запустить проверку
- Тип данных стека соответствует параметру данных кода операции.
- Инструкции по прыжкам назначаются в разумных местах
- Проверка символьной ссылки
- Существует ли описываемый класс в пуле констант: указанный класс должен существовать
- Существует ли метод или поле, к которому осуществляется доступ, и имеют ли они достаточные разрешения: частный…
- Проверка форматов файлов
- Подготовить
-
Выделить памятьи установите начальное значение для класса (в области метода)
- 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.
6.1.2 jinfo
Просмотр параметров процессаЕго можно использовать для просмотра расширенных параметров запущенного Java-приложения и даже для поддержки изменения некоторых параметров во время выполнения.
-flag идентификатор процесса: вывести значение параметра указанной JVM
-flag [+|-] идентификатор процесса: установить логическое значение указанного параметра JVM
-flag=идентификатор процесса: установить значение указанного параметра JVM
6.1.3 jmap
Создание моментальных снимков кучи и статистики объектов для приложений Java.
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
6.1.5 JConsole
Графический инструмент мониторинга
Вы можете просмотреть текущий обзор приложений Java, отслеживать информацию о куче, постоянное использование области, загрузку классов и т. д.
6.1.6 Visual VM
Visual VM — это мощный универсальный инструмент визуализации для устранения неполадок и мониторинга производительности.
6.1.7 MAT
6.2 Анализ кучи Java
- Причины нехватки памяти OOM
Диапазон памяти JVM: куча, постоянная область, стек потоков, прямая память
堆+线程栈 +直接内存<= 操作系统可分配空间
- переполнение кучи
Занимает много места в куче и напрямую переполняется
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)
Решение: увеличить место в куче, вовремя освободить память, пакетный процесс
- Постоянное переполнение области
//生成大量的类
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 и разрешите повторное использование классов.
- Переполнение стека 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 м
Примечание: куча + стек потоков + прямая память
- прямое переполнение памяти
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).
7.3 Блокировка смещения
По мере того, как замки конкурируют, замки могут быть модернизированы от замков со смещением до облегченных замков, а затем модернизированы до тяжелых замков (ноПовышение блокировки является односторонним, то есть его можно только прокачать с низкого на высокий, и деградации блокировки не будет)
В большинстве случаев блокировки не только не конкурируют с несколькими потоками, но и всегда запрашиваются одним и тем же потоком несколько раз.Смещенные блокировки вводятся для того, чтобы снизить стоимость получения блокировок потоком.Предвзятые блокировки работают только в одном потоке.
Смещенная блокировка имеет поле ThreadId в заголовке объекта блокировки.Если это поле пустое, при первом получении блокировки он запишет свой собственный ThreadId в поле ThreadId блокировки и проверит, смещение в заголовке блокировки является смещенным. Статус блокировки равен 1. Таким образом, когда блокировка будет получена в следующий раз, будет непосредственно проверено, согласуется ли ThreadId с собственным идентификатором потока. Если это то же самое, считается, что текущий поток захватил блокировку, поэтому нет необходимости получать блокировку снова, пропуская облегченную фазу блокировки для блокировок и тяжелых блокировок. Повышенная эффективность.
- В большинстве случаев конкуренция отсутствует, поэтому можно предвзято относиться к повышению производительности.
- Так называемое смещение — это эксцентриситет, то есть замок будет смещен в сторону резьбы, которая в данный момент владеет замком.
- Установите отметку заголовка объекта Mark на смещение и напишите ID потока в заголовок объекта Mark
- Пока нет конкуренции, поток, получивший смещенную блокировку, в будущем войдет в синхронизированный блок без синхронизации.
- Когда другие потоки запрашивают ту же блокировку, режим смещения завершается, блокировка смещения отменяется в глобальной безопасной точке (в которой байт-код не выполняется) и принимаются другие блокировки.
-
-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), что заставит систему переключаться между пользовательским режимом и режимом ядра, серьезно влияя на производительность блокировки. - Синхронизированный замок
Когда каждый поток готовится получить общий ресурс:
- Проверьте, есть ли у MarkWord собственный ThreadId, если да, то это означает, что текущий поток находится в «предвзятой блокировке».
- Если MarkWord не является собственным ThreadId, блокировка обновляется. В это время для переключения используется CAS. Новый поток информирует предыдущий поток о приостановке в соответствии с существующим ThreadId в MarkWord, а предыдущий поток устанавливает содержимое Маркворд должен быть пустым.
- Оба потока копируют HashCode объекта в свое вновь созданное пространство записей для хранения блокировок, а затем начинают конкурировать за MarkWord, изменяя содержимое MarKword общего объекта на адрес их вновь созданного пространства записей с помощью операции CAS.
- На третьем шаге CAS успешно выполняется для получения ресурсов, а если не получается, введитевращение
- В процессе вращения вращающийся поток успешно получает ресурс (то есть выполняется поток ранее полученного ресурса и освобождается общий ресурс), все состояние все еще находится в состоянии облегченной блокировки, если вращение завершается неудачно.
- Войдите в состояние тяжеловесной блокировки.В это время вращающийся поток заблокирован, ожидая завершения и пробуждения предыдущего потока.
Структура файла 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 |
9. Выполнение байт-кода JVM
9.1 javap
Данные в стеке кадров потока:
- Счетчик программ: по одному на каждый поток, используемый для указания местоположения инструкций, выполняемых текущим потоком.
- таблица локальных переменных
- стек операндов
9.2 JIT и связанные с ней параметры
- JIT Just-In-Time
Производительность выполнения байт-кода низкая, поэтому Hot Spot Code может быть скомпилирован в машинный код, а затем выполнен, скомпилирован во время выполнения.
Когда виртуальная машина обнаруживает, что определенный метод или блок кода запускается очень часто, она идентифицирует эти коды как «горячий код» (горячий код). Код компилируется в машинный код, соответствующий собственной платформе) - Определение горячих кодов
Счетчик вызовов метода: количество вызовов метода
Счетчик заднего края: количество циклов в методе, которое может быть напрямую заменено машинным кодом в стеке. - Настройки сборки
-XX:CompileThreshold=1000
: Выполнять более тысячи раз — это горячий код
-XX:+PrintCompilation
: печатать код, скомпилированный в машинный код
-Xint
: интерпретировать выполнение
-Xcomp
: Все скомпилировано и выполнено
-Xmixed
: по умолчанию, смешанный