Java虛擬機之自動內存管理機制

虛擬機主要是內存動態分配和垃圾收集技術。程序開發人員不需要對new 的對象寫垃圾回收,回收內存權利交給了虛擬機。如果不瞭解虛擬機出現內存泄漏和溢出方面的問題,該怎麼着手去排查呢。

程序計數器:當前線程所執行的字節碼的行號指示器,佔內存空間較小。字節碼解釋器工作時,通過改變計數器的值來選取下一條要執行的字節碼。依賴計數器的功能有:分支、循環、跳轉、異常處理、線程恢復等基礎功能。

多線程(多核處理器)情況下,一個內核在任意時刻點,只能執行一個線程,爲了線程切換能恢復到正確位置,每個線程都會有一個獨立的計數器,(程序計數器是不共享的),佔用的是線程的私有內存。調用java方法,計數器是記錄的是字節碼指令的地址,如果是native方法(本地方法),計數器就沒有用了,就是空。此內存區域是不會出現OOM的內存區域。

虛擬機棧:虛擬機棧也是線程私有的,生命週期與線程相同。虛擬機棧描述的是方法執行的內存模型。

方法在執行的時候會創建一個棧幀,用來存儲局部變量、操作數棧、動態鏈接、方法出口等信息。方法從執行直到執行完成之前都對應一個棧幀。方法執行過程就是虛擬機棧的棧幀入棧出棧的過程。

如果線程請求的棧深度大於虛擬機棧所允許的深度,將拋出stackoverflowError異常。虛擬棧自動擴展內存,如果無法申請到足夠內存,就會拋出oom異常。

本地方法棧:虛擬機執行本地方法提供服務。會拋出StackOverflowError和OutOfMemoryError異常。HotSpot虛擬機把虛擬機棧和本地方法棧合二爲一。JIT即時編譯原理:分層編譯(僅解釋執行、僅客戶端編譯、混合編譯)。這裏面有個熱點代碼探測然後把Java方法編譯成本地方法。一旦判定代碼段是熱點代碼,則解釋器將發送一次請求編譯器,進行編譯,在編譯成功之前 解釋器仍舊運行着。 等編譯完成後,直接將pc寄存器中方法的調用地址進行替換,替換爲編譯後的方法地址。這個方法就是 本地替換-OSR。

Java堆:虛擬機啓動時就會創建,被所有線程共享的一塊區域,保存對象的實例。所有的對象實例以及數組都要在堆上分配內存,但是隨着JIT技術的發展和逃逸分析技術進步,棧上分配,標量替換優化技術,使得這規範也不在那麼絕對了。Java堆是垃圾收集器管理的主要區域(有時候也成爲GC堆)。

從回收角度來分:新生代、老年代;再細緻劃分,Eden空間、from survivor空間、to survivor空間等。

從內存分配角度來分:多個線程私有的分配緩衝區。

方法區:各個線程共享的內存區域。存儲被虛擬機加載的類信息、常量、靜態變量、JIT編譯後的代碼等等。視爲堆的邏輯部分,也稱爲非堆。垃圾收集器主要針對常量池的回收和類型的卸載。運行常量池是方法區的一部分,用於存放編譯器生成的各種字面量和符號引用,類加載後進入方法區運行時存放在常量池。運行期間也可以把常量存放在常量池中,例如String類的intern()方法。受方法區內存大小的限制,不足時拋出oom異常。

對象在虛擬機Java堆內存中是如何創建?如何佈局?如何訪問?

通過new關鍵字創建一個對象。首先,常量池中能否找到這個類的符號引用,類是否已被加載、解析、初始化。如果沒有類加載則需要加載類信息。類加載檢查過後,虛擬機要爲新對象分配內存。把一塊確定大小的內存空間從java堆中劃分出來。

java堆內存是規整過的,使用過的內存在一端,未使用的在一端,中間依靠指針作爲分界點指示器。指針碰撞分配法

java堆內存是沒有規整過的,使用內存和未使用內存相互交錯,不連續,虛擬機維護一個列表來保存空閒的空間,分配的時候找一個足夠大的空間給它,這種是空閒列表法。

解決空間分配併發安全問題:分配內存空間同步處理,CAS加上失敗重試的方式保證原子性;另外一種是線程在堆中先分配一個緩存,哪個線程要分配內存就從對應的線程緩存空間裏分配,緩存空間用完時才重新分配,這是考慮同步鎖處理。

內存分配完成後,初始化分配到的空間爲零值,把分配的空間清空,填上零。這一步保證了對象的實例字段,在Java代碼中可以不初始化就直接使用。程序能訪問這些字段對應的零值。進一步對象設置(屬於哪一個類、類的元數據、對象的hashcode、對象GC分代、等)這些信息都在對象頭中。下一步是按照程序的邏輯初始化值。

 

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章