JVM瞎琢磨之內存模型及簡單調優思路記錄

JVM:java虛擬機,是每個程序員又愛又恨的存在,因爲有了它,我們的程序纔可以跨平臺,但是又因爲它的艱澀難懂,讓很多人望而卻步。
JVM的內存模型有點像咱們國家的土地,由國家統一規劃分配,有的是公共用地作爲公共場所供所有線程訪問,有的是住宅用地線程私有,每個線程分配一小塊用於自己的邏輯及數據處理。
JVM的內存可以分爲幾大塊:堆,方法區,棧(虛擬機棧),本地方法棧,程序計數器。
棧,本地方法棧,程序計數器爲線程私有,
堆和方法區是所有線程共享,
每創建一個線程,JVM會從棧內存中劃出一小塊內存給該線程,用於存儲線程自己的一些局部變量,方法出口等和運算邏輯、執行狀態等相關內容,棧的深度和棧幀的大小相關,相同棧內存下,棧幀越大,棧的深度就越小(棧幀的大小與局部變量和參數的的多少正相關)。
線程中的每個方法,會獲得一個棧幀,棧幀是該線程私有棧內存的一小塊空間,存儲局部變量表,操作數棧,動態鏈接和方法出口,按照該線程中方法的運行順序進行壓棧,(棧是先進後出的數據結構)如果遞歸過深,就有可能造成StackOverflowError。
由於線程的運行是採用時間片機制,即一個時刻只能有一個線程佔用cpu的一個核,所以線程會進行上下文的切換,這個時候就需要程序計數器對當前線程的執行位置進行記錄,方便下次喚醒線程的時候繼續執行,所以程序計數器也是線程私有的,確切的說程序計數器根本就不在 RAM上,而是直接劃分在CPU上,非常小的一塊內存區域,如果執行的時native本地方法,則程序計數器的值爲空,此區域時唯一沒有任何OutOfMemory的區域。
本地方法棧是爲native方法服務的棧內存,運行機制與虛擬機棧的運行機制一致,用於存儲每個native方法的執行狀態。
堆:堆是所有線程共享的,此內存區域在虛擬機啓動時創建,此區域唯一目的就是存放對象實例,而棧存的時對象的引用(指針),棧中同時存儲運行邏輯,當調用到某一個變量時通過引用(指針)去堆中找到對應的對象值,是gc的重點區域。在gc角度會將內存區域分爲老年代和新生代,其中老年代大約佔堆內存的2/3,新生代大約佔堆內存的1/3。
新生代又被劃分爲eden空間(大約佔新生代的4/5)和survivor0(約佔新生代的1/10),survivor1(約佔新生代的1/10)空間。新生代會經常進行minor gc,即eden滿了,將gc roots引用鏈上的所有內容複製到s0空間,所有gc roots引用鏈上的對象頭中的分代年齡+1,其他不在引用鏈上的內容直接清掉,如果eden又滿了,則將eden與s0再次進行minor gc,將gc roots引用鏈上所有數據移到s1區,所有gc roots引用鏈上的對象頭中的分代年齡+1,清空eden和s0,相當於s0和s1總有一塊是空的,用於存儲那些不能被垃圾回收的對象,如果對象頭中的分代年齡達到15,將會被送入老年代,當老年代也被佔滿,則會發生full gc,而發生full gc的時候會進行stw(stop the word),導致程序會在stw的時候沒有響應影響使用體驗,所以如何確定堆內存,e空間大小和s空間大小是避免發生頻繁full gc調優程序的一個思考方向(一半可根據每秒數據量估算出一個大小,再根據對象使用時間來處理,這裏不做展開)。
如果full gc還是無法釋放足夠的內存,則系統就會拋出OutOfMemory異常。

–本文原創,如轉載請標明出處,謝謝

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