[转载]Class文件在JVM中如何存储

JDK6 HotSpot VM用instanceKlass来记录类的元数据,每个Java类有一个对应的instanceKlass。
每个instanceKlass上引用着一个constantPoolOopDesc对象,然后间接引用着一个constantPoolCacheOopDesc对象。前者跟Class文件里记录的常量池的结构类似,而后者是为了让解释器运行得更高效的一个缓存。

举例的话,用VisualVM里的SA Plugin来演示,java.lang.String的状况。
这里我用JDK 7的一个预览版,build 96来运行VisualVM 1.3和一个groovysh,并且用VisualVM里的SA Plugin来观察groovysh的运行状态:

图1:java.lang.String对应的一个instanceKlass


留意到instanceKlass里有个_constants字段,引用着一个constantPoolOopDesc对象(后面简称constantPool对象)。

图2:观察constantPool对象的内容:


留意到它是一个类似数组的对象,里面有_length字段描述常量池内容的个数,后面就是常量池项了。
各个类型的常量是混在一起放在常量池里的,跟Class文件里的基本上一样。
最不同的是在这个运行时常量池里,symbol是在类之间共享的;而在Class文件的常量池里每个Class文件都有自己的一份symbol内容,没共享。

图3:观察constantPool里其中一个Utf8常量的内容:


这张图的关注点是位于0x180188a8的一个symbol对象(内容是"intern"),它的结构跟数组类似,有_length来记录长度,后面是UTF-8编码的字节。

这些Utf8常量在HotSpot VM里以symbolOopDesc对象(下面简称symbol对象)来表现;它们可以通过一个全局的SymbolTable对象找到。注意:constantPool对象并不“包含”这些symbol对象,而只是引用着它们而已;或者说,constantPool对象只存了对symbol对象的引用,而没有存它们的内容。

让我们来看看原本的Class文件里内容是怎样的:

D:\temp\jdk7b96\jdk1.7.0\fastdebug\bin>javap -verbose -private java.lang.String | more  
Classfile jar:file:/D:/temp/jdk7b96/jdk1.7.0/fastdebug/jre/lib/rt.jar!/java/lang/String.class  
  Last modified 2010-6-3; size 23741 bytes  
  MD5 checksum 293ab9f6781f6cd7d8f1dcaeabf1701c  
  Compiled from "String.java"  
public final class java.lang.String extends java.lang.Object implements java.io.  
Serializable, java.lang.Comparable<java.lang.String>, java.lang.CharSequence  
  Signature: #405                         // Ljava/lang/Object;Ljava/io/Serializable;Ljava/lang/Comparable<Ljava/lang/String;>;Ljava/lang/CharSequence;  
  SourceFile: "String.java"  
  InnerClasses:  
       static #134 of #40; //class java/lang/String$1 of class java/lang/String  
       private static #137= #128 of #40; //CaseInsensitiveComparator=class java/lang/String$CaseInsensitiveComparator of class java/lang/String  
  minor version: 0  
  major version: 51  
  flags: ACC_PUBLIC, ACC_FINAL, ACC_SUPER  
Constant pool:  
    #1 = Methodref          #130.#408     //  java/lang/Object."<init>":()V  
    #2 = Fieldref           #40.#409      //  java/lang/String.offset:I  
    #3 = Fieldref           #40.#410      //  java/lang/String.count:I  
    #4 = Fieldref           #40.#411      //  java/lang/String.value:[C  
    #5 = Methodref          #412.#413     //  java/util/Arrays.copyOfRange:([CII)[C  
    #6 = Methodref          #412.#414     //  java/util/Arrays.copyOf:([CI)[C  
    #7 = Class              #415          //  java/lang/StringIndexOutOfBoundsException  
    #8 = Methodref          #7.#416       //  java/lang/StringIndexOutOfBoundsException."<init>":(I)V  
    #9 = Integer            65536  
   #10 = Methodref          #417.#418     //  java/lang/Character.isSupplementaryCodePoint:(I)Z  
   #11 = Class              #419          //  java/lang/IllegalArgumentException  
   #12 = Methodref          #420.#421     //  java/lang/Integer.toString:(I)Ljava/lang/String;  
   #13 = Methodref          #11.#422      //  java/lang/IllegalArgumentException."<init>":(Ljava/lang/String;)V  
 

再对比图2看看,是不是正好对应上的?

图2里constantPool的第一个常量池项的内容是:

JVM_CONSTANT_Methodref: 26738818

这个26738818数字是怎么来的呢?
实际上是:26738818 = 408 << 16 | 130
而原本Class文件里常量池的第一项内容正是#130.#408,也就是由一个Class_index和一个NameAndType_index组成的Methodref。

图2里还有个细节,可以看到原本Class文件里常量池第7项是一个Class,但在图2里显示的是一个“UnresolvedClass”。这正是动态类加载/链接的一个表现。这个项所指向的Class还没被String里的方法使用过,所以还没跟String链接起来,所以这里看到是unresolved。
我们可以故意在那个groovysh里执行一句:

'abc'.charAt(5)  

这样会引发String.charAt()方法执行的过程中抛出一个java.lang.StringIndexOutOfBoundsException异常,那么就必须要完成链接的步骤。
然后再去看看String的常量池的样子:

就可以看到常量池的第7项已经解析(resolve)好了,从原本的符号引用变成了一个直接引用。

在JDK7以后的更新版中,HotSpot VM会逐渐去除PermGen,原本一些放在GC堆里的元数据会搬到GC管理之外的堆空间里。所以上面描述的实现会有些变化。具体会变成怎样还没真相。

至于其它JVM,其实运行时常量池想怎么组织都可以的,反正Java层面上看不出来JVM内部组织这些元数据的方式的差异。

原文地址: https://hllvm-group.iteye.com/group/topic/26412#post-187861

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,470评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,393评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,577评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,176评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,189评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,155评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,041评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,903评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,319评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,539评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,703评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,417评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,013评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,664评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,818评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,711评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,601评论 2 353

推荐阅读更多精彩内容