'深入理解Java虛擬機'讀書總結2018-04-23

以下內容都是我自己對本書讀后的一個理解加叁,感覺總結一下理解的更深刻哥力,如果能幫助到別人就更好了蔗怠,可能會有些內容說的不正確,如果發(fā)現(xiàn)了吩跋,希望幫忙留言指正寞射,謝謝。

背景

計算機的種類很多锌钮,硬件也都不太一樣桥温。比如Windows,Android梁丘,iOS等侵浸,而具體到某一個平臺,也分32位氛谜,64位等掏觉。想要編寫一個適用于眾多平臺的軟件比較困難,要考慮到各個平臺的兼容性值漫。Java虛擬機就提供了這么一個平臺澳腹,讓開發(fā)者只關心程序的功能開發(fā),兼容的工作就交給虛擬機即可。Java虛擬機做到了一次編譯酱塔,到處運行沥邻。

在JVM上跑一個程序的大概過程

基于JVM的語言除了Java還有Groovy,JRuby羊娃,Scala唐全,F(xiàn)antom,Jython等蕊玷。JVM支持的只有一種文件格式:.class文件邮利。基于JVM的語言集畅,不管語法是什么樣的近弟,脫去漂亮的衣服,最終都要編譯成.class文件格式挺智,才能在JVM虛擬機上運行祷愉。

.class文件結構

image.png

.class文件結構是固定這樣的,ux就代表x個字節(jié)的內容赦颇,.class文件結構中只有兩種數(shù)據(jù)內容:無符號數(shù)和表二鳄,表可以認為是一個數(shù)據(jù)結構,表中還是無符號數(shù)和表媒怯。

  • magic订讼,代表該文件是個class文件
  • mijor_version,major_version扇苞,版本號
  • constant_pool_count欺殿,常量池中常量個數(shù)
  • cp_info,常量池鳖敷,常量池中的常量個數(shù)是constant_pool_count-1個脖苏,0號常量被空了出來。
    常量池中類型:字面量和符號引用定踱。字面量是基礎類型棍潘,字符串,整型崖媚,浮點型這些亦歉。符號引用就是各字段(類的全局變量),方法的名稱及描述符畅哑,類和接口的全限定名肴楷,是以引用其他常量的方式存在,最終都會引用到字面量常量上去荠呐。
    常量池項目類型:


    image.png

    常量池常量項的結構列表:


    image.png

    舉個例子:
    //TestClass.java
package com.ejushang.TestClass;
 public class TestClass implements Super{
     private static final int staticVar = 0;
     private int instanceVar=0;
     public int instanceMethod(int param){
         return param+1;
     }
 }
 interface Super{ }

該Java文件編譯為class文件后:


class.png

我在"'深入理解Java虛擬機'2018-04-16"這個筆記中阶祭,手動將每一項內容都進行了分析绷杜,這里就直接用Javap -verbose TestClass輸出常量表


cmd.png

ConstantPool就是常量池,索引對應的是常量池的第幾個常量濒募。
  • access_flags,訪問標志圾结,表示是類還是接口瑰剃,public,abstract等等


    image.png
  • this_class筝野,super_class晌姚,interfaces_count,interfaces歇竟,這幾項確定了類的繼承關系
  • fields_count挥唠,fields,字段表焕议,包含屬性表


    field.png
  • methods_count宝磨,methods,方法表盅安,包含有屬性表


    method.png
  • attributes_count唤锉,attributes,屬性表


    attribute1.png

    attribute2.png

    attribute3.png

    Code屬性:


    code.png

    Java程序方法體中的代碼經(jīng)過編譯别瞭,轉換成了字節(jié)碼指令存儲在Code屬性中窿祥。Code屬性中max_stack代表操作數(shù)棧深度,max_locals代表方法中局部變量所占Slot之和蝙寨,Slot是虛擬機為局部變量分配內存使用的最小單位晒衩。
    code存儲的就是code_length個字節(jié)碼指令,字節(jié)碼指令表示你使用代碼要做的操作墙歪,比如new听系,+等等,字節(jié)碼指令可以跟隨參數(shù)箱亿,如果需要參數(shù)跛锌,字節(jié)碼后面跟隨的應該就是它的參數(shù)。

類加載

類從被加載到虛擬機內存中開始届惋,到卸載出內存為止髓帽,生命周期包括:加載,驗證脑豹,準備郑藏,解析,初始化瘩欺,使用和卸載必盖。


loa.png

什么情況下類被加載拌牲?由虛擬機來自由把握。

  • 加載歌粥,通過類的全限定名獲取到對應的clas文件塌忽,然后將類信息加載到JVM方法區(qū),再堆中實例化一個Class對象失驶,作為方法區(qū)中該類的入口
  • 驗證土居,確認類型符合Java語言的語義,并且不會危及JVM的完整性
  • 準備嬉探,為類變量(Static變量)分配內存擦耀,設置初值(通過內存清零實現(xiàn),此階段不執(zhí)行Java代碼)涩堤,final修飾的常量初始值就是Java代碼中的初始值
  • 解析眷蜓,在類的常量池中尋找類,接口胎围,方法和字段的符號引用吁系,將符號引用替換為直接引用
  • 初始化,為類變量賦予Java代碼中的初始值的過程痊远。初始化階段是執(zhí)行類構造器<clinit>()方法的過程垮抗,該方法是由編輯器自動收集類中的所有類變量的賦值動作和靜態(tài)語句塊中的語句合并產(chǎn)生的。收集順序由源文件中出現(xiàn)順序決定碧聪。在子類的<clinit>()方法執(zhí)行前父類的<clinit>()方法已經(jīng)執(zhí)行完畢冒版。因此虛擬機中第一個被執(zhí)行<clinit>()方法的類肯定是java.lang.Object。
    有且只有五種情況必須立即對類初始化
  1. 遇到new逞姿,getstatic辞嗡,putstatic,invokestatic這4條字節(jié)碼指令時
  2. 對一個類反射調用時
  3. 初始化一個類滞造,如果父類還沒初始化续室,則先初始化父類
  4. 用戶指定一個要執(zhí)行的主類(包含main()方法的那個類)
  5. 使用JDK1.7的動態(tài)語言支持時,如果java.lang.MethodHandler實例最后的解析結果REF_getStatic谒养,REF_putStatic挺狰,REF_invokeStatic的方法句柄,并且這個方法句柄所對應的類沒有進行過初始化买窟,需要先初始化

虛擬機字節(jié)碼執(zhí)行引擎

運行時棧幀結構丰泊,先了解下棧幀結構,為以后的運行代碼過程做鋪墊
虛擬機棧描述的是Java方法執(zhí)行的內存模型始绍。每個方法在執(zhí)行的同時都會創(chuàng)建一個棧幀瞳购,用于存儲局部變量表,操作數(shù)棧亏推,動態(tài)鏈接学赛,方法出口等信息年堆。


stack_frame.png
  • 局部變量表,用于存儲方法參數(shù)和方法內部的局部變量
  • 操作數(shù)棧盏浇,方法執(zhí)行過程中变丧,會有各種字節(jié)碼指令往操作數(shù)棧中寫入和提取內容,也就是出/入棧操作绢掰。我的理解锄贷,就是在內存中順序將指令寫在內存空間上,這個被寫指令的內存空間被叫做操作數(shù)棧曼月,這條指令寫完了,還有用就留著柔昼,繼續(xù)在下一個地址寫下一條指令哑芹,寫完如果不用了就清空,直到寫完最后一條指令捕透。不知道怎么畫個易懂的圖出來聪姿,如果有會畫出入棧這種圖的朋友,不吝賜教啊乙嘀。
  • 動態(tài)鏈接末购,每個棧幀都包含一個指向運行時常量池中該棧幀所屬方法的引用,應該就是方法區(qū)中類信息方法的地址吧虎谢。
  • 方法返回地址盟榴,遇到方法返回的字節(jié)碼指令時,退出當前方法婴噩,把當前棧幀出棧擎场。棧幀中保存有PC計數(shù)器值,根據(jù)該值几莽,恢復上層方法迅办,把返回值(如果有)壓入調用者棧幀的操作數(shù)棧中。調整PC計數(shù)器指向方法調用的后一條指令章蚣。

對象的創(chuàng)建站欺,內存布局和訪問

  • 對象的創(chuàng)建
    當虛擬機遇到一個new指令時,首先去檢查這個指令的參數(shù)是否能在常量池中定位到一個類的引用符號纤垂,并且檢查這個引用符號代表的類是否已經(jīng)被加載矾策,解析,初始化洒忧。如果沒有蝴韭,就先加載該類。接下來為新生對象分配內存熙侍。
  • 對象的內存布局
    對象在內存中的布局分為三塊區(qū)域榄鉴,對象頭履磨,實例數(shù)據(jù),對齊補充庆尘。
    對象頭剃诅,分為兩部分,第一部分用來存儲自身的運行時數(shù)據(jù)驶忌,如哈希碼矛辕,GC分代年齡,鎖狀態(tài)標志付魔,線程持有的鎖聊品,偏向線程ID,偏向時間戳等几苍。第二部分是類型指針翻屈,指向他的類元數(shù)據(jù)的指針。通過這個指針確定對象是哪個類的實例妻坝。
    實例數(shù)據(jù)伸眶,存儲的是字段內容,包括從父類繼承的和自己的刽宪。
    對齊填充厘贼,對象大小必須是8的整數(shù)倍,沒有對齊的圣拄,通過對齊填充補全嘴秸。

目前問題

  • 垃圾回收機制
    判斷對象是否已死
    引用計數(shù)法,對象中添加一個引用計數(shù)售担,每當有一個地方引用它趋惨,引用計數(shù)就加1仅胞。當引用失效時短条,就減1恭朗。計數(shù)器為0的對象就是不可能再被使用的。但是主流Java虛擬機沒有選用該方法來管理內存的哥攘。循環(huán)引用一個對象的話剖煌,該對象不會被回收。
    可達性分析算法逝淹,當一個引用和GC Roots沒有引用鏈相連耕姊,則證明此對象可回收。GC Roots包括栅葡,虛擬機棧中引用茉兰,方法區(qū)靜態(tài)屬性引用,方法區(qū)常量引用欣簇,本地方法棧引用规脸。
    引用
    存儲著另一塊內存的起始地址坯约。分為強引用,軟引用莫鸭,弱引用闹丐,虛引用。
    兩次標記
    對象在可達性分析后被因,發(fā)現(xiàn)沒有與GC Roots相連卿拴,它將會被第一次標記并進行篩選,當對象沒有覆蓋finalize()方法梨与,或者finalize已經(jīng)被虛擬機調用過堕花,則會被回收。如果finalize中將引用與GC Roots關聯(lián)了粥鞋,則不會被回收航徙。
    垃圾收集算法
    標記-清除算法,最基礎的算法陷虎,效率不高,容易產(chǎn)生大量不連續(xù)的內存碎片
    復制算法杠袱,將可用內存容量分為大小相等的兩塊尚猿,每次只用一塊,當一塊用完了楣富,就將還存活著的對象復制到另一塊上凿掂,然后將已使用的一次清理掉。現(xiàn)在的商業(yè)虛擬機都采用這種算法纹蝴,但并未按照1:1劃分內存空間庄萎,而是劃分為一塊較大的Eden和兩塊較小的Survivor,每次使用一塊Eden和其中一塊Survivor塘安。當回收時糠涛,將Eden和Survivor中還存活著的對象一次性復制到另一塊Survivor上,最后清理掉Eden和剛才用過的Survivor兼犯。Eden和Survivor的比例是8:1忍捡。
    標記整理算法分代收集算法
  • 類加載雙親委派模型


    loader.png
  • 內存溢出
    在Java虛擬機規(guī)范中切黔,虛擬機棧有兩種異常情況:如果線程請求的棧深度大于虛擬機所允許的棧深度砸脊,將拋出StackOverflowError異常;如果虛擬機可以動態(tài)擴展纬霞,如果擴展到無法申請到足夠的內存凌埂,就會拋出OutOfMemoryError異常。
    如果堆中沒有內存完成實例分配诗芜,并且堆也無法再擴展時瞳抓,將會拋出OutOfMemoryError異常埃疫。
    根據(jù)虛擬機規(guī)范的規(guī)定,當方法區(qū)無法滿足內存分配需求時挨下,將拋出OutOfMemoryError異常熔恢。

Java程序從編譯到運行完成的過程

public class A{
    int value;
    public String getValue(){
        return value;
    }
}

public class Example{
    public static void main(String args[]){
        A a = new A();
        a.getValue();
    }
}

1.Java代碼編譯為class文件,class中包含常量池臭笆,字段表叙淌,方法表,屬性表等愁铺;
2.加載(虛擬機自己定義加載時機)鹰霍,將class文件加載到方法區(qū),在堆中實例化一個Class對象茵乱,做為方法區(qū)中該類的入口茂洒;
驗證;準備瓶竭,為類變量分配內存督勺,設置初值0;
解析斤贰,將常量池中符號引用替換為直接引用智哀;
初始化,給類變量賦真正的初始值荧恍;
3.開始執(zhí)行main方法瓷叫,根據(jù)Example的全限定名在方法區(qū)中找到Example,找到name是main的方法送巡,創(chuàng)建main方法棧幀摹菠,將參數(shù)args,對象引用a存儲到main棧幀的局部變量表中骗爆,然后將new指令入棧(操作數(shù)棧)次氨,將參數(shù)A入棧,在堆中保存a對象實例摘投。接下來方法調用指令入棧糟需,參數(shù)入棧,參數(shù)中有a和getValue谷朝,可以根據(jù)這些內容找到方法區(qū)中A類getValue方法的地址洲押,然后運行該方法,創(chuàng)建getValue方法棧幀圆凰,將return指令入棧(getValue的操作數(shù)棧)杈帐,參數(shù)value入棧,該方法執(zhí)行結束,value出棧挑童,return指令出棧累铅。根據(jù)程序計數(shù)器,跳回剛才的main棧幀的操作數(shù)棧中的位置站叼。退出指令入棧娃兽,退出指令出棧,其他內容逐一出棧尽楔。OK投储,程序結束。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末阔馋,一起剝皮案震驚了整個濱河市玛荞,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌呕寝,老刑警劉巖勋眯,帶你破解...
    沈念sama閱讀 206,839評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異下梢,居然都是意外死亡客蹋,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評論 2 382
  • 文/潘曉璐 我一進店門孽江,熙熙樓的掌柜王于貴愁眉苦臉地迎上來嚼酝,“玉大人,你說我怎么就攤上這事竟坛。” “怎么了钧舌?”我有些...
    開封第一講書人閱讀 153,116評論 0 344
  • 文/不壞的土叔 我叫張陵担汤,是天一觀的道長。 經(jīng)常有香客問我洼冻,道長崭歧,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,371評論 1 279
  • 正文 為了忘掉前任撞牢,我火速辦了婚禮率碾,結果婚禮上,老公的妹妹穿的比我還像新娘屋彪。我一直安慰自己所宰,他們只是感情好,可當我...
    茶點故事閱讀 64,384評論 5 374
  • 文/花漫 我一把揭開白布畜挥。 她就那樣靜靜地躺著仔粥,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上躯泰,一...
    開封第一講書人閱讀 49,111評論 1 285
  • 那天谭羔,我揣著相機與錄音,去河邊找鬼麦向。 笑死瘟裸,一個胖子當著我的面吹牛,可吹牛的內容都是我干的诵竭。 我是一名探鬼主播话告,決...
    沈念sama閱讀 38,416評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼秀撇!你這毒婦竟也來了超棺?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,053評論 0 259
  • 序言:老撾萬榮一對情侶失蹤呵燕,失蹤者是張志新(化名)和其女友劉穎棠绘,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體再扭,經(jīng)...
    沈念sama閱讀 43,558評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡氧苍,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,007評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了泛范。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片让虐。...
    茶點故事閱讀 38,117評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖罢荡,靈堂內的尸體忽然破棺而出赡突,到底是詐尸還是另有隱情,我是刑警寧澤区赵,帶...
    沈念sama閱讀 33,756評論 4 324
  • 正文 年R本政府宣布惭缰,位于F島的核電站,受9級特大地震影響笼才,放射性物質發(fā)生泄漏漱受。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,324評論 3 307
  • 文/蒙蒙 一骡送、第九天 我趴在偏房一處隱蔽的房頂上張望昂羡。 院中可真熱鬧,春花似錦摔踱、人聲如沸虐先。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽赴穗。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間般眉,已是汗流浹背了赵。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留甸赃,地道東北人柿汛。 一個月前我還...
    沈念sama閱讀 45,578評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像埠对,于是被迫代替她去往敵國和親络断。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,877評論 2 345

推薦閱讀更多精彩內容

  • 一项玛、運行時數(shù)據(jù)區(qū)域 Java虛擬機管理的內存包括幾個運行時數(shù)據(jù)內存:方法區(qū)貌笨、虛擬機棧、本地方法棧襟沮、堆锥惋、程序計數(shù)器,...
    kennethan閱讀 2,125評論 1 91
  • 一开伏、運行時數(shù)據(jù)區(qū)域 Java虛擬機管理的內存包括幾個運行時數(shù)據(jù)內存:方法區(qū)膀跌、虛擬機棧、本地方法棧固灵、堆捅伤、程序計數(shù)器,...
    加油小杜閱讀 1,511評論 1 15
  • 《深入理解Java虛擬機》筆記_第一遍 先取看完這本書(JVM)后必須掌握的部分巫玻。 第一部分 走近 Java 從傳...
    xiaogmail閱讀 5,062評論 1 34
  • Java 虛擬機屏蔽了與具體操作系統(tǒng)平臺相關的信息,使得 Java 語言編譯程序只需生成在 Java 虛擬機上運行...
    尋夢的尕柳閱讀 859評論 0 11
  • 2015年丛忆,覺得自己要累死了。 精力不濟仍秤,長期的加班熄诡,令人心情抑郁。 可能是因為缺乏鍛煉又沒錢徒扶,導致心里總是沒有安...
    小益達閱讀 152評論 0 1