Как стать автором
Обновить

Комментарии 16

Люблю такого типа статьи, которые не надо в избранное добавлять с мыслями: «Ой как интересно, надо будет когда-нибудь почитать».
Короткая, понятная.
Прочитал за 4 минуты и узнал кое-что новое. Спасибо автору.
А как Вы (или, кто то другой) эту схему нарисовали?
Я, в Microsoft Powerpoint.
Подозреваю, что имелось в виду несколько другое: откуда получили информацию, изображенную на картинке. Как можно посмотреть содержимое пула констант для своего кода?
Есть стандартная тулза javap. Запускайте javap -v <имя-класса>.
Добрый день!
Вы написали:
Если вы автоматически генерируете Java-код, помните, что максимальный номер константы в пуле не превышает 65535, после этого будет ошибка компиляции.

Это означает, что всего констант в классе может быть не больше 65535?
В одном классе — да. В приложении, конечно, нет.
Не понял, а массив как преставляется (и инициализируется)? Загрузкой по одной константе из пула конструктором?
Присоединяюсь к вопросу. Был случай, когда не удалось создать большой массив с инициализацией при создании. Судя по всему как раз из за этого.
Ответил ниже. Скорее всего, вы напоролись на ограничение на размер метода (который тоже 65535, в этом случае компилятор напишет Code too large). Добавление одной записи в double-массив занимает 6 байт байткода для индексов 0-5 (когда iconst_x можно использовать), 7 для индексов 6-127 (когда bipush можно использовать) и 8 для индексов 128-32767 (когда sipush можно использовать). Можно прикинуть, что в конструктор влезет инициализация массива немногим больше 8000 элементов. Тут в размер метода вперёд упрётесь, чем в пул констант.
Именно загрузкой по одной константе или в <init> (то есть приписывается к конструктору) или в <clinit> (для статических массивов). Пусть в классе объявлено поле вида:
double a[] = {1.0,2.0,3.0,4.0,5.0,6.0};

Тогда фактически к каждому (!) конструктору приклеивается кусок кода вида
a = new double[6];
a[0] = 1.0;
a[1] = 2.0;
a[2] = 3.0;
a[3] = 4.0;
a[4] = 5.0;
a[5] = 6.0;

По факту байткод немножко короче, потому что используется инструкция dup вместо того, чтобы в каждой строчке заново делать getfield, но суть такая же.
String — строка. Содержит ссылку на константу типа Utf8

А не UTF-16, не?
А мне вот интересно, почему на картинке номера констант идут в каком-то странном порядке, с пропусками? Где константа #1 и остальные пропущенные? Или они тратятся на описание метода, класса и пакета, в котором зашит этот код?
Да, так и есть. Вот полный код и пул:
public class Hello {
  public static void main(String... args) {
    System.out.println("Hello World!");
  }
}


   #1 = Methodref          #6.#15         //  java/lang/Object."<init>":()V
   #2 = Fieldref           #16.#17        //  java/lang/System.out:Ljava/io/PrintStream;
   #3 = String             #18            //  Hello World!
   #4 = Methodref          #19.#20        //  java/io/PrintStream.println:(Ljava/lang/String;)V
   #5 = Class              #21            //  Hello
   #6 = Class              #22            //  java/lang/Object
   #7 = Utf8               <init>
   #8 = Utf8               ()V
   #9 = Utf8               Code
  #10 = Utf8               LineNumberTable
  #11 = Utf8               main
  #12 = Utf8               ([Ljava/lang/String;)V
  #13 = Utf8               SourceFile
  #14 = Utf8               Hello.java
  #15 = NameAndType        #7:#8          //  "<init>":()V
  #16 = Class              #23            //  java/lang/System
  #17 = NameAndType        #24:#25        //  out:Ljava/io/PrintStream;
  #18 = Utf8               Hello World!
  #19 = Class              #26            //  java/io/PrintStream
  #20 = NameAndType        #27:#28        //  println:(Ljava/lang/String;)V
  #21 = Utf8               Hello
  #22 = Utf8               java/lang/Object
  #23 = Utf8               java/lang/System
  #24 = Utf8               out
  #25 = Utf8               Ljava/io/PrintStream;
  #26 = Utf8               java/io/PrintStream
  #27 = Utf8               println
  #28 = Utf8               (Ljava/lang/String;)V


У класса Hello генерируется конструктор по умолчанию (имя конструктора — #7, сигнатура — #8; для объявления метода NameAndType не нужен), который вызывает конструктор родительского класса Object, используя #1 (отсюда и #6, #7, #8, #15, #22). #9 и #10 — названия атрибутов для хранения собственно байткода и таблицы номеров строк (для отладки или распечатки стектрейсов исключений). #11 и #12 нужны для объявления метода main(), #13 — название атрибута с названием исходного файла, а #14 — значение этого атрибута (тоже для отладки и стектрейсов). #21 — имя класса.

Если найду время, напишу более подробный пост про устройство .class-файлов.
Спасибо за развёрнутый ответ, всё объяснено просто и доходчиво. А подробный пост был бы очень интересен.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации