Heim > Java > javaLernprogramm > Hauptteil

Eine detaillierte Einführung in den JVM-Bytecode in Java

不言
Freigeben: 2018-10-10 11:43:54
nach vorne
3487 Leute haben es durchsucht

Dieser Artikel bietet Ihnen eine detaillierte Einführung in den JVM-Bytecode in Java. Ich hoffe, dass er für Freunde hilfreich ist.

Dies ist ein Artikel zu Java Basics (JVM). Ursprünglich wollte ich über den Java-Klassenlademechanismus sprechen, aber dann dachte ich darüber nach. Die Rolle der JVM besteht darin, den vom Compiler kompilierten Bytecode zu laden und interpretieren Sie ihn in einen Maschinencode. Dann sollten Sie zuerst den Bytecode verstehen und dann über den Klassenlademechanismus zum Laden des Bytecodes sprechen. Daher wird dieser Artikel in eine detaillierte Erklärung des Bytecodes geändert.

Aufgrund der rein objektorientierten Natur von Java kann der Bytecode das gesamte Java-Programm darstellen, solange die JVM die Informationen einer Klasse laden kann , es kann das gesamte Programm laden. Unabhängig davon, ob es sich um einen Bytecode- oder einen JVM-Lademechanismus handelt, liegt der Schwerpunkt auf Klassen. Meine Hauptsorgen sind:

1. Da der Bytecode nicht auf einmal in den Speicher geladen wird, woher weiß die JVM, wo sich die Klasseninformationen, die sie laden möchte, in der .class-Datei befinden?

2. Wie stellt Bytecode Klasseninformationen dar?

3. Wird der Bytecode das Programm optimieren?

Die erste Frage ist sehr einfach, denn selbst wenn eine Quelldatei viele Klassen hat (nur eine öffentliche Klasse), generiert der Compiler eine .class-Datei für jede Klasse und die JVM lädt sie nach Bedarf Laden Sie den geladenen Klassennamen.

Um das folgende Problem zu lösen, schauen wir uns zunächst die Zusammensetzung des Bytecodes an (öffnen Sie ihn mit Hex Fiend auf dem Mac).

Für einen Code wie diesen:

package com.test.main1;

public class ByteCodeTest {

	int num1 = 1;
	int num2 = 2;

	public int getAdd() {

		return num1 + num2;
	}
}

class Extend extends ByteCodeTest {
	public int getSubstract() {
		return num1 - num2;
	}
}
Nach dem Login kopieren

Lassen Sie uns die darin enthaltene Extend-Klasse analysieren.

Öffnen Sie die kompilierte .class-Datei mit Hex Fiend wie folgt (Hexadezimalcode):

Da die Klassendatei kein Trennzeichen hat, bedeutet jede Was jede Position darstellt , die Länge jedes Teils und andere Formate sind strikt reguliert, siehe Tabelle unten:

wobei u1, u2, u4, u8 ein vorzeichenloses Zeichen darstellt Anzahl mehrerer Bytes. In der dekompilierten Hexadezimaldatei stellen zwei Zahlen ein Byte dar, nämlich u1.

Sehen Sie es sich einzeln von Anfang bis Ende an:

(1) Magie: u4, die magische Zahl, bedeutet, dass es sich bei dieser Datei um eine .class-Datei handelt. .jpg usw. haben ebenfalls diese magische Nummer, selbst wenn *.jpg in *.123 geändert wird, kann es weiterhin wie gewohnt geöffnet werden.

(2) Nebenversion, Hauptversion: jeweils u2, Versionsnummer, abwärtskompatibel, das heißt, JDK mit höherer Version kann .class-Dateien mit niedrigerer Version verwenden, aber nicht umgekehrt.

(3) Constant_pool_count: u2, die Anzahl der Konstanten im Konstantenpool, 0019 repräsentiert 24.

(4) Als nächstes folgen die spezifischen Konstanten, insgesamt konstant_pool_count-1.

Der Konstantenpool speichert normalerweise zwei Arten von Daten:

Literale: wie Zeichenfolgen, endgültige geänderte Konstanten usw.;

Symbolreferenzen: wie der vollständige Name eines Klassen-/Schnittstellenqualifizierte Namen, Methodennamen und -beschreibungen, Feldnamen und -beschreibungen usw.

Schauen Sie sich anhand der dekompilierten Zahlen zunächst die Tabelle unten an, um den Typ und die Länge der Konstante zu ermitteln. Die nächste Zahl, die der von Ihnen nachgeschlagenen Länge entspricht, stellt den spezifischen Wert der Konstante dar.

Zum Beispiel 070002 bedeutet dies, dass der Typ CONSTANT_Class_info ist, sein Tag u1 ist und die Länge von u2 der Index ist, der auf das vollständig qualifizierte Namenskonstantenelement verweist. Dieser Index sollte auch in Verbindung mit der von javap -verbose geöffneten Klassendatei angezeigt werden. Der Inhalt und die Reihenfolge im Konstantenpool sind hier klar aufgeführt:

Sie können 0002 sehen hier Die Konstante des Indexelements lautet: com/test/main1/Extend, was der vollständig qualifizierte Name der Klasse ist. Wenn es sich bei dem Wert um eine Zeichenfolge handelt, müssen Sie den Wert in eine Dezimalzahl umwandeln und in der ASCII-Codetabelle nachschlagen, um die spezifischen Zeichen zu erhalten. Die folgenden Konstanten werden wie folgt analysiert:

01001563 6F6D2F74 6573742F 6D61696E 312F4578 74656E64: com/test/main1/Extend

070004: com/test/main1/ByteCodeTest

01001B63 6F6D2F74 6573742F 6D61696E 312F4279 7465436F 64655465 7374:com/test/main1/ByteCodeTest

0100063C 696E6974 3E:

01000328 2956: ()V

01000443 6F6465 : Code

0A000300 09: com/test/main1/ByteCodeTest, "": ()V

0C000500 06: , ()V

01000F4C 696E654E 756D6265 72546162 6C65: LineNumberTable

0100124C 6F63616C 56617269 61626C65 5461626C 65: LocalVariableTable

01000474 686973:dies

0100174C 636F6D2F 74657374 2F6D6169 6E312F45 7874656E 643B:Lcom/test / main1/Extend;

01000C67 65745375 62737472 616374: getSubstract

01000328 2949: ()I

09000100 11: com/test/main1/Extend,num. 1:Ich

0c001200 13: num1, i

0100046e 756d31: num1

01000149: i

09000100 15: com/test/main1/extend, num2: i

0C001600 13: num2, I

0100046E 756D32: num2

01000A53 6F757263 6546696C 65: SourceFile

01001142 79746543 6F646554 6573742E 6A617661: ByteCodeTest.java

Zu diesem Zeitpunkt wurden alle Konstanten im Konstantenpool analysiert.

(5) Als nächstes kommen die access_flags von u2: Der Hauptzweck des access_flags-Zugriffsflags besteht darin, zu markieren, ob die Klasse eine Klasse oder eine Schnittstelle ist, ob die Zugriffsberechtigung öffentlich ist. Ob es abstrakt ist und ob es als endgültig markiert ist usw., sehen Sie in der folgenden Tabelle:

Flag_nameValueInterpretation
ACC_PUBLIC0x0001表示访问权限为public,可以从本包外访问
ACC_FINAL0x0010表示由final修饰,不允许有子类
ACC_SUPER0x0020较为特殊,表示动态绑定直接父类,见下面的解释
ACC_INTERFACE0x0200表示接口,非类
ACC_ABSTRACT0x0400表示抽象类,不能实例化
ACC_SYNTHETIC0x1000表示由synthetic修饰,不在源代码中出现,见附录[2]
ACC_ANNOTATION0x2000表示是annotation类型
ACC_ENUM0x4000表示是枚举类型

所以,本类中的access_flags是0020,表示这个Extend类调用父类的方法时,并非是编译时绑定,而是在运行时搜索类层次,找到最近的父类进行调用。这样可以保证调用的结果是一定是调用最近的父类,而不是编译时绑定的父类,保证结果的正确性。

(6)this_class:u2的类索引,用于确定类的全限定名。本类的this_class是0001,表示在常量池中#1索引,是com/test/main1/Extend

(7)super_class:u2的父类索引,用于确定直接父类的全限定名。本类是0003,#3是com/test/main1/ByteCodeTest

(8)interfaces_count:u2,表示当前类实现的接口数量,注意是直接实现的接口数量。本类中是0000,表示没有实现接口。

(9)Interfaces:表示接口的全限定名索引。每个接口u2,共interfaces_count个。本类为空。

(10)fields_count:u2,表示类变量和实例变量总的个数。本类中是0000,无。

(11)fields:fileds的长度为filed_info,filed_info是一个复合结构,组成如下:

filed_info: {
  u2        access_flags;   
  u2        name_index;
  u2        descriptor_index;      
  u2        attributes_count;
  attribute_info   attributes[attributes_count];
}
Nach dem Login kopieren

由于本类无类变量和实例变量,故本字段为空。

(12)methods_count:u2,表示方法个数。本类中是0002,表示有2个。

(13)methods:methods的长度为一个method_info结构:

method_info {
  u2        access_flags;          0000    ?
  u2        name_index;           0005   <init>
  u2        descriptor_index;         0006    ()V
  u2        attributes_count;         0001    1个
  attribute_info   attributes[attributes_count];   0007    Code
}
Nach dem Login kopieren

其中attribute_info结构如下:

attribute_info {
  u2  attribute_name_index;  0007  Code
  u1  attribute_length;
  u1  info[attribute_length];
}
Nach dem Login kopieren

上面是通用的attribute_info的定义,另外,JVM里预定义了几种attribute,Code即是其中一种(注意,如果使用的是JVM预定义的attribute,则attribute_info的结构就按照预定义的来),其结构如下:

Code_attribute {  //Code_attribute包含某个方法、实例初始化方法、类或接口初始化方法的Java虚拟机指令及相关辅助信息
  u2  attribute_name_index;  0007       Code
  u4  attribute_length;    0000002F      47
  u2  max_stack;       0001        1 //用来给出当前方法的操作数栈在方法执行的任何时间点的最大深度
  u2  max_locals;      0001         1 //用来给出分配在当前方法引用的局部变量表中的局部变量个数
  u4  code_length;      00000005      5 //给出当前方法code[]数组的字节数
  u1  code[code_length];    2AB70008 B1    42、183、0、8、177 
        //给出了实现当前方法的Java虚拟机代码的实际字节内容 (这些数字代码实际对应一些Java虚拟机的指令)
  u2  exception_table_lentgh;   0000       0          //异常的信息
  {
    u2  start_pc;   //这两项的值表明了异常处理器在code[]中的有效范围,即异常处理器x应满足:start_pc≤x≤end_pc
    u2  end_pc;   //start_pc必须在code[]中取值,end_pc要么在code[]中取值,要么等于code_length的值
    u2  handler_pc; //表示一个异常处理器的起点
    u2  catch_type; //表示当前异常处理器需要捕捉的异常类型。为0,则都调用该异常处理器,可用来实现finally。
  } exception_table[exception_table_lentgh];      在本类中大括号里的结构为空
  u2  attribute_count;    0002          2    表示该方法的其它附加属性,本类有1个
  attribute_info  attributes[attributes_count];       000A、000B     LineNumberTable、LocalVariableTable
}
Nach dem Login kopieren

LineNumberTable和LocalVariableTable又是两个预定义的attribute,其结构如下:

LineNumberTable_attribute { //被调试器用来确定源文件中由给定的行号所表示的内容,对应于Java虚拟机code[]数组的哪部分
  u2  attribute_name_index;      000A
  u4  attribute_length;          00000006
  u2  line_number_table_length;     0001
  {  u2  start_pc;          0000    
     u2  line_number;         000E     //该值必须与源文件中对应的行号相匹配
  } line_number_table[line_number_table_length];
}
Nach dem Login kopieren

以及:

LocalVariableTable_attribute {
  u2  attribute_name_index;    000B
  u4  attribute_length;        0000000C
  u2  local_variable_table_length;   0001
  {  u2  start_pc;          0000
     u2  length;           0005
     u2  name_index;       000C
     u2  descriptor_index;    000D    //用来表示源程序中局部变量类型的字段描述符
    u2   index;          0000
  } local_variable_table[local_variable_table_length];
Nach dem Login kopieren

然后就是第二个方法,具体略过。

(14)attributes_count:u2,这里的attribute表示整个class文件的附加属性,和前面方法的attribute结构相同。本类中为0001。

(15)attributes:class文件附加属性,本类中为0017,指向常量池#17,为SourceFile,SourceFile的结构如下:

SourceFile_attribute {
  u2  attribute_name_index;  0017     SourceFile
  u4  attribute_length;      00000002   2
  u2  sourcefile_index;        0018     ByteCodeTest.java //表示本class文件是由ByteCodeTest.java编译来的
}
Nach dem Login kopieren

嗯,字节码的内容大概就写这么多。可以看到通篇文章基本都是在分析字节码文件的16进制代码,所以可以这么说,字节码的核心在于其16进制代码,利用规范中的规则去解析这些代码,可以得出关于这个类的全部信息,包括:

1. 这个类的版本号;

2. 这个类的常量池大小,以及常量池中的常量;

3. 这个类的访问权限;

4. 这个类的全限定名、直接父类全限定名、类的直接实现的接口信息;

5. 这个类的类变量和实例变量的信息;

6. 这个类的方法信息;

7. 其它的这个类的附加信息,如来自哪个源文件等。

解析完字节码,回头再来看开始提出的问题,也就迎刃而解了。由于字节码文件格式严格按照规定,可以用来表示类的全部信息;字节码只是用来表示类信息的,不会进行程序的优化。

那么在编译期间,编译器会对程序进行优化吗?运行期间JVM会吗?什么时候进行的,按照什么原则呢?这个留作以后再表。

最后,值得注意的是,字节码不仅是平台无关的(任何平台生成的字节码都可以在任何的JRE环境运行),还是语言无关的,不仅Java可以生成字节码,其它语言如Groovy、Jython、Scala等也能生成字节码,运行在JRE环境中。

Das obige ist der detaillierte Inhalt vonEine detaillierte Einführung in den JVM-Bytecode in Java. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Verwandte Etiketten:
jvm
Quelle:cnblogs.com
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage
Über uns Haftungsausschluss Sitemap
Chinesische PHP-Website:Online-PHP-Schulung für das Gemeinwohl,Helfen Sie PHP-Lernenden, sich schnell weiterzuentwickeln!