`
wupuyuan
  • 浏览: 75926 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

JVM学习笔记之CLASS结构和动态链接模型

阅读更多

 

    之前分析了方法调用和运行的过程,基本上可以满足代码层的需求了,不过为了更好的理解调用和运行的过程,还是分析下CLASS文件的结构和他动态链接的模型。

    首先还是得看下CLASS文件的结构。CLASS文件的结构分为(只列了简单的总结,我目前也用不到太深入的):
1.magic(魔数):是一个定值0xCAFEBABE,不是它开头的文件就不是CLASS。为什么是这么个数呢?据说是巧合,我们不管它。它是JVM校验一个JAVA文件的第一个步骤。

2.minor_version 和 major_version : CLASS的版本号,用于标识支持的JDK的版本范围。比如JDK1.5编译出的CLASS文件是不能被更早的JDK加载的。

3.constant_pool_count 和 constant_pool : 常量池,记录了文字字符串、final值、类名和方法名的常量。在常量池之前是入口在列表中的计数器。 常量池在java动态链接的过程中有十分重要的作用。一个CLASS文件都有自己的常量池,但是很少资料说常量池在内存中的哪。我推测是在方法区。(不过对于写代码来说无关紧要)

4.access_flags : 对应成代码就是标记的接口还是类,是不是final 、 抽象等。

5.this_class : 是对常量池的索引,简单来说就是让JVM知道这个CLASS对应的常量池范围。

6.super_class : 顾名思义,父类的信息,依然是个常量池的所以,只不过这里标记的是父类对应的常量池范围。

7.interfaces_count 和 interfaces : interfaces_count是实现的接口数量,紧跟着的interfaces是实现的接口数组,包含每个接口对应的常量池索引。

8.fields_count 和 fields : 自身CLASS文件的类变量+实例变量的总数 和 对应的序列表

9.methods_count 和 methods : 自身CLASS文件的所有方法总数 和 对应的序列表

10.attributes_count 和 attributes : CLASS属性,介绍有很多,简单来说就是内部类和非内部类InnerClasses和SourceCode。

    每个CLASS文件在加载到内存的时候,都是保存在方法区。加载到方法区后,主要的属性就是属性表、方法表、常量池。

    弄清楚了CLASS结构以后就可以看看在JVM中,不同的文件是怎么链接到一起的了。
    JVM编译一个java程序的时候,会得到程序中每个类(或者接口)的class文件,这些CLASS文件通过接口(harbor)符号相互关联,在JVM运行时,动态的将这些接口符号组织成链接CLASS的网。
   之前说过CLASS包含一个很重要的部分,常量池,所有的接口符号就是保存在这里的。每个CLASS都有一个常量池,每个被JVM加载的CLASS有个自己的内部常量池,或者说运行时常量池。当CLASS刚刚被加载的时候,运行时常量池存储的就是刚刚说的接口符号(harbor),当程序运行到一个时间点,需要调用某个类时,JVM将解析运行时常量池的接口符号,根据符号引用查找到实体(每个CLASS在JVM加载后都会保存在方法区中的某个位置,在运行前不知道某个具体的CLASS将会被加载到内存中的具体地址),再把符号引用替换成直接引用的过程。又因为符号都保存在常量池中,这一步骤也称为常量池解析。

    常量池解析是在运行前就知道了将要链接的CLASS,在代码里可以认为是 Object o = new Object()这样。不过JVM也可以在运行时决定链接某一类型(反射),比如Class.forName()和用户自定义装载器loadCLass()等方法。这种动态运行是才决定链接具体的CLASS的功能,可以调用在代码中没有提及的类型,所以很灵活,当然所带来的开销也很大,自然反射的效率要低于new的效率。
由此可见在灵活和效率上,程序不能兼顾,不能所有的地方都采用反射来创建。

    之后在装载时还会有一系列的解析和校验的过程。不过步骤都比较统一,过程也很死板,人为干预不了,对于代码优化来说没有太大意义,这里就不详细列举了。不过有个地方我觉得还是值得看一下,就是装载类时的双亲委派模型
    理解这个模型首先也得理解Java术语中的初始类装载器和定义类装载器。JVM中的Class装载器是相互委托的,比如在要求某个类装载器去装载一个类型,结果返回的是其他类装载器装载的类型。比如门面模式经常可以看到。所以在加载CLASS时一般通过委派先递归到系统装载器,若系统装载器不能装载这个类型,则控制权返回给自定义的加载器来加载。双亲模型说白了就是先系统JDK加载器加载,再用户自定义加载。这种模式的好处还是灵活。
    举个例子,如果按JDK1.2以前的模型来看,现在想要加入脚本语言比如ruby、groovy等,一定得等到JDK加入相关的加载类才能使用,而现在只需要有对应的第三方jar即可使用了。
    
    至此,我的《深入JVM虚拟机》的学习告一段落,现在写代码用到的只有这么多,希望能有更好的环境让我研究下去。也希望牛人能来拍拍砖。


2
7
分享到:
评论
2 楼 g29times 2012-05-19  
真好 学习了
1 楼 沙和尚 2012-04-21  
你好,请教一个字符串的问题
public class A {
        public String x = "data";
}
public class B {
    public String y = "data";
}
AB这两个类里面的"data"字符串应该都是存在class文件的常量池中,载入jvm的常量池之后
应该是A类"data"存到A类的运行时常量池中,B类的"data"存放到B类的运行时常量池中,但是为什么
A.x == B.y
这个会是true啊?求解

相关推荐

Global site tag (gtag.js) - Google Analytics