Страницы

Поиск по вопросам

понедельник, 15 апреля 2019 г.

Пул констант в Java

Не раз слышал о так называемом пуле констант в языке программирования Java. Знаю о пуле объектов типа String, пуле для типов Byte, Short, Character, Integer, Long и даже Boolean. Также знаю, что мы сами можем определять размер пула типа Integer, если запускать приложение с параметром -Djava.lang.Integer.IntegerCache.high=XX, где XX может колебаться в диапазоне от 127 до (Integer.MAX_VALUE - 129). Весь этот пул представляет собой массив, каждое значение которого является элементом расположенным в порядке числового возрастания и мы можем напрямую обратиться к нему по индексу за константное время O(1). Я прекрасно понимаю, где хранится этот массив. У каждого целочисленного обёрточного типа есть свой вложенный класс такого типа:
private static class WrappingСlassNameCache {...}
Где вместо WrappingСlassNameCache, мы подставляем конкретное имя класса, к примеру, IntegerCache. Внутри это выглядит так:
private static class IntegerCache { static final int low = -128; static final int high; static final Integer cache[];
static { // high value may be configured by property int h = 127; String integerCacheHighPropValue = sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high"); if (integerCacheHighPropValue != null) { try { int i = parseInt(integerCacheHighPropValue); i = Math.max(i, 127); // Maximum array size is Integer.MAX_VALUE h = Math.min(i, Integer.MAX_VALUE - (-low) -1); } catch( NumberFormatException nfe) { // If the property cannot be parsed into an int, ignore it. } } high = h;
cache = new Integer[(high - low) + 1]; int j = low; for(int k = 0; k < cache.length; k++) cache[k] = new Integer(j++);
// range [-128, 127] must be interned (JLS7 5.1.7) assert IntegerCache.high >= 127; }
Ну Бог с ним, тут ничего мудрёного нет! А что же такое пул констант? Что он из себя представляет? И вообще где хранятся все константы в Java? Если мы говорим о локальных финализированных переменных, то они хранятся в стековой памяти. Если мы имеем дело со static final полями, то эти поля хранятся в MetaSpace (раньше в PermGen), если мы говорим о реализации JVM HotSpot. Так о каком вообще пуле может идти речь? Как всё это реализовано? Описано в каком-то классе, который поставляется вместе со стандартным API от Oracle (раньше Sun Microsystems) или же это нужно заглядывать в реализацию конкретной JVM и читать JVMS? Подскажите, пожалуйста, надоело, что в которой раз сталкиваюсь с этим понятием и никак не могу понять о чём идёт речь. Всем огромное спасибо за помощь! :)


Ответ

У каждого класса свой пул констант. Чтобы понять для чего он нужен, разберём простой пример
class Example { public void hello() { System.out.println("Hello"); } }
Скомпилируем
$ javac Example.java
И заглянем внутрь
$ javap -c -v Example.class
public class Example minor version: 0 major version: 52 flags: ACC_PUBLIC, ACC_SUPER Constant pool: #1 = Methodref #6.#14 // java/lang/Object."":()V #2 = Fieldref #15.#16 // java/lang/System.out:Ljava/io/PrintStream; #3 = String #17 // Hello #4 = Methodref #18.#19 // java/io/PrintStream.println:(Ljava/lang/String;)V #5 = Class #20 // Example #6 = Class #21 // java/lang/Object #7 = Utf8 #8 = Utf8 ()V #9 = Utf8 Code #10 = Utf8 LineNumberTable #11 = Utf8 hello #12 = Utf8 SourceFile #13 = Utf8 Example.java #14 = NameAndType #7:#8 // "":()V #15 = Class #22 // java/lang/System #16 = NameAndType #23:#24 // out:Ljava/io/PrintStream; #17 = Utf8 Hello #18 = Class #25 // java/io/PrintStream #19 = NameAndType #26:#27 // println:(Ljava/lang/String;)V #20 = Utf8 Example #21 = Utf8 java/lang/Object #22 = Utf8 java/lang/System #23 = Utf8 out #24 = Utf8 Ljava/io/PrintStream; #25 = Utf8 java/io/PrintStream #26 = Utf8 println #27 = Utf8 (Ljava/lang/String;)V { public Example(); descriptor: ()V flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: aload_0 1: invokespecial #1 // Method java/lang/Object."":()V 4: return LineNumberTable: line 1: 0
public void hello(); descriptor: ()V flags: ACC_PUBLIC Code: stack=2, locals=1, args_size=1 0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream; 3: ldc #3 // String Hello 5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 8: return LineNumberTable: line 3: 0 line 4: 8 }
Первое, что представляет интерес - это байткоды метода hello
0: getstatic #2 3: ldc #3 5: invokevirtual #4
Цифры до двоеточия обозначают байт, с которого начинается байткод и его параметры. По шагу видно, что каждая операция - это два байта, один байт на код операции и один байт на параметр. Байткоды могут принимать только параметры фиксированного размера - integer, long, short, byte, character, float, double, reference. Чтобы передать в метод println строку "Hello", надо загрузить в стек ссылку на строку "Hello", а саму строку где-то сохранить. Вот это где-то как раз и есть пул констант. А символы #3 после байткода ldc - ссылка на 3-й элемент в пуле констант.
Constant pool: #1 = Methodref #6.#14 // java/lang/Object."":()V #2 = Fieldref #15.#16 // java/lang/System.out:Ljava/io/PrintStream; #3 = String #17 // Hello #4 = Methodref #18.#19 // java/io/PrintStream.println:(Ljava/lang/String;)V
Как можно увидеть из этого кусочка пула, его элементы сами часто ссылаются в пул. В частности наша строка под номером 3 ссылается на массив символов под номером 17.
Если говорить о расположении пула констант в памяти JVM, то это один из участков MetaSpace.

Комментариев нет:

Отправить комментарий