Сжатый код JVM

JVM

Для 32-разрядных машин максимальная память, которую может использовать процесс, составляет 4G. Если процессу требуется больше памяти, требуется 64-разрядная машина.

Для процесса Java, когда oop имеет только 32 бита, он может ссылаться только на память 4G. Поэтому, если вам нужно использовать большую память кучи, вам нужно развернуть 64-битную JVM. Таким образом, oop составляет 64 бита, а куча памяти, на которую можно ссылаться, больше.

Примечание: oop (обычный указатель объекта), обычный указатель объекта — это дескриптор, используемый для представления ссылочного объекта в JVM.

В куче ссылки на 32-битные объекты занимают 4 байта, а ссылки на 64-битные объекты занимают 8 байтов. То есть ссылка на 64-битный объект в два раза больше, чем 32-битный объект.

Хотя 64-разрядная JVM поддерживает большие кучи, она создает проблемы с производительностью из-за больших ссылок на объекты:

  1. Увеличенные накладные расходы GC

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

  1. Уменьшить частоту попаданий в кэш процессора

Ссылка на 64-битный объект увеличивается, и ЦП может кэшировать меньше операций, тем самым снижая эффективность кэш-памяти ЦП.

Чтобы иметь возможность поддерживать 32-битную производительность, oop должен оставаться 32-битным. Итак, как использовать 32-битную oop для ссылки на большую память кучи?

Ответ — CompressedOops.

Способ реализации JVM заключается в том, что вместо сохранения всех ссылок она сохраняет ссылку каждые 8 ​​байтов. Например, вместо сохранения каждой ссылки 0, 1, 2... теперь только 0, 8, 16.... Таким образом, после сжатия указателя не все ссылки сохраняются в куче, а сохраняются с интервалом в 8 байт.

С точки зрения реализации ссылки в куче фактически хранятся в соответствии с 0x0, 0x1, 0x2... . Просто когда ссылка хранится в 64-битном регистре, JVM сдвигает ее влево на 3 бита (эквивалентно добавлению 3 нулей в конце), например, 0x0, 0x1, 0x2... преобразуются в 0x0, 0x8, 0x10 соответственно. А при чтении из регистра JVM может сдвигаться вправо на 3 бита, отбрасывая конечные нули. (oop — это 32 бита в куче, 35 битов в регистре, 2 в 35-й степени = 32G. Другими словами, используйте 32 бита для получения пространства кучи памяти, на которое может ссылаться 35-битный oop)

В JVM (будь то 32-битная или 64-битная) объекты уже выровнены по 8-байтовым границам. Для большинства процессоров такое выравнивание является оптимальным. Поэтому использование сжатого oop не приносит потерь, а повышает производительность.

Oracle JDK будет включать сжатые указатели по умолчанию в 64-битных системах, начиная с 6 обновления 23.

32-разрядная виртуальная машина HotSpot не поддерживает параметр UseCompressedOops, его поддерживает только 64-разрядная виртуальная машина HotSpot.

Для размеров кучи от 4G до 32G следует использовать сжатый oop.

Посмотреть рабочий режим сжатого указателя

Когда виртуальная машина запускается, вы можете установить параметры -XX:+UnlockDiagnosticVMOptions -XX:+PrintCompressedOopsMode, чтобы подтвердить рабочий режим сжатого указателя.

JDK 7

Указатели сжатия включены по умолчанию:

$ java -server -Xms2G -Xmx2G -XX:+UseConcMarkSweepGC -XX:+UnlockDiagnosticVMOptions -XX:+PrintCompressedOopsMode -version

heap address: 0x000000077ae00000, size: 2130 MB, zero based Compressed Oops

java version "1.7.0_79"
Java(TM) SE Runtime Environment (build 1.7.0_79-b15)
Java HotSpot(TM) 64-Bit Server VM (build 24.79-b02, mixed mode)

JDK 8

Указатели сжатия включены по умолчанию:

$ java -server -Xms2G -Xmx2G -XX:+UseConcMarkSweepGC -XX:+UnlockDiagnosticVMOptions -XX:+PrintCompressedOopsMode -version

heap address: 0x0000000080000000, size: 2048 MB, Compressed Oops mode: 32-bit

Narrow klass base: 0x0000000000000000, Narrow klass shift: 3
Compressed class space size: 1073741824 Address: 0x000000013fe20000 Req Addr: 0x0000000100000000
java version "1.8.0_121"
Java(TM) SE Runtime Environment (build 1.8.0_121-b13)
Java HotSpot(TM) 64-Bit Server VM (build 25.121-b13, mixed mode)

Отключить сжатые указатели:

$ java -server -Xms2G -Xmx2G -XX:+UseConcMarkSweepGC -XX:-UseCompressedOops -XX:+UnlockDiagnosticVMOptions -XX:+PrintCompressedOopsMode -version
java version "1.8.0_121"
Java(TM) SE Runtime Environment (build 1.8.0_121-b13)
Java HotSpot(TM) 64-Bit Server VM (build 25.121-b13, mixed mode)

Пример сравнения

Тестовая среда: JDK 1.8.0_121

тестовый код

import java.util.LinkedList;
import java.util.List;
import java.util.Scanner;
public class IntegerApplication {
	public static void main(String[] args) {
		List<Integer> intList = new LinkedList<>();
		for (int i = 0; i < 2000000; i++) {
			Integer number = new Integer(1);
			intList.add(number);
		}
		Scanner scanner = new Scanner(System.in);
		System.out.println("application is running...");
		String tmp = scanner.nextLine();
		System.exit(0);
	}
}

Используйте Eclipse Memory Analyzer для просмотра количества и размера объектов Integer.

Сначала запускаем программу IntegerApplication, а потом проверяем выделение объекта через мат.

Включить сжатые указатели

Сжатые указатели включены по умолчанию (-XX:+UseCompressedOops).

$ java IntegerApplication
application is running...

Размер каждого целого числа:

64(Mark Word)+32(Compressed oops)+32(int)=128bits=16bytes

Общий размер всех целых чисел:

2000256*16=32004096bytes

закрыть указатель сжатия

Установите параметр -XX:-UseCompressedOops, чтобы отключить указатель сжатия.

$ java -XX:-UseCompressedOops IntegerApplication
application is running...

Размер каждого целого числа:

64(Mark Word)+64(Compressed oops)+32(int)=160bits=20bytes

Поскольку выделение памяти JVM необходимо выравнивать по ширине слова, для 64-битной JVM ширина слова составляет 8 байтов. Следовательно, Integer на самом деле занимает 24 байта, что составляет 192 бита.

Общий размер всех целых чисел:

2000256*24=48006144bytes

Как видно из приведенного выше примера, после включения указателя сжатия размер oop действительно стал 32 бита, и фактические результаты тестирования согласуются с теоретическим анализом.

Object Header

Object Header on a 64bit VM with compressed oops

Object Header on a 64bit VM without compressed oops

Object Header on a 32bit VM


Ссылаться на

Полное руководство по производительности Java Скотта Оукса

Woohoo. Java-программист is.com/2016/05/com…

Получите эти противные мехи. ITeye.com/blog/101007…

gist.GitHub.com/Artur покраска корпуса двери…

Личный публичный аккаунт

Для получения дополнительных статей, пожалуйста, обратите внимание на общедоступный номер: Binary Road

二进制之路