`

解读JVM06-类文件结构

    博客分类:
  • jvm
 
阅读更多

JVM如何实现平台无关性和语言无关性的?

平台无关性:使用java编译器为java源码为统一存储格式的字节码文件,实现不同平台的JVM去解释执行字节码。

语言无关性:实现不同语言的编译器,编译成统一存储格式的字节码文件,由某平台JVM去解释执行字节码。

 

什么是高位在前?

就是各个字节上的各个bit代表的数据的数位是从高到低。

123,表示一百二十三,就是高位在前的大端数;如果表示三百二十一,就是高位在尾的小端数。

 

对class文件数据结构表的理解。

类似于C语言结构体,仍旧由无符号数组成,用于描述有层次关系的复合结构的数据。

field_info {

    u2             access_flags;

    u2             name_index;

    u2             descriptor_index;

    u2             attributes_count;

    attribute_info attributes[attributes_count];

}

 

利用class文件数据结构如何表示某一类型数据的集合?

通常是一个前置的容器计数器n + n个连续的数据项组成。

如字段集合由1个u2类型的field_count和field_count个field_info数据项组成。

 

查看class文件的版本号。

方法一:用十六进制工具查看class文件,第7、8个字节即为版本号。

Linux工具:

od -t x1 Test.class   内核命令,推荐

hexdump Test.class

Windows工具:

Winhex16   还需要额外装个软件

文本编辑器的hexdump插件   如notepad++,vscode,推荐‘

方法二:使用反编译工具

javap -version Test.class

 

字节码查看器:

jclasslib bytecode viewer  用于查看字节码格式,神器呀

bytecode-viewer  一个java8 jar和android apk反向工程套件

 

由低版本JDK执行高版本class文件引发的异常。

Exception in thread "main" java.lang.UnsupportedClassVersionError: WriteLog : Unsupported major.minor version 52.0  

52.0对应jdk1.8即class文件是由jdk1.8的编译器生成,而JVM的版本低于1.8时,报不支持的Class版本错误

 

理解常量池。

常量池中的常量并不是指java中被static final修饰的变量,而是整个字节码文件的一个共享资源仓库,方便被后面的数据项重用。分成两大类:字面量和符号引用。

字面量:

基本类型字面量:当基本类型被static final修饰时生成,byte、char、boolean被转换成int

private final static int = 2;

字符串字面量:java中在任何地方出现的字符串字面量都被生成1个CONSTANT_String_info和1个CONSTANT_Utf8_info类型。

private String str = "hello";

utf-8字面量:

字节码文件中所有需要用到字符串描述的地方都会生成CONSTANT_Utf8_info类型。

 

符号引用:用来准确描述类、字段和方法的字符串。

描述类和接口:类和接口的全限定名   com/tiro/jvm/Test

描述字段:   类全限定义称.字符名称:字段描述符  com/tiro/jvm/Test.m:I

描述方法:   类全限定名称.方法名:方法描述符  java.lang.Object."<init>":()V 

 

为什么常量池的常量数量比容器计数要少1个?

常量的索引从1开始,如果有21个常量,索引从1~20

第0项常量空出来用于某些指向常量池的索引值的数据在特定情况下需要表达“不引用任何一个常量池项目”的含义。

 

解读一个空类的class文件。

 一个空类的class文件大小为182字节,也是一个合法的java字节码文件的最小存储空间了。

javap -verbose Test.class

常量池解读:

   #1 = Methodref          #3.#10         // java/lang/Object."<init>":()V                 //描述父类默认构造方法的符号引用

   #2 = Class              #11            // com/tiro/jvm/Test      //描述当前类的符号引用 

   #3 = Class              #12            // java/lang/Object     //描述父类的符号引用 

   #4 = Utf8               <init>            //默认构造方法的名称

   #5 = Utf8               ()V               //默认构造方法的描述符

   #6 = Utf8               Code           //方法的Code属性名称,所有方法都有

   #7 = Utf8               LineNumberTable     //方法的LineNumberTable属性名称,所有方法都有

   #8 = Utf8               SourceFile    //当前类的SourceFile属性名称

   #9 = Utf8               Test.java      //当前类的SourceFile属性值

  #10 = NameAndType        #4:#5          // "<init>":()V    //默认构造方法的部分符号引用 

  #11 = Utf8               com/tiro/jvm/Test          //当前类的全限定名称

  #12 = Utf8               java/lang/Object       //父类的全限定名称

 

方法解读:

当类没有显式定义构造方法时,会自动生成一个不带参数的构造方法。是不是很熟悉呀!

{

  public com.tiro.jvm.Test();

    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 3: 0

}

Code属性数据项的结构自行查阅。

stack=1,代表方法运行时,操作数栈深度的最大值。

locals=1,代表方法运行时,局部变量表的最大存储空间,单位为Slot。32位虚拟机中,不超过32位的数据类型占用1个Slot,long和double占用两个Slot;64位虚拟机中,所有的数据类型占用1个Slot。

args_size=1,代表当前的参数个数,隐含参数this必有,通常是存储在局部变量表的第0个Slot。

接着就是4条执行指令:

0: aload_0  把第0个Slot的引用(即this)加载到操作数栈;

1: invokespecial #1  调用父类java.lang.Object的默认构造方法,这条指令占3个字节,后两个字节存放操作数的常量索引

4: return  退出方法执行

 

 

解读实例变量、类变量、类常量的赋值。(以基础数据类型为例)

类常量:

public class Test {  

    private final static int m = 2; 

    public int add(int n) {  

        return m + n;  

    }  

}

编译阶段:

在常量池中生成1个CONSTANT_Integer_info类型的常量;

与任何其它字面量和常量的运算立即进行;

在字段表中生成1个ConstantValue的属性,属性值指向常量池中的索引。

类加载阶段:

在加载阶段会把class文件常量池的内容放入方法区的运行时常量池时中(字符串常量被移到堆中了)

方法调用:

iconst_2 对于int类型,在编译时被直接写入指令的操作数中,不需要从运行时常量池中获取

ldc2_w #2 对于long、double类型,从运行时常量池中加载常量到操作数

 

类变量:

public class Test {  

    private static int m = 2; 

    public int add(int n) {  

        return m + n;  

    }  

}

编译阶段:

如果类中有static语句块和类变量赋值行为,就会生成类初始化方法<clinit>,而类变量赋值在<clinit>中进行。

类加载阶段:

在准备阶段在方法区静态域中为类变量分配内存,为类变量赋零值,而不是实际值;

在初始化阶段执行<clinit>方法为类变量赋值。

方法调用:

getstatic #2 调用getstatic指令从方法区静态域中获取值

 

实例变量:

public class Test {  

    private int m = 2; 

    public int add(int n) {  

        return m + n;  

    }  

}

对象初始化阶段:

创建对象时,会为每个对象在堆中分配该对象的内存空间,并把所有实例变量设置为零值。

调用对象的<init>方法时,为有初始值的实例变量赋值,用到了iconst_2和putfield #2两个指令。

 

 

 

 

 

 

 

 

 

 

 

 

分享到:
评论

相关推荐

    jvm源码解读以及jvm调优,看的过程中会把c-c++文件也会放到里面进行解读-jvm-original.zip

    本资料主要探讨JVM的源码解读以及如何进行JVM调优,同时也涉及到了JVM内部实现中C/C++部分的解析。 1. **JVM结构概述** JVM由类装载器、运行时数据区、执行引擎、本地方法接口和本地方法库五大部分组成。其中,类...

    MSB企业级JVM优化与性能调优课程 解读JVM内部机制-针对性解决企业架构优化问题

    ### MSB企业级JVM优化与性能调优课程解读 #### 一、JVM基础知识与原理 ##### 1.1 JVM内存结构 JVM(Java虚拟机)内存模型主要包括堆内存(Heap)、方法区(Method Area)、程序计数器(Program Counter Register...

    Jvm堆栈dump文件分析

    当JVM遇到问题,如内存溢出或长时间的垃圾回收,它可能会生成堆栈转储(dump)文件,这些文件包含了JVM在特定时刻的详细状态,包括对象、类加载器、线程、堆和栈信息。"Jvm堆栈dump文件分析"是指通过特定工具对这些...

    JBOSS4.2.3 文件结构图

    ### JBoss 4.2.3 文件结构解析 JBoss AS 4.2.3是JBoss Application Server的一个版本,广泛应用于Java企业级应用程序的开发与部署。理解JBoss AS 4.2.3的文件结构对于管理和配置该服务器至关重要。下面我们将详细...

    jvm-introduction.rar_site:www.pudn.com

    以下是对这份"目前为止最清楚最精辟的Java内存介绍文档"的详细解读。 1. **Java内存区域** - **程序计数器**:每个线程都有自己的程序计数器,用于存储下一条指令的位置。 - **虚拟机栈**:与方法关联,每次方法...

    jvm调优.zip & hotspot源码解读

    6. 字节码文件结构深度剖析: .class文件包含类的元数据和字节码指令,理解字节码的构成和执行逻辑对于理解JVM如何运行Java代码至关重要。字节码优化,如即时编译(JIT),能显著提高代码运行效率。 7. JVM调优...

    JVM的那些事.pptx

    - **基于栈的体系结构**:JVM是基于栈的体系结构,通过操作数栈来执行字节码指令。 - **栈帧**:代表了一个方法的一次调用,包含局部变量区和操作数栈。局部变量区用于存储局部变量和方法参数;操作数栈用于存放...

    class的实例在JVM中的内部表示

    以上内容仅是对“类的实例在JVM中的内部表示”这一主题的基本解读,具体博客可能会更深入地讨论JVM内部的实现细节,例如对象头的精确结构、垃圾回收算法、类加载器的实现等。通过阅读`instanceKlass.xls`这个文件,...

    JVM构造思维导图

    Java虚拟机(JVM)是Java程序...以上是对JVM构造的详细解析,涵盖了其基本结构、内存模型、垃圾收集、线程管理和类加载等多个方面。理解这些知识点有助于开发者更好地优化Java应用程序,提升性能,并解决运行时问题。

    java源码解读-java-src:java源码解读

    源码解读可以帮助我们理解如何实现类加载器、垃圾回收机制,以及JVM的内存模型。比如,我们可以看到类的加载过程,包括加载、验证、准备、解析和初始化五个阶段,这有助于我们避免双亲委派模型中的类加载问题。 接...

    JAVA虚拟机解读入门

    1. 类装载器:负责查找并加载类文件到JVM内存中,确保类的唯一性。 2. 运行时数据区:包括堆、方法区、栈、本地方法栈和程序计数器。每个线程都有自己独立的栈和程序计数器,而堆和方法区则是所有线程共享的。 - 堆...

    Resin 3.1 配置文件.txt

    #### 二、配置文件结构与解析 **1. 基础配置** ```xml xmlns:resin="http://caucho.com/ns/resin/core"&gt; ``` 这部分定义了配置文件的命名空间,确保 XML 文件能够被正确解析。 **2. 类加载器配置** ```xml ...

    ha456.jar(IBMHeapAnalyzer)JVM内存分析工具

    它可以显示类的实例数量、占用的内存大小,以及引用结构等。通过这些信息,你可以发现哪些类或对象占用了大量内存,以及它们之间的引用关系,从而找出可能的内存泄漏源。 在分析过程中,IBMHeapAnalyzer可能还会...

    java源码解读-ITG-JavaBook01:Java面试高频源码解读

    深入理解JVM内存结构(堆、栈、方法区等)及垃圾回收机制(GC)对优化程序性能至关重要。通过分析对象创建、内存分配、垃圾回收等源码,可以深入理解JVM的工作原理。 五、反射与注解 Java反射API允许我们在运行时...

    class文件解析案例

    1. **类文件结构**: - **魔数**:标识文件是否为合法的class文件,固定值0CAFEBABE。 - **版本号**:包括Minor和Major版本号,表示该class文件的JVM版本。 - **常量池**:存储各种符号引用和字面量,如类名、...

    JVM 规范 java7版本

    本文将详细解读JVM规范Java 7版本中的一些核心知识点。 首先,JVM规范的版本标识了规范的更新和改进。在这个文档中提到的是Java SE 7 Edition,表明它是针对Java Platform Standard Edition 7版本的虚拟机规范。...

    从一个class文件深入理解Java字节码结构

    对于字节码文件中的每一个元素,如魔数、版本号、常量池等,都需要根据它们的结构进行解读。例如,魔数`0xCAFEBABE`用于识别文件格式;版本号则指示了字节码的Java版本,影响后续解析规则。 6. **常量池详解** ...

    基于Java Reflection自动逆向生成类间方法关系图的解析器.pdf

    在逆向分析Java文件时,它扮演着重要角色,因为最终需要通过JVM来加载和执行.class文件。JVM在运行时提供了动态加载、链接、验证和执行Java字节码的能力。 七、项目实践和应用 在论文中,作者提出的基于Java反射...

    JVM调优前戏之JDK命令行工具.docx

    在这篇文章中,我们将详细解读几个常用的JDK命令行工具,包括jhat、jmap、jstack等。 jhat jhat是JVM Heap Analysis Tool的缩写,主要用于分析jmap生成的dump。jhat内置了一个微型的HTTP/HTML服务器,生成dump的...

Global site tag (gtag.js) - Google Analytics