今天工作有点忙,没时间学习了,赶紧补上吧。
昨天开始学习class文件结构,今天继续。
一、class类文件结构
Class文件是一组以8位字节为基础单位的二进制流。
根据java虚拟机的规定,Class文件格式采用一种类似于C语言结构体的微结构来存储,这种伪结构只有两种数据类型:无符号数和表。
1、魔数与Class文件的版本,下面是从ue中拷贝出来的十六进制片段
package com.struts.action; public class TestClass { private int m; public int inc(){ return m+1; } }
00000000h: CA FE BA BE 00 00 00 32 00 16 07 00 02 01 00 1B ; 漱壕...2........
00000010h: 63 6F 6D 2F 73 74 72 75 74 73 2F 61 63 74 69 6F ; com/struts/actio
00000020h: 6E 2F 54 65 73 74 43 6C 61 73 73 07 00 04 01 00 ; n/TestClass.....
00000030h: 10 6A 61 76 61 2F 6C 61 6E 67 2F 4F 62 6A 65 63 ; .java/lang/Objec
00000040h: 74 01 00 01 6D 01 00 01 49 01 00 06 3C 69 6E 69 ; t...m...I... 00000050h: 74 3E 01 00 03 28 29 56 01 00 04 43 6F 64 65 0A ; t>...()V...Code. 00000060h: 00 03 00 0B 0C 00 07 00 08 01 00 0F 4C 69 6E 65 ; ............Line 00000070h: 4E 75 6D 62 65 72 54 61 62 6C 65 01 00 12 4C 6F ; NumberTable...Lo 00000080h: 63 61 6C 56 61 72 69 61 62 6C 65 54 61 62 6C 65 ; calVariableTable 00000090h: 01 00 04 74 68 69 73 01 00 1D 4C 63 6F 6D 2F 73 ; ...this...Lcom/s 000000a0h: 74 72 75 74 73 2F 61 63 74 69 6F 6E 2F 54 65 73 ; truts/action/Tes 000000b0h: 74 43 6C 61 73 73 3B 01 00 03 69 6E 63 01 00 03 ; tClass;...inc... 000000c0h: 28 29 49 09 00 01 00 13 0C 00 05 00 06 01 00 0A ; ()I............. 000000d0h: 53 6F 75 72 63 65 46 69 6C 65 01 00 0E 54 65 73 ; SourceFile...Tes 000000e0h: 74 43 6C 61 73 73 2E 6A 61 76 61 00 21 00 01 00 ; tClass.java.!... 000000f0h: 03 00 00 00 01 00 02 00 05 00 06 00 00 00 02 00 ; ................ 00000100h: 01 00 07 00 08 00 01 00 09 00 00 00 2F 00 01 00 ; ............/... 00000110h: 01 00 00 00 05 2A B7 00 0A B1 00 00 00 02 00 0C ; .....*?.?..... 00000120h: 00 00 00 06 00 01 00 00 00 03 00 0D 00 00 00 0C ; ................ 00000130h: 00 01 00 00 00 05 00 0E 00 0F 00 00 00 01 00 10 ; ................ 00000140h: 00 11 00 01 00 09 00 00 00 31 00 02 00 01 00 00 ; .........1...... 00000150h: 00 07 2A B4 00 12 04 60 AC 00 00 00 02 00 0C 00 ; ..*?..`?...... 00000160h: 00 00 06 00 01 00 00 00 07 00 0D 00 00 00 0C 00 ; ................ 00000170h: 01 00 00 00 07 00 0E 00 0F 00 00 00 01 00 14 00 ; ................ 00000180h: 00 00 02 00 15 ; ..... 每个Class文件的头4个字节成为魔数,它的唯一作用是用于确定这个文件是否为一个能被虚拟机接受的class文件,很多文件标准都采用魔数身份识别,使用魔数而不是扩展名主要是为了安全考虑,因为文件扩展名可以被随意的改动。 哈哈,发现一件很有意思的事情,class文件的前四个字节是 0xCAFEBABE(咖啡宝贝)。 我们来分析一行Class文件用ue打开后的十六进制内容吧: CA FE BA BE 00 00 00 32 前四个字节标示它是Class格式的,第五和第六标示次版本号,而主版本号的值为0x0032,即十进制的50,该版本号说明这个是可以被jdk1.6以上版本的虚拟机执行的Class文件。 2、常量池 常量池紧跟在主次版本之后,由于常量池中常量的数量是不固定的,所以在常量池的入口需要放置一项u2类型的数据,也就是两个字节的数据,代表常量池计数值。0x0016,即十进制的22,代表常量池中有21项常量。 常量池中主要存放两大类常量:字面量和符号引用,字面量比较接近java语言层面的常量概念,如文本字符串,被生命为final的常量值等,而符号引用则属于编译原理方面的概念,包括下面三类: (1)类和接口的全限定名 (2)字段的名称和描述符 (3)方法的名称和描述符 其它的推倒就不再累述,也没有细看,总之,十六进制格式字符对应前面所说的表里面的数据,都是有意义的,最终都可以翻译成原生的java文件。 二、虚拟机类加载机制 虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的java类型,这就是虚拟机的类加载机制。 在java语言里面,类型的加载和连接过程都是在程序运行期间完成的,这样会在类加载时稍微增加一点开销,但是却能为java应用程序提供高度的灵活性,java中天生可以动态扩展的语言特性就是依赖运行期动态加载和动态连接这个特点实现的。 类从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期包括了:加载,验证、准备、解析、初始化、使用和卸载七个阶段。其中验证准备和解析统称为连接。 对于初始化阶段,虚拟机规定了有且只有这四种情况,需要对类进行初始化(加载,验证,准备自然需要在此之前开始) (1)遇到new、getstatic、putstatic或invokestatic这四条指令码,生成这四条指令码最常见的java代码场景是:使用new关键字实例化对象、读取或设置一个类的静态字段(被final修饰,已在编译期把结果放入常量池的静态字段除外),以及调用一个类的静态方法的时候。 (2)使用java.lang.reflect包的方法对类进行反射调用的时候 (3)当初始化一个类的时候,如果发现父类还没有进行初始化,则需要先出发其父类的初始化 (4)当虚拟机启动时,用户需要指定一个要执行的主类(包括main()方法那个类),虚拟机会先初始化这个主类。