深入理解JVM(二)

本文详细解析了Java Class文件的结构,包括魔数、常量池、访问标识、类索引、字段表集合、方法表集合及属性表集合等关键组成部分。通过实例展示了如何使用javap工具分析Class文件,深入理解字节码格式与执行机制。

系列课程, 待更新......

前半节都学完了,我们已经很深入了,后面讲的和实际离的远,可以继续学习

第一章  Class文件简介和发展历史

1.1Class文件简介

接下来一段时间,我们将要进行枯燥有趣的看二进制的.class文件,

1.2 Class文件结构概述

1.3 Class文件设计理念以及意义

它们都是自己定义语法规范,编译器,把语言编译成jvm认识的字节码.CLASS文件执行

 

第二章、Class文件结构

2.1 文件结构 - 魔数(magic value)

也就是读取class文件第一行十六进制文件的含义,

KAFEBABE : 指的是class文件类型,对应十进制188...., 如果不匹配,就不是class文件

00 34:  对应十进制 52, 则得知jdk版本是1.8版本,主版本号

2.2 文件结构 - 常量池字节码数据的读取规则

接下来再按照字节长度往下面计算,两个字节代表常量池数量,

 

00 1D: 对应十进制是29(1 - 28)

class文件由无符号数和表组成

cp_info的结构: tag表示类型,首先根据tag值,验证出是哪一个类型, 根据类型找到类型的表,然后接着往下读。info[] 表示表的集合,里面可嵌套info

下面列出的就是info里面嵌套的一些info ,罗列几个

下面是罗列详细的前18个常量池 和class文件对应起来,说明常量池的解析过程

1D: 说明有29 个常量,

0A: 一个字节  10, 表示10号常量类型,

对应 constant_methodref_info(0A  0006  000F) --->   #1 = Methodref          #6.#15  ; 

它后面有四个字节,读完constant_methodref_info之后,再读一个字节的tag位

09: constant_fielderref_info(09   0010  0011) --->   #2 = Fieldref           #16.#17    ;

08: constant_Stringref_info(08  0012)  --->   #3 = String             #18            // hello, youngpeng

.......直到读取完29个常量池................

 

 知道了解析常量池的规则,我们就可以用工具来解析: java -verbose xxx.class

C:\Users\Administrator>cd Desktop

C:\Users\Administrator\Desktop>javap -verbose Hello.class
Classfile /C:/Users/Administrator/Desktop/Hello.class
  Last modified 2019-10-12; size 420 bytes
  MD5 checksum 66f7607a3c46c2e4a43d39d116856371
  Compiled from "Hello.java"
public class Hello
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #6.#15         // java/lang/Object."<init>":()V
   #2 = Fieldref           #16.#17        // java/lang/System.out:Ljava/io/PrintStream;
   #3 = String             #18            // hello, youngpeng
   #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, youngpeng
  #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
{
  public Hello();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 1: 0

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=1, args_size=1
         0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: ldc           #3                  // String hello, youngpeng
         5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         8: return
      LineNumberTable:
        line 3: 0
        line 4: 8
}
SourceFile: "Hello.java"

C:\Users\Administrator\Desktop>

 

2.3 文件结构 - 访问标识(access flag)

1.   这个是旧的(请忽略,参考而已)

这个位数才是新的 

 2.   先写一段程序接口

public interface Interf{
	public static void main(String args[]){
		
	}
}

3. 分析class文件

C:\Users\Administrator\Desktop>javap -verbose Interf.class
Classfile /C:/Users/Administrator/Desktop/Interf.class
  Last modified 2019-10-12; size 189 bytes
  MD5 checksum 304913b072ab554619c41b23c1fd4339
  Compiled from "Interf.java"
public interface Interf
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
Constant pool:
   #1 = Class              #9             // Interf
   #2 = Class              #10            // java/lang/Object
   #3 = Utf8               main
   #4 = Utf8               ([Ljava/lang/String;)V
   #5 = Utf8               Code
   #6 = Utf8               LineNumberTable
   #7 = Utf8               SourceFile
   #8 = Utf8               Interf.java
   #9 = Utf8               Interf
  #10 = Utf8               java/lang/Object
{
  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=0, locals=1, args_size=1
         0: return
      LineNumberTable:
        line 4: 0
}
SourceFile: "Interf.java"

C:\Users\Administrator\Desktop>

4.分析class字节码

flags: ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT

interface = ACC_PUBLIC +  ACC_INTERFACE +   ACC_ABSTRACT

0601 = 0001   +    0200   +    0400  

  

2.4 文件结构 - 类索引(class 文件格式)

介绍完class文件标识cafababe, 常量池,和文件标识(public,interface,abstract)之后,紧跟着就是

1. 内容

2. 编写程序

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.FileInputStream;
import java.io.FileNotFoundException;

public class Hello1 extends FileInputStream implements Runnable, ActionListener {
    public Hello1(String name) throws FileNotFoundException {
        super(name);
    }
    @Override
    public void actionPerformed(ActionEvent e) {
    }
    @Override
    public void run() {
    }
}

 

3. 用工具分析查看 

C:\Users\Administrator\Desktop>javap -verbose Hello1.class
Classfile /C:/Users/Administrator/Desktop/Hello1.class
  Last modified 2019-10-13; size 479 bytes
  MD5 checksum 2a88671d70b6efb7d259ca4043f13edd
  Compiled from "Hello1.java"
public class Hello1 extends java.io.FileInputStream implements java.lang.Runnable,java.awt.event.ActionListener
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #3.#18         // java/io/FileInputStream."<init>":(Ljava/lang/String;)V
   #2 = Class              #19            // Hello1
   #3 = Class              #20            // java/io/FileInputStream
   #4 = Class              #21            // java/lang/Runnable
   #5 = Class              #22            // java/awt/event/ActionListener
   #6 = Utf8               <init>
   #7 = Utf8               (Ljava/lang/String;)V
   #8 = Utf8               Code
   #9 = Utf8               LineNumberTable
  #10 = Utf8               Exceptions
  #11 = Class              #23            // java/io/FileNotFoundException
  #12 = Utf8               actionPerformed
  #13 = Utf8               (Ljava/awt/event/ActionEvent;)V
  #14 = Utf8               run
  #15 = Utf8               ()V
  #16 = Utf8               SourceFile
  #17 = Utf8               Hello1.java
  #18 = NameAndType        #6:#7          // "<init>":(Ljava/lang/String;)V
  #19 = Utf8               Hello1
  #20 = Utf8               java/io/FileInputStream
  #21 = Utf8               java/lang/Runnable
  #22 = Utf8               java/awt/event/ActionListener
  #23 = Utf8               java/io/FileNotFoundException
{
  public Hello1(java.lang.String) throws java.io.FileNotFoundException;
    descriptor: (Ljava/lang/String;)V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=2, args_size=2
         0: aload_0
         1: aload_1
         2: invokespecial #1                  // Method java/io/FileInputStream."<init>":(Ljava/lang/String;)V
         5: return
      LineNumberTable:
        line 9: 0
        line 10: 5
    Exceptions:
      throws java.io.FileNotFoundException

  public void actionPerformed(java.awt.event.ActionEvent);
    descriptor: (Ljava/awt/event/ActionEvent;)V
    flags: ACC_PUBLIC
    Code:
      stack=0, locals=2, args_size=2
         0: return
      LineNumberTable:
        line 13: 0

  public void run();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=0, locals=1, args_size=1
         0: return
      LineNumberTable:
        line 16: 0
}
SourceFile: "Hello1.java"

C:\Users\Administrator\Desktop>

4. 分析过程:

常量池结束位置: #23 = Utf8               java/io/FileNotFoundException

access_flags结束位置0021:  flags: ACC_PUBLIC, ACC_SUPER  -->  0001  or  0020 = 0021

2.5 文件结构 -字段表集合

1. 字段访问标识

2. 字段表集合

3. 写一段程序 

public class Hello{
	private int a;
	public byte b;
	public static Object obj;
	protected Object[] objs;
}

 4. 工具分析

C:\Users\Administrator\Desktop>javap -verbose Hello.class
Classfile /C:/Users/Administrator/Desktop/Hello.class
  Last modified 2019-10-13; size 288 bytes
  MD5 checksum 7b5c6270785b47745b8e50db5048cda2
  Compiled from "Hello.java"
public class Hello
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #3.#18         // java/lang/Object."<init>":()V
   #2 = Class              #19            // Hello
   #3 = Class              #20            // java/lang/Object
   #4 = Utf8               a
   #5 = Utf8               I
   #6 = Utf8               b
   #7 = Utf8               B
   #8 = Utf8               obj
   #9 = Utf8               Ljava/lang/Object;
  #10 = Utf8               objs
  #11 = Utf8               [Ljava/lang/Object;
  #12 = Utf8               <init>
  #13 = Utf8               ()V
  #14 = Utf8               Code
  #15 = Utf8               LineNumberTable
  #16 = Utf8               SourceFile
  #17 = Utf8               Hello.java
  #18 = NameAndType        #12:#13        // "<init>":()V
  #19 = Utf8               Hello
  #20 = Utf8               java/lang/Object

5. 分析过程

 #4 = Utf8               a           变量a
   #5 = Utf8               I          int类型
   #6 = Utf8               b         变量b
   #7 = Utf8               B         byte类型
   #8 = Utf8               obj       变量obj
   #9 = Utf8               Ljava/lang/Object;              L开头,引用类型
  #10 = Utf8               objs
  #11 = Utf8               [Ljava/lang/Object;             [L  开头,数组类型

得一个一个往下数:

2.6 文件结构 - 方法表集合

1. 方法表数据结构

2. 接着数对应的class文档

常量池结束  --  访问标志  --  类索引  --  父类索引  --  接口  --  字段表长度  --    方法表长度(默认加构造方法) -- 方法表结构【access_flags,...】

3. 写几个方法

public class Hello1{
    private int add (int a, int b){
        return a + b;
    } 
    public String append(String s, Object l){
        return s + l;
    }
}

4. 工具查看

C:\Users\Administrator\Desktop>javap -verbose Hello.class
Classfile /C:/Users/Administrator/Desktop/Hello.class
  Last modified 2019-10-13; size 559 bytes
  MD5 checksum e0d7132926bc95d3b6360d77abde2a5f
  Compiled from "Hello.java"
public class Hello
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #8.#19         // java/lang/Object."<init>":()V
   #2 = Class              #20            // java/lang/StringBuilder
   #3 = Methodref          #2.#19         // java/lang/StringBuilder."<init>":()V
   #4 = Methodref          #2.#21         // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   #5 = Methodref          #2.#22         // java/lang/StringBuilder.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder;
   #6 = Methodref          #2.#23         // java/lang/StringBuilder.toString:()Ljava/lang/String;
   #7 = Class              #24            // Hello
   #8 = Class              #25            // java/lang/Object
   #9 = Utf8               <init>
  #10 = Utf8               ()V
  #11 = Utf8               Code
  #12 = Utf8               LineNumberTable
  #13 = Utf8               add
  #14 = Utf8               (II)I
  #15 = Utf8               append
  #16 = Utf8               (Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/String;
  #17 = Utf8               SourceFile
  #18 = Utf8               Hello.java
  #19 = NameAndType        #9:#10         // "<init>":()V
  #20 = Utf8               java/lang/StringBuilder
  #21 = NameAndType        #15:#26        // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
  #22 = NameAndType        #15:#27        // append:(Ljava/lang/Object;)Ljava/lang/StringBuilder;
  #23 = NameAndType        #28:#29        // toString:()Ljava/lang/String;
  #24 = Utf8               Hello
  #25 = Utf8               java/lang/Object
  #26 = Utf8               (Ljava/lang/String;)Ljava/lang/StringBuilder;
  #27 = Utf8               (Ljava/lang/Object;)Ljava/lang/StringBuilder;
  #28 = Utf8               toString
  #29 = Utf8               ()Ljava/lang/String;
{
  public Hello();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 1: 0

  public int add(int, int);
    descriptor: (II)I
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=3, args_size=3
         0: iload_1
         1: iload_2
         2: iadd
         3: ireturn
      LineNumberTable:
        line 3: 0

  public java.lang.String append(java.lang.String, java.lang.Object);
    descriptor: (Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/String;
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=3, args_size=3
         0: new           #2                  // class java/lang/StringBuilder
         3: dup
         4: invokespecial #3                  // Method java/lang/StringBuilder."<init>":()V
         7: aload_1
         8: invokevirtual #4                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        11: aload_2
        12: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder;
        15: invokevirtual #6                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
        18: areturn
      LineNumberTable:
        line 6: 0
}
SourceFile: "Hello.java"

code:  内容是方法体的内容

()V:   表示 方法返回值空void

(I I)I:   表示 方法返回值空int, 两个int 参数

2.7 文件结构 - 属性表集合

方法表存储的是方法表的摘要信息,方法体的信息存储在属性表的code信息中。

属性表就是描述其他一些额外信息的,自定义和外定义属性,方法表属性表示不了了,用属性表补充。

1. 大概理解意思

2. 属性表结构

3. code表结构

第三章  字节码部分

学完字节码格式 ->  接下来关注字节码指令

  • 学完字节码格式 

  • 字节码指令

  • 类加载机制

  • 虚拟机字节码执行引擎

3.1 字节码指令简介

3.2 两种不同的架构执行 1 + 1 = 2 时的操作

3.3 字节码和数据类型

3.4 加载和存储指令

public class Hello{
	int add(int a, int b){
		int c = 1 + 3;
		return a + b;
	}
}
{
  int add(int, int);
    descriptor: (II)I
    flags:
    Code:
      stack=2, locals=4, args_size=3
         0: iconst_4
         1: istore_3
         2: iload_1
         3: iload_2
         4: iadd
         5: ireturn
      LineNumberTable:
        line 3: 0
        line 4: 2
}

我们会发现  参数总会多一个,那是因为默认会带一个 this 这个参数

istore  是编译器优化,编译时期直接将静态变量运算了

3.7 类加载指令-对象的创建和访问

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值