Страницы

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

воскресенье, 9 февраля 2020 г.

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

#java #переменные #const #константа #pool


Не раз слышал о так называемом пуле констант в языке программирования 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? Подскажите, пожалуйста, надоело, что в которой раз сталкиваюсь с
этим понятием и никак не могу понять о чём идёт речь. Всем огромное спасибо за помощь! :)
    


Ответы

Ответ 1



У каждого класса свой пул констант. Чтобы понять для чего он нужен, разберём простой пример 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.

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

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