Для 32-разрядных машин максимальная память, которую может использовать процесс, составляет 4G. Если процессу требуется больше памяти, требуется 64-разрядная машина.
Для процесса Java, когда oop имеет только 32 бита, он может ссылаться только на память 4G. Поэтому, если вам нужно использовать большую память кучи, вам нужно развернуть 64-битную JVM. Таким образом, oop составляет 64 бита, а куча памяти, на которую можно ссылаться, больше.
Примечание: oop (обычный указатель объекта), обычный указатель объекта — это дескриптор, используемый для представления ссылочного объекта в JVM.
В куче ссылки на 32-битные объекты занимают 4 байта, а ссылки на 64-битные объекты занимают 8 байтов. То есть ссылка на 64-битный объект в два раза больше, чем 32-битный объект.
Хотя 64-разрядная JVM поддерживает большие кучи, она создает проблемы с производительностью из-за больших ссылок на объекты:
- Увеличенные накладные расходы GC
Ссылки на 64-разрядные объекты должны занимать больше места в куче, оставляя меньше места для других данных, что ускоряет сборку мусора и выполняет сборку мусора чаще.
- Уменьшить частоту попаданий в кэш процессора
Ссылка на 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