java gc会回收类么_Java GC 垃圾回收机制
一、Java GC是什么?
Java垃圾回收是對(duì)JVM(Java Virtual Machine)中的內(nèi)存進(jìn)行標(biāo)記,并確定哪些內(nèi)存需要回收,根據(jù)一定的回收策略,自動(dòng)的回收內(nèi)存,永不停息(Nerver Stop)的保證JVM中的內(nèi)存空間,防止出現(xiàn)內(nèi)存泄露和溢出問(wèn)題,由JVM負(fù)責(zé)啟動(dòng)。
Java GC機(jī)制主要完成3件事:確定哪些內(nèi)存需要回收,確定什么時(shí)候需要執(zhí)行GC,如何執(zhí)行GC。
二、Java堆內(nèi)存
Java內(nèi)存分配和回收的機(jī)制概括的說(shuō),就是:分代分配,分代回收。對(duì)象將根據(jù)存活的時(shí)間被分為:年輕代(Young Generation)、年老代(Old Generation)、永久代(Permanent Generation,也就是方法區(qū))。
1、年輕代(Young Generation):對(duì)象被創(chuàng)建時(shí),內(nèi)存的分配首先發(fā)生在年輕代(大對(duì)象可以直接被創(chuàng)建在年老代),大部分的對(duì)象在創(chuàng)建后很快就不再使用,因此很快變得不可達(dá),于是被年輕代的GC機(jī)制清理掉(IBM的研究表明,98%的對(duì)象都是很快消亡的),這個(gè)GC機(jī)制被稱(chēng)為Minor GC或叫Young GC。注意,Minor GC并不代表年輕代內(nèi)存不足,它事實(shí)上只表示在Eden區(qū)上的GC。
年輕代上的內(nèi)存分配是這樣的,年輕代可以分為3個(gè)區(qū)域:Eden區(qū)(伊甸園,亞當(dāng)和夏娃偷吃禁果生娃娃的地方,用來(lái)表示內(nèi)存首次分配的區(qū)域,再貼切不過(guò))和兩個(gè)存活區(qū)(Survivor 0 、Survivor 1)。
1)絕大多數(shù)剛創(chuàng)建的對(duì)象會(huì)被分配在Eden區(qū),其中的大多數(shù)對(duì)象很快就會(huì)消亡。Eden區(qū)是連續(xù)的內(nèi)存空間,因此在其上分配內(nèi)存極快;
2)最初一次,當(dāng)Eden區(qū)滿的時(shí)候,執(zhí)行Minor GC,將消亡的對(duì)象清理掉,并將剩余的對(duì)象復(fù)制到一個(gè)存活區(qū)Survivor0(此時(shí),Survivor1是空白的,兩個(gè)Survivor總有一個(gè)是空白的);
3)下次Eden區(qū)滿了,再執(zhí)行一次Minor GC,將消亡的對(duì)象清理掉,將存活的對(duì)象復(fù)制到Survivor1中,然后清空Eden區(qū);
4)將Survivor0中消亡的對(duì)象清理掉,將其中可以晉級(jí)的對(duì)象晉級(jí)到Old區(qū),將存活的對(duì)象也復(fù)制到Survivor1區(qū),然后清空Survivor0區(qū);
5)當(dāng)兩個(gè)存活區(qū)切換了幾次(HotSpot虛擬機(jī)默認(rèn)15次,用-XX:MaxTenuringThreshold控制,大于該值進(jìn)入老年代,但這只是個(gè)最大值,并不代表一定是這個(gè)值)之后,仍然存活的對(duì)象(其實(shí)只有一小部分,比如,我們自己定義的對(duì)象),將被復(fù)制到老年代。
從上面的過(guò)程可以看出,Eden區(qū)是連續(xù)的空間,且Survivor總有一個(gè)為空。經(jīng)過(guò)一次GC和復(fù)制,一個(gè)Survivor中保存著當(dāng)前還活著的對(duì)象,而Eden區(qū)和另一個(gè)Survivor區(qū)的內(nèi)容都不再需要了,可以直接清空,到下一次GC時(shí),兩個(gè)Survivor的角色再互換。因此,這種方式分配內(nèi)存和清理內(nèi)存的效率都極高,這種垃圾回收的方式就是著名的“停止-復(fù)制(Stop-and-copy)”清理法(將Eden區(qū)和一個(gè)Survivor中仍然存活的對(duì)象拷貝到另一個(gè)Survivor中),這不代表著停止復(fù)制清理法很高效,其實(shí),它也只在這種情況下高效,如果在老年代采用停止復(fù)制,則挺悲劇的。
2、年老代(Old Generation):對(duì)象如果在年輕代存活了足夠長(zhǎng)的時(shí)間而沒(méi)有被清理掉(即在幾次Young GC后存活了下來(lái)),則會(huì)被復(fù)制到年老代,年老代的空間一般比年輕代大,能存放更多的對(duì)象,在年老代上發(fā)生的GC次數(shù)也比年輕代少。當(dāng)年老代內(nèi)存不足時(shí),將執(zhí)行Major GC,也叫 Full GC。
可以使用-XX:+UseAdaptiveSizePolicy開(kāi)關(guān)來(lái)控制是否采用動(dòng)態(tài)控制策略,如果動(dòng)態(tài)控制,則動(dòng)態(tài)調(diào)整Java堆中各個(gè)區(qū)域的大小以及進(jìn)入老年代的年齡。
如果對(duì)象比較大(比如長(zhǎng)字符串或大數(shù)組),Young空間不足,則大對(duì)象會(huì)直接分配到老年代上(大對(duì)象可能觸發(fā)提前GC,應(yīng)少用,更應(yīng)避免使用短命的大對(duì)象)。用-XX:PretenureSizeThreshold來(lái)控制直接升入老年代的對(duì)象大小,大于這個(gè)值的對(duì)象會(huì)直接分配在老年代上。
三、Java GC機(jī)制-回收算法
GC機(jī)制的基本算法是:分代收集,這個(gè)不用贅述。下面闡述每個(gè)分代的收集方法。
1、年輕代-“停止-復(fù)制”算法
事實(shí)上,在上一節(jié),已經(jīng)介紹了新生代的主要垃圾回收方法,在新生代中,使用“停止-復(fù)制”算法進(jìn)行清理,將新生代內(nèi)存分為2部分,1部分 Eden區(qū)較大,1部分Survivor比較小,并被劃分為兩個(gè)等量的部分。每次進(jìn)行清理時(shí),將Eden區(qū)和一個(gè)Survivor中仍然存活的對(duì)象拷貝到 另一個(gè)Survivor中,然后清理掉Eden和剛才的Survivor。
這里也可以發(fā)現(xiàn),停止復(fù)制算法中,用來(lái)復(fù)制的兩部分并不總是相等的(傳統(tǒng)的停止復(fù)制算法兩部分內(nèi)存相等,但新生代中使用1個(gè)大的Eden區(qū)和2個(gè)小的Survivor區(qū)來(lái)避免這個(gè)問(wèn)題)
由于絕大部分的對(duì)象都是短命的,甚至存活不到Survivor中,所以,Eden區(qū)與Survivor的比例較大,HotSpot默認(rèn)是 8:1,即分別占新生代的80%,10%,10%。如果一次回收中,Survivor+Eden中存活下來(lái)的內(nèi)存超過(guò)了10%,則需要將一部分對(duì)象分配到 老年代。用-XX:SurvivorRatio參數(shù)來(lái)配置Eden區(qū)域Survivor區(qū)的容量比值,默認(rèn)是8,代表Eden:Survivor1:Survivor2=8:1:1.
2、老年代-“標(biāo)記-整理”算法
老年代存儲(chǔ)的對(duì)象比年輕代多得多,而且不乏大對(duì)象,對(duì)老年代進(jìn)行內(nèi)存清理時(shí),如果使用停止-復(fù)制算法,則相當(dāng)?shù)托?。一?#xff0c;老年代用的算法是標(biāo)記-整理算法,即:標(biāo)記出仍然存活的對(duì)象(存在引用的),將所有存活的對(duì)象向一端移動(dòng),以保證內(nèi)存的連續(xù)。
在發(fā)生Minor GC時(shí),虛擬機(jī)會(huì)檢查每次晉升進(jìn)入老年代的大小是否大于老年代的剩余空間大小,如果大于,則直接觸發(fā)一次Full GC,否則,就查看是否設(shè)置了-XX:+HandlePromotionFailure(允許擔(dān)保失敗),如果允許,則只會(huì)進(jìn)行MinorGC,此時(shí)可以容忍內(nèi)存分配失敗;如果不允許,則仍然進(jìn)行Full GC(這代表著如果設(shè)置-XX:+Handle PromotionFailure,則觸發(fā)MinorGC就會(huì)同時(shí)觸發(fā)Full GC,哪怕老年代還有很多內(nèi)存,所以,最好不要這樣做)。
3、方法區(qū)(永久代)
永久代的回收有兩種:常量池中的常量,無(wú)用的類(lèi)信息,常量的回收很簡(jiǎn)單,沒(méi)有引用了就可以被回收。對(duì)于無(wú)用的類(lèi)進(jìn)行回收,必須保證3點(diǎn):
類(lèi)的所有實(shí)例都已經(jīng)被回收
加載類(lèi)的ClassLoader已經(jīng)被回收
類(lèi)對(duì)象的Class對(duì)象沒(méi)有被引用(即沒(méi)有通過(guò)反射引用該類(lèi)的地方)
永久代的回收并不是必須的,可以通過(guò)參數(shù)來(lái)設(shè)置是否對(duì)類(lèi)進(jìn)行回收。HotSpot提供-Xnoclassgc進(jìn)行控制
使用-verbose,-XX:+TraceClassLoading、-XX:+TraceClassUnLoading可以查看類(lèi)加載和卸載信息
-verbose、-XX:+TraceClassLoading可以在Product版HotSpot中使用;
-XX:+TraceClassUnLoading需要fastdebug版HotSpot支持
四、對(duì)象什么時(shí)候符合垃圾回收的條件?
1、所有實(shí)例都沒(méi)有活動(dòng)線程訪問(wèn)。
2、沒(méi)有被其他任何實(shí)例訪問(wèn)的循環(huán)引用實(shí)例。
Java 中有不同的引用類(lèi)型。判斷實(shí)例是否符合垃圾收集的條件都依賴(lài)于它的引用類(lèi)型。
引用類(lèi)型 垃圾收集
強(qiáng)引用(Strong Reference)不符合垃圾收集
軟引用(Soft Reference) 垃圾收集可能會(huì)執(zhí)行,但會(huì)作為最后的選擇
弱引用(Weak Reference) 符合垃圾收集
虛引用(Phantom Reference)符合垃圾收集
GC Scope 示例程序
class GCScope {
GCScope t;
static int i = 1;
public static void main(String args[]) {
GCScope t1 = new GCScope();
GCScope t2 = new GCScope();
GCScope t3 = new GCScope();
// 沒(méi)有對(duì)象符合GC
t1.t = t2; // 沒(méi)有對(duì)象符合GC
t2.t = t3; // 沒(méi)有對(duì)象符合GC
t3.t = t1; // 沒(méi)有對(duì)象符合GC
t1 = null;
// 沒(méi)有對(duì)象符合GC (t3.t 仍然有一個(gè)到 t1 的引用)
t2 = null;
// 沒(méi)有對(duì)象符合GC (t3.t.t 仍然有一個(gè)到 t2 的引用)
t3 = null;
// 所有三個(gè)對(duì)象都符合GC (它們中沒(méi)有一個(gè)擁有引用。
// 只有各對(duì)象的變量 t 還指向了彼此,
// 形成了一個(gè)由對(duì)象組成的環(huán)形的島,而沒(méi)有任何外部的引用。)
}
protected void finalize() {
System.out.println("Garbage collected from object" + i);
i++;
}
五、 JVM參數(shù)使用
1、堆內(nèi)存相關(guān)
-Xms 與 -Xmx
-Xms用于指定Java應(yīng)用使用的最小堆內(nèi)存,如-Xms1024m表示將Java應(yīng)用最小堆設(shè)置為1024M。-Xmx用于指定Java應(yīng)用使用的最大堆內(nèi)存,如-Xmx1024m表示將Java應(yīng)用最大堆設(shè)置為1024m。過(guò)小的堆內(nèi)存可能會(huì)造成程序拋出OOM異常,所以正常發(fā)布的應(yīng)用應(yīng)該明確指定這兩個(gè)參數(shù)。并且,一般會(huì)選擇將-Xms與-Xmx設(shè)置成一樣大小,防止JVM動(dòng)態(tài)調(diào)整堆內(nèi)存容量對(duì)程序造成性能影響。
-Xmn
通過(guò)-Xmn可以設(shè)置堆內(nèi)存中新生代的容量,以此達(dá)到間接控制老年代容量的作用,因?yàn)闆](méi)有JVM參數(shù)可以直接控制老年代的容量。如-Xmn256m表示將新生代容量設(shè)置為256M。假如這個(gè)時(shí)候額外指定了-Xms1024m -Xmx1024m,那么老年代的容量為768M(1024-256=768)。
-XX:PermSize 與 -XX:MaxPermSize
-XX:PermSize和-XX:MaxPermSize分別用于設(shè)置永久代的容量和最大容量。如-XX:PermSize=64m -XX:MaxPermSize=128m表示將永久代的初始容量設(shè)置為64M,最大容量設(shè)置為128M。
-XX:SurvivorRatio
這個(gè)參數(shù)用于設(shè)置新生代中Eden區(qū)和Survivor(S0、S1)的容量比值。默認(rèn)設(shè)置為-XX:SurvivorRatio=8表示Eden區(qū)與Survivor的容量比例為8:1:1。假設(shè)-Xmn256m -XX:Survivor=8,那么Eden區(qū)容量為204.8M(256M / 10 * 8),S0和S1區(qū)的容量大小均為25.6M(256M / 10 * 1)。
總結(jié)
以上是生活随笔為你收集整理的java gc会回收类么_Java GC 垃圾回收机制的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: private用法 java_关于and
- 下一篇: java怎么构造map_Java中Map