Почему в JVM Integer хранится как байт и короткий?

вот один фрагмент кода

public class Classifier {
    public static void main(String[] args) 
    {
        Integer x = -127;//this uses bipush
        Integer y = 127;//this use bipush
        Integer z= -129;//this use sipush
        Integer p=32767;//maximum range of short still sipush
        Integer a = 128; // use sipush
        Integer b = 129786;// invokes virtual method to get Integer class

    }

}

вот частичный байтовый код этого

      stack=1, locals=7, args_size=1
         0: bipush        -127
         2: invokestatic  #16                 // Method java/lang/Integer.valueO
f:(I)Ljava/lang/Integer;
         5: astore_1
         6: bipush        127
         8: invokestatic  #16                 // Method java/lang/Integer.valueO
f:(I)Ljava/lang/Integer;
        11: astore_2
        12: sipush        -129
        15: invokestatic  #16                 // Method java/lang/Integer.valueO
f:(I)Ljava/lang/Integer;
        18: astore_3
        19: sipush        32767
        22: invokestatic  #16                 // Method java/lang/Integer.valueO
f:(I)Ljava/lang/Integer;
        25: astore        4
        27: sipush        128
        30: invokestatic  #16                 // Method java/lang/Integer.valueO
f:(I)Ljava/lang/Integer;
        33: astore        5
        35: ldc           #22                 // int 129786
        37: invokestatic  #16                 // Method java/lang/Integer.valueO
f:(I)Ljava/lang/Integer;
        40: astore        6
        42: return

как я вижу, для целого диапазона между -128 to 127 он использует bipush которые помещают байт в стек как целое значение. В диапазоне -32768 to 32767 он использует short как класс оболочки as sipush. Для next он использует Integer. Что JVM использует byte и short для хранения целочисленного значения?

3 ответов


насколько я понял.

как вы можете из оставшейся инструкции байтового кода, она не хранит int as byte или short Во-первых, почему bipush или short : bipush имеет 2 байта один для кода операции и второй для значения. я.Е, который может находиться в диапазоне от -128 127 тп (я.е 2 мощность 8) Это экономит пространство и время исполнения. Как вы можете видеть из компилятора кода remianing создает ссылку на эту переменную как целочисленный тип

2: invokestatic  #16                 // Method java/lang/Integer.valueO
f:(I)Ljava/lang/Integer;

а то astore_1 что store what's on top of the stack i.e a reference here into local variable 1 Похожие для sipush где вы можете хранить значение из диапазона (-32768 to 32767) beacuse это набор инструкций 3 байта, один байт для кода операции и отдохните два байта для значения (i.Э может держать силу 2 16)

теперь почему бы и нет lDC JVM имеет постоянный пул для каждого типа. Байтовый код требует данных, но большую часть времени эти данные слишком велики, чтобы хранить их непосредственно в байтовых кодах. таким образом, он хранится в пуле констант, а байтовый код содержит ссылку на пул констант. Что?!--12--> нажимает a constant #index из постоянного пула (String, int или float) в стек Что потребляет дополнительное время и циклы Вот примерное сравнение между ldc, а bipush операция

enter image description hereenter image description here

байт-код JVM ref здесь написано

где возможный, свой эффективный для использования одного из bipush, sipush, или одна из инструкций const вместо ldc.


он не хранится как byte или short во время выполнения, только в байт-коде. Предположим, вы хотите сохранить значение 120 на Integer. Вы пишете компилятор, поэтому вы анализируете исходный код и знаете постоянное значение 120 может поместиться в один байт со знаком. Потому что вы не хотите тратить место в байт-коде для хранения значения 120 как 32-битное(4 байта) значение, если его можно сохранить в 8 бит (1 байт), вы создадите специальную инструкцию, которая сможет загрузить только один byte от байт-код метода и сохраните его в стеке как 32bit integer. Это означает, что во время выполнения у вас действительно есть integer тип данных.

полученный код меньше и быстрее, чем при использовании ldc везде, где необходимо более тесное взаимодействие с JVM в beceause манипуляции с пула константы этапа выполнения.

bipush имеет 2 байта, один байт кода операции, второй байт непосредственного значения constat. Поскольку у вас есть только один байт для значения, его можно использовать для значений между -128 и 127.

sipush имеет 3 байта, один байт кода операции, Второй и третий размер постоянное значение.

bipush:

bipush
byte

sipush:

sipush
byte1
byte2

одной из причин могут быть преимущества, касающиеся байтового кода, которые были упомянуты в других ответах.

однако, можно также рассуждать об этом, начиная с язык. В частности, вы не хотите вставлять приведение, когда (постоянное) значение фактически представляется в целевом типе.

Итак, одна из причин наблюдаемого поведения: компилятор использует наименьший возможный тип который может представлять заданную константу значение.

для назначения на int (или Integer) это не было бы необходимо - но это не наносит никакого вреда, когда байт-код присваивает "меньший" тип "большему" типу. И наоборот, для меньших типов это is необходимо использовать меньший тип, поэтому использование "байт-кода для наименьшего типа" является поведением по умолчанию.


Это также неявно упоминается как "сужение времени компиляции постоянных выражений" в 5.2., Контексты Назначения спецификации языка Java:

сужающее примитивное преобразование может использоваться, если тип переменной-byte, short или char, а значение выражения константы можно представить в типе переменной.

...

сужение времени компиляции постоянных выражений означает, что код, такой как:

byte theAnswer = 42;

разрешено. Без сужения, тот факт, что целое число литерал 42 имеет тип int будет означать, что приведение к байту потребуется:

byte theAnswer = (byte)42;  // cast is permitted but not required