深刻理解java虛擬機第二版(二)java內存區域與內存溢出異常

JVM運行時數據區域;HostSpot虛擬機對象建立(java堆給對象分配內存的方式,解決對象內存分配的線程安全方案,對象內存佈局,對象的訪問定位,異常實例);java

1.JVM運行時數據區域:

  • 程序計數器(Program Counter)
  • Java虛擬機棧(Java Virtual Machine Stack)
  • 堆(Heap)
  • 方法區(Method Area)
  • 本地方法棧(Native Method Stack)
  • 程序計數器(Program Counter)
  • 運行時常量池

Java虛擬機在執行Java程序的過程當中會把它管理的內存劃分爲若干個不一樣的數據區域。這些區域都有各自的用途,根據Java虛擬機規範的規定,虛擬機所管理內存會被分爲以下圖所示的幾個區域:程序員

 

2.HostSpot虛擬機對象建立

​ 虛擬機遇到一條new指令時,首先將去檢查這個指令的參數是否能在常量池中定位到一個類的符號引用,而且檢查這個符號引用表明的類是否已經被加載、解析和初始化過。若是沒有,那必須先執行相應的類加載過程。在類加載檢查經過後,接下來虛擬機將爲新生對象分配內存。對象所需內存的大小在類加載完成後,即可以徹底肯定,爲對象分配空間的任務等同於把一塊肯定大小的內存從java堆中劃分出來。內存分配完成後,虛擬機將要分配到的內存空間都初始化爲零值(不包括對象頭),若是使用TLAb,這一工做過程也能夠提早至TLAB分配時進行。這一步操做,保證了對象的實例字段在java代碼中能夠不賦是初始值就直接使用,程序能訪問到這些字段的數據類型所對應的零值。最後在執行new指令後,會接着執行init方法,把對象按照程序員的意願進行初始化,這樣一個真正可用的對象纔算徹底產生出來。安全

2.1 java堆給對象分配內存的方式:

  • 「指針碰撞(Bump the Pointer)」:假設java中的內存是絕對規整的,全部用過的內存都放在一邊,空閒的內存放在另外一邊,中間放着一個指針做爲分界點的指示器,那全部分配內存就僅僅是把那個指針向空閒空間那邊挪動一段與對象大小相等的距離
  • 「空閒列表(Free List)」:若是java堆中的內存並非規整的,已經使用的內存和空閒的內存相互交錯,那就沒有辦法進行簡單的指針碰撞了,虛擬機就必須維護一個列表,記錄上哪些內存塊是能夠用的,在分配的時候,從列表中找到一塊足夠大的空間劃分對象實例,並更新列表上的記錄

2.2 解決對象內存分配的線程安全方案:

  • 一種是對分配內存空間的動做進行同步處理—實際上虛擬機採用CAS配上失敗重試的方式保證更新操做的原子性;
  • 另外一種是把內存分配的動做按照線程劃分在不一樣的空間中進行,即每一個線程在java堆中預先分配一小塊內存,稱爲本地線程分配緩衝區(Thread Loacl Allocation Buffer,TLAB)。

ps:虛擬機是否使用TLAb,能夠經過–XX:+/UseTLAB參數來設定網絡

2.3 對象內存佈局:

  • 對象頭(Object Header)
  • 實例數據(Instance Data)
  • 對齊填充(Padding)

2.4 對象的訪問定位:

句柄的方式:

說明:若是使用句柄訪問的話,那麼Java堆中將會劃分出一起內存做爲句柄池,reference中存儲的就是對象的句柄
           地址,而句柄中包含了對象實例數據與類型數據各自的具體的地址信息。佈局

 

直接指針的方式:

說明:若是使用直接指針訪問,那麼Java堆對象的佈局中就必須考慮如何放置訪問類型數據的具體信息,而reference中
           存儲的直接就是對象地址。學習

句方式柄與直接指針方式的對比: 

使用句柄來訪問的最大好處就是reference中存儲的是穩定的句柄地址,在對象被移動(垃圾收集時移動對象是很是廣泛的行爲)時只會改變句柄中的實例數據指針,而reference自己不須要修改;使用直接指針訪問方式的最大好處就是速度更快,它節省了一次指針定位的時間開銷,因爲對象的訪問在Java中很是頻繁,所以這類開銷聚沙成塔後也是一項很是可觀的執行成本。spa

注:就HotSpot而言,它是使用的直接指針的方式進行對象訪問的。線程

2.5 異常實例

java.lang.OutOfMemoryError指針

java.lang.StackOverflowError對象

聲明:本文是我的學習筆記,內容來自《深刻理解Java虛擬機·JVM高級特性與最佳實踐》周志明 與網絡文章

相關文章
相關標籤/搜索