java與c 相比,在內(nèi)存的分配與回收方面更加具備“自動(dòng)化”,似乎我們并不需要了解虛擬機(jī)GC與內(nèi)存分配。然而當(dāng)需要排查各種內(nèi)存溢出,內(nèi)存泄露的問題時(shí),當(dāng)垃圾收集成為系統(tǒng)優(yōu)化的瓶頸時(shí),我們必須了解JVM的“自動(dòng)化”技術(shù),以實(shí)現(xiàn)對其的監(jiān)控與調(diào)節(jié)。
在上一篇博文(java內(nèi)存區(qū)域)中介紹了java內(nèi)存運(yùn)行時(shí)區(qū)域的各個(gè)部分,其中虛擬機(jī)棧,程序計(jì)數(shù)器,本地方法棧是線程私有的,這幾個(gè)區(qū)域在編譯器其分配的內(nèi)存空間大小基本上是確定的。然而方法區(qū)和java堆則不一樣,隨著線程的進(jìn)行,方法的執(zhí)行以及對象的創(chuàng)建等,其內(nèi)存空間的大小是不斷變化的,所以垃圾回收的作用范圍主要是這些區(qū)域。
內(nèi)存的回收主要包括三個(gè)方面:
我們可以肯定的是,我們需要回收的對象是那些不再使用的對象,在c 中我們通過析構(gòu)函數(shù)釋放對象占用的內(nèi)存空間。而在java中,這一步是虛擬機(jī)幫我們完成的。那么在回收對象的內(nèi)存空間之前,虛擬機(jī)需要判斷哪些對象是無用的。下面介紹兩種算法:
給對象添加一個(gè)引用計(jì)數(shù)器,每當(dāng)有一個(gè)地方引用它時(shí),計(jì)數(shù)器就加1;當(dāng)引用失效時(shí),計(jì)數(shù)器就減1。當(dāng)計(jì)數(shù)器的值為0時(shí)就認(rèn)為這個(gè)對象是無用的。
這種算法簡單易懂,判定效率也很高,不需要進(jìn)行復(fù)雜的計(jì)算,但是其很難解決對象之間相互引用的問題。比如下圖:
objA引用B,objB引用A,這時(shí)就算我們不在使用A和B了,但由于A和B之間相互引用導(dǎo)致引用計(jì)數(shù)器的值不為0,通過引用計(jì)數(shù)算法無法將其回收。正是由于這種算法的弊端,所以主流的java虛擬機(jī)都沒有選擇這種算法來管理內(nèi)存。
通過一系列的稱為“GC ROOT”的對象作為起始點(diǎn),從這些節(jié)點(diǎn)通過其指向其他對象的用用向下搜索,搜索走過的路徑稱為引用鏈,當(dāng)一個(gè)對象到“GC ROOT”之間沒有引用鏈就認(rèn)為其是不可用的。也就是從root開始不可達(dá)的節(jié)點(diǎn)對象為無用對象
圖中對象4,5,6不可達(dá)。
在java中可作為GC root的對象包括下面幾種:
在JDK1.2之后,java對引用的概念進(jìn)行了擴(kuò)充,將引用分為:強(qiáng)引用,軟引用,弱引用,虛引用。
不同虛擬機(jī)的垃圾收集算法的實(shí)現(xiàn)各不相同,這里只介紹幾種算法的主要思想。
該算法分為標(biāo)記
和清除
兩個(gè)階段。首先標(biāo)記出所有需要回收的對象,在標(biāo)記完成后統(tǒng)一回收所有被標(biāo)記的對象。
不足:
不足:將內(nèi)存空間縮小為了原來的一半。
標(biāo)記過程與標(biāo)記-清除算法
一樣,但后續(xù)的步驟不是直接對可回收對象進(jìn)行清理,而是讓所有存活的對象都想一端移動(dòng),然后直接清理掉端邊界以外的內(nèi)存。
- 新生代:主要是用來存放新生的對象。一般占據(jù)堆的1/3空間。由于頻繁創(chuàng)建對象,所以新生代會(huì)頻繁觸發(fā)MinorGC進(jìn)行垃圾回收。
來源:http://www.icode9.com/content-1-121351.html
- 老年代:主要存放應(yīng)用程序中生命周期長的內(nèi)存對象,或者占用空間比較大的對象。