Pull to refresh

Пул констант

Reading time 3 min
Views 30K
Многие знают, что в каждом .class-файле есть замечательная структура данных, которая называется пулом констант. Но далеко не каждый Java-разработчик, глядя на исходник, сможет даже примерно оценить, сколько констант будет создано в пуле.

Возьмём, к примеру, такой код:

System.out.println("Hello World!");

Он транслируется в три инструкции байткода: getstatic (для загрузки статического поля System.out), ldc (для загрузки константной строки «Hello World!») и invokevirtual (для выполнения виртуальной функции println). Попробуйте прикинуть, сколько констант нужно для того, чтобы этот код работал.

Оказывается, эта строка использует 14 констант, которые схематично можно изобразить таким образом:

Как мы видим, большинство констант являются ссылками на другие константы. У каждой константы есть тип, значение и номер, по которому на неё можно сослаться. Вот базовые типы, которые ни на что не ссылаются:
  • Integer — целое число. На них ссылаются из кода, там, где число потребовалось. Числа от -32768 до 32767 компилятор сюда не складывает: их можно явно указать в коде с помощью инструкций iconst_x, bipush и sipush.
  • Long — длинное целое. По историческим причинам занимает две позиции в пуле констант (то есть у следующей константы номер на два больше). Сэкономить можно только на 0L и 1L с помощью lconst_x; 2L уже попадает в пул.
  • Float — вещественное одинарной точности. Специальные инструкции fconst_x есть для 0.0f, 1.0f и 2.0f; остальные используемые числа попадают в пул.
  • Double — вещественное двойной точности. Как и Long, занимает две позиции в пуле. Только числа 0.0 и 1.0 можно использовать без пула с помощью инструкций dconst_x.
  • Utf8 — строковой литерал в кодировке Utf8. Максимальная длина не превышает 65535 байт (символов, конечно, может быть меньше). Как раз на эти литералы ссылаются из других констант чаще всего.

А вот некоторые из ссылочных типов:
  • String — строка. Содержит ссылку на константу типа Utf8. Если вы используете в коде строку, вы ссылаетесь на константу типа String. Выглядит как лишняя сущность, но есть.
  • NameAndType — константа, описывающая имя и тип поля или метода. Содержит две ссылки на Utf8-константы с именем и типом.
  • Class — константа, описывающая имя класса или интерфейса. Содержит ссылку на Utf8-константу с внутренним именем класса.
  • Fieldref — константа, описывающая конкретное поле конкретного класса. Содержит ссылку на Class-константу и ссылку на NameAndType-константу.
  • MethodRef, InterfaceMethodRef — константы, описывающие конкретный метод класса или интерфейса. Содержат ссылки на Class-константу и NameAndType-константу.

Для обращения к полю вам нужно не только его имя, но и полное имя класса, где поле объявлено, а также тип поля. Для обращения к методу вам нужен класс, имя и сигнатура метода. К счастью, вся сигнатура кодируется в одну строку вне зависимости от количества параметров метода: примитивные типы кодируются одной буквой (например, D = double), объекты — буквой L, за которой следует полное имя класса и точка с запятой, а один уровень массива добавляет квадратную скобку. В скобочках идут типы аргументов, а за ними — возвращаемый тип (V — это void). Например, сигнатура (IDLjava/lang/Thread;)Ljava/lang/Object; будет у такого метода:
Object m(int i, double d, Thread t) {...}

От того, что для ссылки на метод требуется его точная сигнатура, разработчики иногда напарываются на NoSuchMethodError. Скажем, вы поменяли возвращаемый тип метода на более конкретный. Тогда вызовы этого метода из других классов остаются прежними, и компилятор не будет пересобирать эти классы, потому что исходные файлы не изменились. Однако при попытке вызвать этот метод во время выполнения, Java-машина будет искать старую сигнатуру.

Если вы автоматически генерируете Java-код, помните, что максимальный номер константы в пуле не превышает 65535, после этого будет ошибка компиляции. Этого не такое большое число, учитывая огромное количество ссылок и то, что long и double занимают по две позиции. Знание устройства пула поможет вам контролировать его заполнение при генерации кода.

Подробнее с устройством пула констант можно ознакомиться в §4.4 спецификации виртуальной машины Java.
Tags:
Hubs:
+44
Comments 16
Comments Comments 16

Articles