• ,

Работа с компилятором в командной строке

Всем привет! Сегодня начал читать книгу «Изучаем Java», наткнулся на 1 практическое задание и застрял на нем. В частности: последняя (4) картинка: не понимаю, как нужно «запустить JVM»?
Буду очень благодарен за разъяснения! Заранее спасибо.
Фрагмент книги "Изучаем Java"
  • ,

В теории о байт-коде, JVM и микроконтроллерах | Java-код для МК

Статья-черновик. В конечном итоге напишу несколько вики-подобных статей, по схеме:

Тема: Где и как работает Ява
-Об устройстве Явы
-О железе
-Как этот софт работает на железе

Статья была об проблемах, с которыми я столкнулся. Изучив инфу, дополнил её ответами.
Представляет из себя кашу-малашу из тем сверху.

Доброго!

Знакомство с байт-кодами Java

В IntellijIDEA есть замечательная фича — показать байткод, позволяющая посмотреть как и в итоге JRE будет выполнять написанный код.(впрочем это также некоторая интерпретация компилятором машинного байткода для удобства чтения)

Находится в View->Show Byte Code

Что делает — анализирует скомпилированные .class — файлы. Описание команд можно найти по ссылке википедии.

Соответственно появилась идея разобрать байткоды для двух вариантов main-метода с инициализацией одинаковых строк, и попытаться прояснить почему они дают разные результаты:

Вариант 1:(если сравнить str1==str2, то получаем false)
public static void main(String[] args){
        String str1 = new String("test");
        String str2 = new String("test");
    }

Вариант 2:(если сравнить str3==str4, то получаем true)
public static void main(String[] args){
        String str3 = "test";
        String str4 = "test";
    }


Байткод первого варианта(в квадратных скобках[] будет состояние стека после выполнения команды(вершина стека справа)):

public static main([Ljava/lang/String;)V
   L0
    LINENUMBER 5 L0
    NEW java/lang/String  
    //новая строка - кладем ссылку на класс "String "на вершину стека: [value_string]
    
    DUP                 
    //копируем ссылку на вершине стека: [value_string, value_string]
    
    LDC "test"            
    //кладём в стек ссылку на "test" из constant pool(а если в нём еще нет ссылки на "test",
    //то она также остается в constant pool'e): [value_string, value_string, value_test]

    INVOKESPECIAL java/lang/String.<init> (Ljava/lang/String;)V 
    //вызов new для String с аргументами с вершины стека - 
    //после вызова инициализирующего метода для стринг(и его внутренних манипуляций со стеком,
    //которые аналогично можно посмотреть в байткоде java.lang.String )
    //- в стеке будет ссылка на проинициализированную строку: [value_result_string]
    //Почему для инициализации String(String) нужно три аргумента в стеке:
    //1)ссылка на входной аргумент 2)новая строка, 3)хешкод строки

    ASTORE 1        
    //сохраняем ссылку с вершины стека в локальную переменную(1) с вершины стека: [ ]
   L1
    LINENUMBER 6 L1
    NEW java/lang/String //аналогично первой строке
    DUP
    LDC "test"   
    INVOKESPECIAL java/lang/String.<init> (Ljava/lang/String;)V
    ASTORE 2    
   L2
    LINENUMBER 7 L2
    RETURN    //return void - компилятор дописывает за нас если метод возвращает void
   L3
    LOCALVARIABLE args [Ljava/lang/String; L0 L3 0    // список локальных переменных метода
    LOCALVARIABLE str3 Ljava/lang/String; L1 L3 1    // L1(метка где операции с локальной переменной) L3(метка где описание что это за локальная переменная) 1(номер локальной переменной)     
    LOCALVARIABLE str4 Ljava/lang/String; L2 L3 2    // 
    MAXSTACK = 3    //максимальная глубина стека метода
    MAXLOCALS = 3    //количество локальных переменных метода
                     //
                     //То есть сначала JRE будет выделять память при 
                     //вызове метода учитывая всю вложенность вызовов методов и new - инициализаций
                     //(и если её не хватит - то всё вылетит с каким-нибудь OutOfMemory
                     //эксепшном), а только потом будут производиться операции


Байткод второго варианта:

public static main([Ljava/lang/String;)V
   L0
    LINENUMBER 5 L0
    LDC "test"    //берем ссылку на "test" из constant_pool и кладем на вершину стека: [value_test]
    ASTORE 1      //забираем ссылку с вершины стека в переменную(1) str1 : [ ]
   L1
    LINENUMBER 6 L1
    LDC "test"    //берем ссылку на "test" из constant_pool и кладем на вершину стека: [value_test]
    ASTORE 2      //забираем ссылку с вершины стека в переменную(2) str2: [ ]
   L2
    LINENUMBER 7 L2
    RETURN        //return void (который компилятор дописывает за нас как успешный конец метода)
   L3
    LOCALVARIABLE args [Ljava/lang/String; L0 L3 0  //список локальных переменных, под которые выделяется память
    LOCALVARIABLE str1 Ljava/lang/String; L1 L3 1   //например: L1(метка где записан код) L3(метка где описан тип объекта и выделяется память) 1(локальный номер объекта)
    LOCALVARIABLE str2 Ljava/lang/String; L2 L3 2   //
    MAXSTACK = 1    //максимальная глубина стека для метода
    MAXLOCALS = 3    //количество локальных переменных метода



Ссылки на почитать ещё:
src1: http://cs.au.dk/~mis/dOvs/jvmspec/ref--33.html
src2: http://mihaimoldovan.com/download/Inside-Java-Virtual-Machine.pdf
src3: http://en.wikipedia.org/wiki/Java_class_file#The_constant_pool
src4: http://en.wikipedia.org/wiki/Java_bytecode_instruction_listings
src5: http://stackoverflow.com/questions/10209952/java-constant-pool
src6: http://stackoverflow.com/questions/14150628/string-constant-pool-java

Так почему же в одном случае строки равны, а в другом нет? Потому что в случае new String — игнорируется string constant pool, и создается именно новая копия строки(ссылка src6 про это). И хоть байткод не очень помог прояснить это, очевидно что во втором случае он совершает меньше операций, а потому этот вариант лучше.