- 浏览: 19823 次
- 性别:
- 来自: 广州
文章分类
最新评论
-
zhangdunkang:
AnnotationSessionFactoryBean在哪个 ...
springMVC+hibernate配置
一、Java的语言无关性
到今天为止,或许大部分的程序员都还认为Java虚拟机执行Java程序是一件理所当然和天经地义的事情。但在Java发展之初,设计者们就考虑过了在Java虚拟机上运行其它语言的可能性。时至今日商业机构和开源机构以及在Java语言之外发展出一大批在Java虚拟机上运行的语言,如Clojure,Groovy,JRuby,Jython,Scala,等等。
实现语言无关性的基础仍然是虚拟机和字节码存储格式,使用Java编译器可以把Java代码编译为存储字节码的Class文件,使用JRuby等其它语言的编译器一样可以把程序编译成Class文件,虚拟机不需要关心Class来源于什么语言,只要它符合Class文件应用的结构就可以在Java虚拟机中运行。如下图所示:
图1:Java虚拟机提供的语言无关性
二、Class类文件的结构
Class文件是一组以8位字节为基础单位的二进制流,各个数据项目严格按照顺序紧凑地排列在Class文件之中。当遇到需要占用8个字节以上空间的数据项时,则会按照高位在前的方式分割成若干个8位字节进行存储。
根据Java虚拟机规范的规定,Class文件格式采用一种类似于C语言结构体的伪结构来存储,这种伪结构只有两种数据类型:无符号数和表。
无符号数属于基本的数据类型,以u1,u2,u4,u8来分别代表1个字节、2个字节、4个字节和8个字节的无符号数。
表是由多个无符号数或其他表作为数据项组成的符合数据类型。表用于描述有层次关系的复合结构的数据,整个Class文件本质上就是一张表,由下图所示的数据项构成:
图2:Class文件格式
下面首先给出一段简单的Java程序,以这段代码编译成的Class文件为基础进行Class文件结构的介绍,代码如下:
- public class TestClass{
- private int num;
- public int inc(){
- for(int i=0; i<10; i++){
- num = i;
- }
- return num;
- }
- public static void main(String[] args){
- new TestClass().inc();
- }
- }
public class TestClass{ private int num; public int inc(){ for(int i=0; i<10; i++){ num = i; } return num; } public static void main(String[] args){ new TestClass().inc(); } }
把代码编译成Class文件后,使用16进制编辑器WinHex打开这个Class文件,可以看到Class的内部结构如下:
图3:Class文件的结构
2.1 魔数与Class文件的版本
从上面的图2可以看出,Class文件格式表中,第一项为一个u4类型的名为magic的数据项。这是因为每个Class文件的头4个字节称为魔数(Magic Number),它的唯一作用就是用来确定这个文件是否为一个能被虚拟机接受的Class文件。很多文件存储标准中都使用魔数来进行身份识别。例如图片格式。使用魔数而不是扩展名来进行识别主要是基于安全考虑,因为文件扩展名可以很随便地被人为改动。
Class文件的魔数的值为:0xCAFFBABE,这个名称的由来据说还有个很有趣的典故,在这里就不详细表述了,有兴趣的可以百度或者google!
class文件魔数值如下图所示:
图4:Class文件的魔数
紧接着魔数的4个字节存储的是Class文件的版本号:第5和第6字节是次版本号(Minor Version),第7个和第8个字节是主版本号(Major Version)。Java的版本号是从45开始的,Jdk1.1之后的每个JDK版本发布主版本号向上加1,高版本的JDK能向下兼容以前版本的Class文件,但不能运行以后版本的Class文件。
根据上面的图4所示,代表版本号的第5个字节和第6个字节值为0x0000,而主版本号的值为0x0032,即十进制的50,该版本号说明这个是可以被JDK1.6或以上版本的虚拟机执行的Class文件。
下图列出了从jdk1.1到jdk1.7之间,主流的JDK版本编译器输出的默认的和可支持的Class文件版本号:
图5:Class文件版本号
2.2 常量池
紧接着主次版本号之后的是常量池入口,常量池是Class文件结构中与其他项目关联最多的数据类型。常量池之中主要存放两大类常量:字面量(Literal)和符号引用(Symbolic Reference)。字面量比较接近于Java语言层面的常量概念,如文本字符串、被声明为final的常量值等。而符号引用则属于编译原理方面的概念,包括了下面三类常量:
1.类和接口的全限定名(Fully Qualified Name)
2.字段的名称和描述符(Descriptor)
3.方法的名称和描述符。
使用Javap命令输出常量表
- D:\JVM>javap -verbose TestClass
- Compiled from "TestClass.java"
- public class TestClass extends java.lang.Object
- SourceFile: "TestClass.java"
- minor version: 0
- major version: 50
- Constant pool:
- const #1 = Method #6.#20; // java/lang/Object."<init>":()V
- const #2 = Field #3.#21; // TestClass.num:I
- const #3 = class #22; // TestClass
- const #4 = Method #3.#20; // TestClass."<init>":()V
- const #5 = Method #3.#23; // TestClass.inc:()I
- const #6 = class #24; // java/lang/Object
- const #7 = Asciz num;
- const #8 = Asciz I;
- const #9 = Asciz <init>;
- const #10 = Asciz ()V;
- const #11 = Asciz Code;
- const #12 = Asciz LineNumberTable;
- const #13 = Asciz inc;
- const #14 = Asciz ()I;
- const #15 = Asciz StackMapTable;
- const #16 = Asciz main;
- const #17 = Asciz ([Ljava/lang/String;)V;
- const #18 = Asciz SourceFile;
- const #19 = Asciz TestClass.java;
- const #20 = NameAndType #9:#10;// "<init>":()V
- const #21 = NameAndType #7:#8;// num:I
- const #22 = Asciz TestClass;
- const #23 = NameAndType #13:#14;// inc:()I
- const #24 = Asciz java/lang/Object;
- {
- public TestClass();
- 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 inc();
- Code:
- Stack=2, Locals=2, Args_size=1
- 0: iconst_0
- 1: istore_1
- 2: iload_1
- 3: bipush 10
- 5: if_icmpge 19
- 8: aload_0
- 9: iload_1
- 10: putfield #2; //Field num:I
- 13: iinc 1, 1
- 16: goto 2
- 19: aload_0
- 20: getfield #2; //Field num:I
- 23: ireturn
- LineNumberTable:
- line 5: 0
- line 6: 8
- line 5: 13
- line 8: 19
- StackMapTable: number_of_entries = 2
- frame_type = 252 /* append */
- offset_delta = 2
- locals = [ int ]
- frame_type = 250 /* chop */
- offset_delta = 16
- public static void main(java.lang.String[]);
- Code:
- Stack=2, Locals=1, Args_size=1
- 0: new #3; //class TestClass
- 3: dup
- 4: invokespecial #4; //Method "<init>":()V
- 7: invokevirtual #5; //Method inc:()I
- 10: pop
- 11: return
- LineNumberTable:
- line 12: 0
- line 13: 11
- }
D:\JVM>javap -verbose TestClass Compiled from "TestClass.java" public class TestClass extends java.lang.Object SourceFile: "TestClass.java" minor version: 0 major version: 50 Constant pool: const #1 = Method #6.#20; // java/lang/Object."<init>":()V const #2 = Field #3.#21; // TestClass.num:I const #3 = class #22; // TestClass const #4 = Method #3.#20; // TestClass."<init>":()V const #5 = Method #3.#23; // TestClass.inc:()I const #6 = class #24; // java/lang/Object const #7 = Asciz num; const #8 = Asciz I; const #9 = Asciz <init>; const #10 = Asciz ()V; const #11 = Asciz Code; const #12 = Asciz LineNumberTable; const #13 = Asciz inc; const #14 = Asciz ()I; const #15 = Asciz StackMapTable; const #16 = Asciz main; const #17 = Asciz ([Ljava/lang/String;)V; const #18 = Asciz SourceFile; const #19 = Asciz TestClass.java; const #20 = NameAndType #9:#10;// "<init>":()V const #21 = NameAndType #7:#8;// num:I const #22 = Asciz TestClass; const #23 = NameAndType #13:#14;// inc:()I const #24 = Asciz java/lang/Object; { public TestClass(); 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 inc(); Code: Stack=2, Locals=2, Args_size=1 0: iconst_0 1: istore_1 2: iload_1 3: bipush 10 5: if_icmpge 19 8: aload_0 9: iload_1 10: putfield #2; //Field num:I 13: iinc 1, 1 16: goto 2 19: aload_0 20: getfield #2; //Field num:I 23: ireturn LineNumberTable: line 5: 0 line 6: 8 line 5: 13 line 8: 19 StackMapTable: number_of_entries = 2 frame_type = 252 /* append */ offset_delta = 2 locals = [ int ] frame_type = 250 /* chop */ offset_delta = 16 public static void main(java.lang.String[]); Code: Stack=2, Locals=1, Args_size=1 0: new #3; //class TestClass 3: dup 4: invokespecial #4; //Method "<init>":()V 7: invokevirtual #5; //Method inc:()I 10: pop 11: return LineNumberTable: line 12: 0 line 13: 11 }
2.3 访问标志
在常量池结束之后,紧接着的两个字节代表访问标志(access_flags),这个标志用于识别一些类或接口层次的访问信息,包括:这个Class是接口还是类;是否定义为public类型;是否定义为abstract类型;如果是类的话,是否声明为final,等等。
2.4 类索引、父类索引与接口索引集合
类索引(this_class)和父类索引(super_class)都是一个u2类型的数据,而接口索引集合(interfaces)是一组u2类型的数据的集合,Class文件中由这三项数据来确定这个类的继承关系。类索引用于确定这个类的全限定名,父类索引用于确定这个类的父类的全限定名。Java不允许多重继承,所以父类索引只有一个,除了java.lang.Object外,所有Java类的父类索引都不为0。接口索引集合就用来描述这个类实现了哪些接口,所有被实现的接口按类定义中的implements(如果类是一个接口则是extends)后的接口顺序从左到右排列在接口的索引集合中。
2.5 字段表集合
字段表(field_info)用于描述接口或类中声明的变量。字段(field)包括了类级变量和实例级变量,但不包括方法内部声明的变量。一个字段的信息包括:作用域(public、private、protected修饰符)、是实例变量还是类变量(static修饰符)、可变性(final)、并发可见性(volatile修饰符,是否强制从主内存读写)、可否序列化(transient修饰符)、字段数据类型(基本数据类型、对象、数组)、字段名称。这些信息中,各个修饰符都是布尔值,要么有,要么没有。
全限定名称:如果TestClass类是定义在com.chenzhou.jvm包中,那么这个类的全限定名为com/chenzhou/jvm/TestClass。
简单名称:简单名称指没有类型和参数修饰的方法或字段名称,在上面的例子中,TestClass类中的inc()方法和num字段的简单名称分别为“inc”和“num”。
描述符:描述符的作用是用来描述字段的数据类型、方法的参数列表(包括数量、类型以及顺序)和返回值。根据描述符规则,基本数据类型(byte,char,double,float,int,long,short,boolean)及代表无返回值的void类型都用一个大写字符来表示,而对象则用字符L加对象的全限定名来表示,如下图所示:
图6:描述符标识字符含义
对于数组类型,每一维度将使用一个前置的“[”字符来描述,如定义一个“java.lang.String[][]”类型的二维数组,将会被记录为:"[[Ljava/lang/String;",一个整型数组"int[]"将被记录为"[I"。
用描述符来描述方法时,按照先参数列表,后返回值的顺序描述,参数列表按照严格的顺序放在一组小括号"()"内,如方法void inc()的描述符为"()V",方法java.lang.String.toString()的描述符为"()Ljava/lang/String;",方法int indexOf(char[] source,int offset,int count,char[] target,int tOffset,int tCount,int fromIndex)的描述符为"([CII[CIII)I"。
2.6 方法表集合
Class文件存储格式中对方法的描述与对字段的描述几乎完全一致。方法表的结构如同字段表一样,一次包括了访问标志、名称索引、描述符索引、属性表集合几项。
2.7 属性表集合
Java虚拟机规范第二版中预定义了9项虚拟机应当能识别的属性,包括:Code、ConstantValue、Deprecated、Exceptions、InnerClasses、LineNumberTable、LocalVariableTable、SourceFile、Synthetic。
发表评论
-
JDK JRE 的作用与区别【转】
2014-04-24 21:19 479对于java初学者来说,往 ... -
JAVA为什么要配置环境变量,怎样配置
2014-04-24 21:19 401自己总结些再加抄点:安装JDK后要配置环境变量,主要有三个: ... -
Java四种线程池的使用
2014-03-23 22:51 484Java通过Executors提供四种线程池,分别为:ne ... -
JVM学习笔记(九):基于栈的解释器执行过程
2014-03-23 22:52 417本文根据《深入理解jav ... -
JVM学习笔记(八):类加载器以及双亲委派模型介绍
2014-03-23 22:52 375本章内容根据《深入理解Java虚拟机》第7章部分内容整理 ... -
JVM学习笔记(七):类加载的过程
2014-03-23 22:51 550本文根据《深入理解jav ... -
JVM学习笔记(六):类加载的时机
2012-08-24 09:54 486本文根据《深入理解java虚拟机》第7章部分内容整理 ... -
JVM学习笔记(五):根据字节码指令介绍方法执行流程
2012-08-24 09:54 757在上一篇博客中介绍了《Class文件结构》,其中就提到了一个例 ... -
JVM学习笔记(三):JVM的垃圾回收
2012-08-24 09:53 670注:本文根据《深入理解Java虚拟机》第3章部分内容整理而成。 ... -
JVM学习笔记(二):JVM中对象访问的两种方式
2012-08-24 09:52 1111由于Reference类型在Java虚拟机规范里面只规定了一个 ... -
JVM学习笔记(一):运行时数据区
2014-03-23 22:51 560运行时数据区 java虚拟机定义了若干种程序运行时 ... -
Java编码原理与解决方案 .
2014-03-24 12:24 580一、Java编码是怎么回事? 对于使用中文以及其他非拉丁语 ...
相关推荐
### JVM学习笔记(一) #### 一、JVM概述与工具使用 JVM(Java Virtual Machine)是Java语言的核心组成部分之一,它为Java程序提供了一个跨平台的运行环境。本篇学习笔记主要介绍如何利用一系列工具来查看和监控JVM...
这个“java之jvm学习笔记五(实践写自己的类装载器)”很可能是对这一主题的详细探讨。 类装载器在Java中的主要职责是动态加载类到JVM中。Java的类装载器分为三个基本层次:启动类装载器(Bootstrap ClassLoader)、...
本篇JVM学习笔记主要涵盖了以下几个核心知识点: 1. **运行时数据区**: - **程序计数器**:记录当前线程执行的字节码的行号,用于线程恢复执行时跳转到正确位置。 - **Java虚拟机栈**:每个方法执行时创建的栈帧...
### JVM学习笔记 #### JVM内存模型 (JMM) JVM内存模型主要分为以下几个部分: - **Java堆**:这是所有线程共享的一块区域,在虚拟机启动时创建。主要用于存放对象实例,几乎所有的对象实例都在这里分配内存。 - *...
- 统一且强大的 Class 文件结构是 JVM 的核心,它构建了一个统一的执行环境。 - **前端编译器的主要任务** - 将 Java 源代码转换为符合 JVM 规范的字节码文件。 - `javac` 是 Java 语言的标准前端编译器,用于将...
源码编译由Java源码编译器完成,这个过程包括了源码的分析、输入到符号表、注解处理和语义分析,最终生成包含结构信息、元数据和方法信息的.class文件。执行阶段则是由JVM执行引擎解析字节码并执行。这一过程涉及到...
### JVM学习笔记核心知识点整理 #### 一、引言与背景 随着软件开发技术的不断发展,Java作为一种广泛应用的编程语言,其背后的核心技术——Java虚拟机(JVM)的重要性日益凸显。掌握JVM不仅可以帮助开发者更好地理解...
《JVM学习笔记》 Java虚拟机(JVM)是Java平台的核心组成部分,它负责运行所有的Java应用程序。这篇笔记将深入探讨JVM的工作原理、内存管理、类加载机制以及优化策略,帮助读者全面理解JVM并提升Java程序的性能。 ...
今天,我们要讨论的是 JVM 的笔记,这个笔记包括了 JVM 的基本结构、类加载机制、类文件格式等内容。 首先,让我们来看一下 JVM 的基本结构。JVM 可以分为三个部分:CLASS LOADER、RUN TIME DATA AREA 和 EXECUTION...
这篇学习笔记主要探讨了Class文件的结构、内容和重要特性。 首先,Class文件是一种语言独立的存储格式,不仅用于Java,也可以支持其他如Scala等JVM为基础的语言。它由字节码构成,这种格式允许JVM执行不同语言编译...
【Java分布式应用学习笔记-谈JVM】 在Java分布式应用中,JVM(Java虚拟机)扮演着至关重要的角色。虽然有些人可能认为分布式系统与JVM的关系并不密切,但事实上,尤其是在大型分布式环境,如云计算服务平台,对Java...
其中,加载是找到类的.class文件并读入内存;验证确保字节码的正确性;准备为类的静态变量分配内存并初始化为默认值;解析将符号引用转换为直接引用;初始化执行类的初始化方法。了解这个过程有助于我们理解和控制类...
Java_JVM This is my Java JVM learn notes JVM: java虚拟机 一:上篇——内存与垃圾回收器 二:中篇——字节码与类的加载 三:下篇——性能监控与调优篇 一: 上篇——内存与垃圾回收器 架构: jvm依赖的架构: 栈架构...
JVM通过解释执行字节码(.class文件)来实现Java的“一次编写,到处运行”特性。JVM的主要组件包括类装载器、运行时数据区、执行引擎、本地方法接口和本地方法库。 1. 类装载器:负责加载.class文件,分为引导类...
总的来说,了解JVM的工作原理、类文件的结构以及编译过程对于Java开发者来说至关重要,因为这能帮助我们更好地优化代码、理解和解决问题,从而提高程序的运行效率和稳定性。通过学习这些知识,我们可以深入探究Java...
加载是找到.class文件并读入内存;验证确保字节码的正确性,防止恶意代码;准备阶段为静态变量分配内存,并设置默认初始值;解析将符号引用转换为直接引用;初始化执行类构造器。 二、内存管理 JVM内存主要分为堆...
这份JVM相关的笔记包含了深入理解JVM内部工作机制的关键代码资源,是学习和优化Java应用程序的重要参考资料。下面,我们将深入探讨Java与JVM的相关知识点。 1. **类加载机制**:JVM通过类加载器(ClassLoader)将...
【描述】"ImagesForJVM——JVM笔记图片" 暗示这些图片可能是教学或学习笔记的一部分,旨在通过视觉化的方式解释JVM的关键概念,如内存模型、类加载机制、垃圾收集以及性能优化等方面。 【标签】"java" 明确了这些...