jvm學習筆記1:JVM內存數據區域介紹


1:程序計數器

        字節碼的行號指示器,字節碼解釋器通過改變計數器的值來選取下一條需要執行的字節碼指令,計數器是線程私有內存,多線程情況下。當虛擬機將一條線程切換到另一條線程時通過該線程內部的計數器來恢復該線程原來的執行位置
        如果線程執行的是一個java方法,則計數器值是正在執行的字節碼指令地址,如果正在執行的是個natvie方法,則計數器值爲null
        還有該內存區域是唯一一個在java虛擬機規範中沒有規定任何OutOfMemoryError情況的區域,也就是說該內存區域在任何情況下都不會出現OutOfMemoryError

2:虛擬機棧,本地方法棧

        虛擬機棧也是線程私有的,它的生命週期跟線程相同
        虛擬機棧是java方法的內存模型 : 每個方法被執行的時候會創建一個棧幀(stack frame),用於存儲局部變量表(1),操作棧,動態鏈接,方法出口,每個方法被調用直至執行完成的過程對應着一個棧幀在虛擬機棧中入棧出棧的過程
        本地方法棧跟虛擬機棧基本類似,區別是本地方法棧是爲虛擬機使用的natvie方法服務,而虛擬機棧爲虛擬機使用的java方法服務
        可以通過-Xss來調節棧的內存容量

3:java堆

        java堆內存被所有線程共享,所有對象跟數組都在這裏分配內存,這裏也是垃圾蒐集器管理的主要區域,
        從內存回收角度來看:由於收集器都採用分代收集算法回收內存垃圾,所以java堆還可以細分爲:新生代,老年代;再細緻一點的有Eden空間,from survivor空間,to survivor空間等。
        從內存分配角度來看:線程共享的java堆可能劃分出多個線程私有的內存緩衝區(Thread local allocation buffer,TLAB)
        可以通過配置啓動參數 -Xms(最小堆內存) -Xmx(最大堆內存)來控制堆內存容量是固定的還是可擴展的,如果java堆中沒有足夠的內存分配對象實例,並且也無法擴展時,將會拋出OutOfMomeryError

4:方法區

        方法區跟java堆一樣被所有線程共享,它用於存儲已被虛擬機加載的類信息,常量,靜態變量以及即時編譯器編譯後的代碼等數據
        方法區也被成爲永久代,因爲垃圾蒐集行爲在這個區域很少出現,但並不意味着數據進入了方法區就如永久代的名字一樣永久存在了,這個區域還是會被內存回收的,主要目標針對是常量池的回收跟類型的卸載
       可以通過配置啓動參數-XX:permSize(最小持久代內存)-XX:maxPermSiz(最大持久代內存)來控制方法區容量是固定的還是可擴展的。當方法區無法滿足內存分配的需求時,將會拋出OutOfMomeryError

5:運行常量池

        運行常量池是方法區的一部分,class文件除了有類的版本,字段,接口,方法等描述外還有一項常量池,用於存儲編譯期生成的各種字面量跟符號引用。常量池具有動態性,可以在運行期將新的常量放入常量池中。

6:對象內存佈局

   對象佈局 :在HotSpot虛擬機中,對象在內存中的存儲佈局分爲3塊,對象頭,實例數據,對齊填充,
            對象頭分爲兩部分,第一部用於保存對象運行時數據,例如hash碼 ,GC分代年齡,鎖狀態標識,線程持有的鎖。第二部分存儲對象類型指針,該指針指向對象的元數據,虛擬機通過該指針確定對象屬於哪個類的實例。 如果對象是數組那麼在對象頭中還必須有一塊用於記錄數組長度的數據,因爲虛擬機在對象的元數據中可以確定對象的大小,但是在數組中通過元數據卻無法確定數組的大小
實例數據,保存對象所有字段的數據,包括從父類中繼承的還是從子類中定義的
對齊填充,該部分不是必然存在的,沒什麼含義。僅僅是起到佔位符的作用,因爲HotSpot VM的自動內存管理系統要求對象的大小必須是8字節的整數倍,而對象頭部分正好是8字節的倍數,所以當對象實例數據部分不是8字節的倍數時,就需要通過對齊填充來補全,已達自動內存管理系統要求的對象大小爲8字節的整數倍的條件。

  對象訪問定位:
        object obj = new object() ,
        假如該語句出現在方法體中,那麼obj將會在java棧的本地變量表中作爲一個reference類型出現,new object 將對在java堆中形成一塊存儲了object類型所有實例數據值(對象所有字段的數據)的結構內存,另外在java堆中還必須包含能查到該對象類型數據(對象類型,實現接口,父類,方法等)的地址信息,這些類型數據存儲在方法區中
        因爲reference類型只是一個指向對象的引用,並沒有規定這個引用通過那種方式去定位以及訪問到java堆中對象的具體位置,因此有不同方式去實現對象的訪問,主流的方式方式有兩種:句柄訪問跟直接指針
        句柄訪問方式:java堆會劃出一份內存作爲句柄池,reference存儲的是句柄池中對象句柄的地址,而句柄包含着對象實例數據與類型數據各自的具體地址
        
        直接指針 : reference直接保存對象在java堆中的地址
        
        這兩種訪問方式都各有優勢,句柄方式最大的好處是reference類型中儲存着穩定的句柄地址,當對象被移動時,只需要改變句柄中實例數據的地址就行了,reference本身不需要改變,
        直接指針訪問方式最大的好處就是速度更快,它節省了一次定位的時間開銷,由於對象訪問非常頻繁,所以這種方式的性能會更好,java默認的HotSpot使用的就是這種方式。
(1) 局部變量表:局部變量表的大小在編譯期間完成分配,它存儲着基本數據類型(int,char,short,long,float,double,boolean,byte),對象引用(refarence類型 不是對象本身,根據不同虛擬機實現,可能是指向對象起始化地址的指針,也可能是一個代表對象本身的句柄或者與對象相關的位置),returnAddress類型(該類型指向一條字節碼的地址)
    
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章