[轉(zhuǎn)載]Class文件在JVM中如何存儲

JDK6 HotSpot VM用instanceKlass來記錄類的元數(shù)據(jù),每個Java類有一個對應(yīng)的instanceKlass橘券。
每個instanceKlass上引用著一個constantPoolOopDesc對象沧卢,然后間接引用著一個constantPoolCacheOopDesc對象。前者跟Class文件里記錄的常量池的結(jié)構(gòu)類似像吻,而后者是為了讓解釋器運行得更高效的一個緩存堕汞。

舉例的話勺爱,用VisualVM里的SA Plugin來演示,java.lang.String的狀況讯检。
這里我用JDK 7的一個預(yù)覽版琐鲁,build 96來運行VisualVM 1.3和一個groovysh,并且用VisualVM里的SA Plugin來觀察groovysh的運行狀態(tài):

圖1:java.lang.String對應(yīng)的一個instanceKlass


留意到instanceKlass里有個_constants字段人灼,引用著一個constantPoolOopDesc對象(后面簡稱constantPool對象)围段。

圖2:觀察constantPool對象的內(nèi)容:


留意到它是一個類似數(shù)組的對象,里面有_length字段描述常量池內(nèi)容的個數(shù)投放,后面就是常量池項了奈泪。
各個類型的常量是混在一起放在常量池里的,跟Class文件里的基本上一樣灸芳。
最不同的是在這個運行時常量池里涝桅,symbol是在類之間共享的;而在Class文件的常量池里每個Class文件都有自己的一份symbol內(nèi)容烙样,沒共享冯遂。

圖3:觀察constantPool里其中一個Utf8常量的內(nèi)容:


這張圖的關(guān)注點是位于0x180188a8的一個symbol對象(內(nèi)容是"intern"),它的結(jié)構(gòu)跟數(shù)組類似谒获,有_length來記錄長度蛤肌,后面是UTF-8編碼的字節(jié)壁却。

這些Utf8常量在HotSpot VM里以symbolOopDesc對象(下面簡稱symbol對象)來表現(xiàn);它們可以通過一個全局的SymbolTable對象找到裸准。注意:constantPool對象并不“包含”這些symbol對象展东,而只是引用著它們而已;或者說炒俱,constantPool對象只存了對symbol對象的引用琅锻,而沒有存它們的內(nèi)容。

讓我們來看看原本的Class文件里內(nèi)容是怎樣的:

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看看向胡,是不是正好對應(yīng)上的?

圖2里constantPool的第一個常量池項的內(nèi)容是:

JVM_CONSTANT_Methodref: 26738818

這個26738818數(shù)字是怎么來的呢惊完?
實際上是:26738818 = 408 << 16 | 130
而原本Class文件里常量池的第一項內(nèi)容正是#130.#408僵芹,也就是由一個Class_index和一個NameAndType_index組成的Methodref。

圖2里還有個細(xì)節(jié)小槐,可以看到原本Class文件里常量池第7項是一個Class拇派,但在圖2里顯示的是一個“UnresolvedClass”。這正是動態(tài)類加載/鏈接的一個表現(xiàn)凿跳。這個項所指向的Class還沒被String里的方法使用過件豌,所以還沒跟String鏈接起來,所以這里看到是unresolved控嗜。
我們可以故意在那個groovysh里執(zhí)行一句:

'abc'.charAt(5)  

這樣會引發(fā)String.charAt()方法執(zhí)行的過程中拋出一個java.lang.StringIndexOutOfBoundsException異常茧彤,那么就必須要完成鏈接的步驟。
然后再去看看String的常量池的樣子:

就可以看到常量池的第7項已經(jīng)解析(resolve)好了疆栏,從原本的符號引用變成了一個直接引用曾掂。

在JDK7以后的更新版中,HotSpot VM會逐漸去除PermGen壁顶,原本一些放在GC堆里的元數(shù)據(jù)會搬到GC管理之外的堆空間里珠洗。所以上面描述的實現(xiàn)會有些變化。具體會變成怎樣還沒真相若专。

至于其它JVM许蓖,其實運行時常量池想怎么組織都可以的,反正Java層面上看不出來JVM內(nèi)部組織這些元數(shù)據(jù)的方式的差異调衰。

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末膊爪,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子窖式,更是在濱河造成了極大的恐慌蚁飒,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,525評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件萝喘,死亡現(xiàn)場離奇詭異淮逻,居然都是意外死亡琼懊,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,203評論 3 395
  • 文/潘曉璐 我一進(jìn)店門爬早,熙熙樓的掌柜王于貴愁眉苦臉地迎上來哼丈,“玉大人,你說我怎么就攤上這事筛严∽淼” “怎么了?”我有些...
    開封第一講書人閱讀 164,862評論 0 354
  • 文/不壞的土叔 我叫張陵桨啃,是天一觀的道長车胡。 經(jīng)常有香客問我,道長照瘾,這世上最難降的妖魔是什么匈棘? 我笑而不...
    開封第一講書人閱讀 58,728評論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮析命,結(jié)果婚禮上主卫,老公的妹妹穿的比我還像新娘。我一直安慰自己鹃愤,他們只是感情好簇搅,可當(dāng)我...
    茶點故事閱讀 67,743評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著软吐,像睡著了一般瘩将。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上关噪,一...
    開封第一講書人閱讀 51,590評論 1 305
  • 那天鸟蟹,我揣著相機與錄音,去河邊找鬼使兔。 笑死建钥,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的虐沥。 我是一名探鬼主播熊经,決...
    沈念sama閱讀 40,330評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼欲险!你這毒婦竟也來了镐依?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,244評論 0 276
  • 序言:老撾萬榮一對情侶失蹤天试,失蹤者是張志新(化名)和其女友劉穎槐壳,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體喜每,經(jīng)...
    沈念sama閱讀 45,693評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡务唐,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,885評論 3 336
  • 正文 我和宋清朗相戀三年雳攘,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片枫笛。...
    茶點故事閱讀 40,001評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡吨灭,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出刑巧,到底是詐尸還是另有隱情喧兄,我是刑警寧澤,帶...
    沈念sama閱讀 35,723評論 5 346
  • 正文 年R本政府宣布啊楚,位于F島的核電站吠冤,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏恭理。R本人自食惡果不足惜咨演,卻給世界環(huán)境...
    茶點故事閱讀 41,343評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望蚯斯。 院中可真熱鬧,春花似錦饵较、人聲如沸拍嵌。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,919評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽横辆。三九已至,卻和暖如春茄猫,著一層夾襖步出監(jiān)牢的瞬間狈蚤,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,042評論 1 270
  • 我被黑心中介騙來泰國打工划纽, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留脆侮,地道東北人。 一個月前我還...
    沈念sama閱讀 48,191評論 3 370
  • 正文 我出身青樓勇劣,卻偏偏與公主長得像靖避,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子比默,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,955評論 2 355

推薦閱讀更多精彩內(nèi)容