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