java內(nèi)存區(qū)域劃分
Java與C++之間有一堵由內(nèi)存動態(tài)分配和垃圾收集技術(shù)所圍成的“高墻”蕊连,墻外面的人想進去扒最,墻里面的人卻想出來。
java虛擬機在執(zhí)行java程序的過程中篡诽,會把它管理的內(nèi)存劃分為若干個不同的數(shù)據(jù)區(qū)域路星,如下圖所示
- 程序計數(shù)器
程序計數(shù)器是一塊較小的內(nèi)存空間,可以看作是當前線程所執(zhí)行的字節(jié)碼的信號指示器
每條線程都需要一個獨立的程序指示器色迂,是為了程序切換后能恢復到正確的位置香缺。
此內(nèi)存區(qū)域是唯一一個在java虛擬機規(guī)范中沒有規(guī)定任何OutOfMemoryError的區(qū)域
-
java虛擬機棧
Java虛擬機棧是線程私有的,它的生命周期與線程相同歇僧。虛擬機棧描述的是Java方法執(zhí)行的內(nèi)存模型:每個方法執(zhí)行的同時會創(chuàng)建一個棧幀图张,棧幀用于存儲局部變量表锋拖、操作數(shù)棧、動態(tài)鏈接祸轮、方法出口等信息兽埃。每個方法從調(diào)用直至執(zhí)行完成的過程,就對應著一個棧幀在虛擬機棧中入棧到出棧的過程适袜。
局部變量表存放了編譯期間可知的各種基本數(shù)據(jù)類型柄错,對象引用和returnAddress類型
局部變量表所需的內(nèi)存空間在編譯期間完成分配,在方法運行期間不會改變局部變量表的大小 本地方法棧
本地方法棧為虛擬機使用的Nativie方法服務java堆
java堆是被所有線程共享的內(nèi)存區(qū)域苦酱,在虛擬機啟動的時候創(chuàng)建售貌,此內(nèi)存區(qū)域的唯一目的就是存放對象的實例
java堆是垃圾收集器管理的主要區(qū)域方法區(qū)
方法區(qū)也是被所有線程共享的內(nèi)存區(qū)域,用于存儲已被虛擬機加載的類信息疫萤、常量颂跨、靜態(tài)變量、即時編譯器編譯后的代碼等數(shù)據(jù)
這區(qū)域的內(nèi)存回收目標主要是針對常量池的回收和類型的卸載扯饶,一般而言恒削,這個區(qū)域的內(nèi)存回收比較難以令人滿意,尤其是類型的回收尾序,條件相當苛刻钓丰,但是這部分區(qū)域的內(nèi)存回收確實是必要的。運行時常量池
運行時常量池是方法區(qū)的一部分每币,用于存放編譯期生成的各種字面量和符號引用
程序計數(shù)器携丁、虛擬機棧、本地方法棧3個區(qū)域隨線程而生兰怠、隨線程而滅
棧中的棧幀隨著方法的進入和退出而有條不紊地執(zhí)行著入棧和出棧操作则北。每一個棧楨中分配多少內(nèi)存是在類結(jié)構(gòu)已經(jīng)確定下來就已知的,這幾個區(qū)域的內(nèi)存分配和回收多具備確定性痕慢,這幾個區(qū)域不需要過多考慮回收的問題尚揣,方法結(jié)束或者線程結(jié)束時。內(nèi)存就跟著自動回收了掖举。
java堆和方法區(qū)快骗,只有在程序運行期間才知道會創(chuàng)建哪些對象,內(nèi)存的分配和回收都是動態(tài)的塔次。
JVM在進行垃圾回收前方篮,需要判斷哪些對象是需要回收的
- 引用計數(shù)算法
給對象添加一個引用計數(shù)器,被引用時計數(shù)器值+1励负,引用失效計數(shù)器值-1藕溅,當計數(shù)器值為0時對象不可能再被使用;
主流Java虛擬機未選用該算法管理內(nèi)存(未解決對象之間相互循環(huán)引用的問題)
實現(xiàn)簡單继榆,判斷效率高(應用:FlashPlayer巾表,Python等)
- 可達性分析算法
將"GC Roots"對象作為起始節(jié)點汁掠,向下搜索,搜索走過的路徑為引用鏈集币;當一個對象到GC Roots沒有引用鏈時考阱,則該對象是不可用的;
可作為"GC Roots"的對象:
1.方法區(qū)中靜態(tài)屬性引用的對象
2.方法區(qū)中常量引用的對象
3.虛擬機棧引用的對象 (棧幀中本地變量表)
4.本地方法棧中JNI引用的對象 (Native方法)
GC算法
- 標記 - 清除算法
</figure>
標記-清除算法采用從根集合進行掃描鞠苟,對存活的對象進行標記乞榨,標記完畢后,再掃描整個空間中未被標記的對象進行直接回收当娱,如上圖吃既。
標記-清除算法不需要進行對象的移動,并且僅對不存活的對象進行處理跨细,在存活的對象比較多的情況下極為高效态秧,但由于標記-清除算法直接回收不存活的對象,并沒有對還存活的對象進行整理扼鞋,因此會導致內(nèi)存碎片。
- 復制算法
復制算法將內(nèi)存劃分為兩個區(qū)間愤诱,使用此算法時云头,所有動態(tài)分配的對象都只能分配在其中一個區(qū)間(活動區(qū)間),而另外一個區(qū)間(空間區(qū)間)則是空閑的淫半。
復制算法采用從根集合掃描溃槐,將存活的對象復制到空閑區(qū)間,當掃描完畢活動區(qū)間后科吭,會的將活動區(qū)間一次性全部回收昏滴。此時原本的空閑區(qū)間變成了活動區(qū)間。下次GC時候又會重復剛才的操作对人,以此循環(huán)谣殊。
復制算法在存活對象比較少的時候,極為高效牺弄,但是帶來的成本是犧牲一半的內(nèi)存空間用于進行對象的移動姻几。所以復制算法的使用場景,必須是對象的存活率非常低才行势告,而且最重要的是蛇捌,我們需要克服50%內(nèi)存的浪費。
- 標記 - 整理算法
標記-整理算法采用 標記-清除 算法一樣的方式進行對象的標記咱台、清除络拌,但在回收不存活的對象占用的空間后,會將所有存活的對象往左端空閑空間移動回溺,并更新對應的指針春贸。標記-整理 算法是在標記-清除 算法之上混萝,又進行了對象的移動排序整理,因此成本更高祥诽,但卻解決了內(nèi)存碎片的問題譬圣。
JVM為了優(yōu)化內(nèi)存的回收,使用了分代回收的方式雄坪,對于新生代內(nèi)存的回收(Minor GC)主要采用復制算法厘熟。而對于老年代的回收(Major GC),大多采用標記-整理算法