java 全局变量 内存不回收_JAVA知识梳理:内存管理与垃圾回收机制
相對于C,C++來說,java程序員最幸運的事就是不用進行內(nèi)存控制,很少會出現(xiàn)內(nèi)存溢出的異常。但是這也不是絕對的,當(dāng)出現(xiàn)oom的時候,如果不了解虛擬機是如何使用內(nèi)存的,排查錯誤將會成為一項艱難的任務(wù)。
首先給大家介紹一下JVM運行時的數(shù)據(jù)區(qū)。入門的java程序員都接觸過一個概念,JVM中存在堆和棧兩塊數(shù)據(jù)存儲區(qū)域。因為棧是一種先進后出的存儲結(jié)構(gòu),所以很適合存儲程序運行的邏輯順序。比如在方法1中調(diào)用方法2,此時棧頂元素是方法2,棧底元素是方法1。所以程序運行的時候先運行方法2,然后方法2出棧,再運行方法1,方法1出棧。這就是棧的用法。堆是一個很大的區(qū)域。在堆中存儲的是程序運行的各種數(shù)據(jù),包括對象的實例,常量等等。這是一種粗粒度的劃分,但是很好理解。
如圖,這是《Java虛擬機規(guī)范(Java SE7 版)》的規(guī)定。方法區(qū)和堆就是上述堆的概念,方法區(qū)是堆的一個邏輯分區(qū),有個別稱叫非堆(Non-Heap)。目的是與真正的堆區(qū)分出來。
方法區(qū)中存放的是虛擬機加載的類信息,常量,靜態(tài)變量等。
堆的唯一目的是存放對象實例。 堆是垃圾收集管理的主要區(qū)域,也稱為GC堆。
官方的虛擬機是Hotspot,采用分代收集算法進行垃圾收集(下文有描述)。所以堆可以再進行細分為:新生代和老年代。再將新生代細分一點有:Eden空間、From Survivor空間、To Survivor空間。這兩個區(qū)域是所有線程共享的。
在“棧區(qū)”主要存在三個數(shù)據(jù)區(qū):虛擬機棧、本地方法棧、程序計數(shù)器。
程序計數(shù)器是一個很小很小的空間。可以看成當(dāng)前線程程序執(zhí)行字節(jié)碼的行號指示器。此區(qū)域是java虛擬機規(guī)范中唯一沒有規(guī)定任何OutOfMemoryError情況的區(qū)域。
虛擬機棧和本地方法棧的作用很相似。用于存儲局部變量、操作數(shù)棧、動態(tài)鏈接、方法出口等。他們兩個的區(qū)別是虛擬機棧是為執(zhí)行java方法服務(wù),本地棧是為Native方法( 簡單地講,一個Native Method就是一個java調(diào)用非java代碼的接口)服務(wù)。
有了上面的基本概念。下面談?wù)刯ava垃圾收集。在java“棧”中時刻進行著進棧和出棧,棧隨線程而生,隨線程而滅,內(nèi)存自然回收了。所以GC主要發(fā)生在“堆”中。
在堆中占空間最大的就是對象實例了。所以解決回收問題的第一步就是判斷對象是否已死。主要有兩種算法。
第一:引用計數(shù)算法。給對象一個應(yīng)用計數(shù),引用一次“+1”,失效時“-1”。當(dāng)引用為0時進行回收。但是這樣無法解決相互應(yīng)用的問題。主流的虛擬機沒有采用這種方式。
第二種:可達性分析算法。如下圖,通過GC Root可以遍歷到的Object節(jié)點即為可達,剩下的節(jié)點將會被回收。(雖然object6和object7存在引用,但是也會被回收)
找到了死掉的對象,下一步就是回收了。回收算法這里介紹四種:標(biāo)記-清除算法、復(fù)制算法、標(biāo)記-整理算法、分代收集算法。
標(biāo)記-清除:如圖將已死對象(灰色)進行標(biāo)記,回收直接將內(nèi)存回收掉,這是一種最基本的算法。后續(xù)的其他算法都是根據(jù)這種思路來的。這種方式明顯存在不足。1、效率問題,標(biāo)記和清除的效率都不高。2、清除后內(nèi)存空間很零散。如果出現(xiàn)“大對象”則不容易找到一個合適的位置存儲。
復(fù)制算法:將內(nèi)存空間劃分為兩個相等的區(qū)域,當(dāng)一個區(qū)域空間用完之后將存活的對象復(fù)制到另外一個區(qū)域,然后回收整個區(qū)域。這個算法的優(yōu)點是高效,缺點是總是有一半的空間被浪費掉。
標(biāo)記-整理法:和標(biāo)記-清除相似,只是將存活對象向一端進行移動。
分代收集算法:這其實只是對前面幾種算法的綜合應(yīng)用。了解這種算法首先得更進一步了解java堆。上面提到j(luò)ava堆新生代細分為:Eden空間、From Survivor空間、To Survivor空間。他們之間的內(nèi)存大小為:8:1:1。這其實是為了更好的適應(yīng)復(fù)制算法。IBM研究表明:新生代中的對象98%都是“朝生夕死”的。即運行一次復(fù)制算法得到的存活對象是很少的,所以復(fù)制算法并不需要采用1:1的空間,而是將一塊大的空間分給伊甸園(Eden)兩塊小的區(qū)域分配給兩個幸存區(qū)(Survivor)。在新生代采用復(fù)制算法,每次保留一塊幸存區(qū),將另外的一塊幸存區(qū)和伊甸園中的存活對象復(fù)制到保留的幸存區(qū)中。這樣每次都只有10%的空間被浪費,改善了算法。但是萬一伊甸園中和一塊幸存區(qū)中的對象存活的數(shù)量超過了另一塊幸存區(qū)的容量怎么辦?(雖然極少出現(xiàn)這種情況)這時就要依賴?yán)夏甏鰮?dān)保了。老年代的對象存活率較高,而且還要為新生代做擔(dān)保,所以不宜采用復(fù)制算法。一般采用標(biāo)記-清理或者標(biāo)記整理算法。
對于垃圾回收和內(nèi)存管理就先說到這里了,歡迎大家在評論區(qū)注水交流!
點擊鏈接,文章!
總結(jié)
以上是生活随笔為你收集整理的java 全局变量 内存不回收_JAVA知识梳理:内存管理与垃圾回收机制的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java 变量锁_一张图看透java的“
- 下一篇: 使用append之后数组维度消失_JAV