JVM:gc什么时候开始?System.gc()能保证gc一定发生吗?
gc什么時候開始?
“你能不能談談,java GC是在什么時候,對什么東西,做了什么事情?”
在什么時候:
1.新生代有一個Eden區和兩個survivor區,首先將對象放入Eden區,如果空間不足就向其中的一個survivor區上放,如果仍然放不下就會引發一次發生在新生代的minor GC,將存活的對象放入另一個survivor區中,然后清空Eden和之前的那個survivor區的內存。在某次GC過程中,如果發現仍然又放不下的對象,就將這些對象放入老年代內存里去。
2.大對象以及長期存活的對象直接進入老年區。
3.當每次執行minor GC的時候應該對要晉升到老年代的對象進行分析,如果這些馬上要到老年區的老年對象的大小超過了老年區的剩余大小,那么執行一次Full GC以盡可能地獲得老年區的空間。
對什么東西:從GC Roots搜索不到,而且經過一次標記清理之后仍沒有復活的對象。
做什么:?
新生代:復制清理;?
老年代:標記-清除和標記-壓縮算法;?
永久代:存放Java中的類和加載類的類加載器本身。
GC Roots都有哪些:?
1. 虛擬機棧中的引用的對象?
2. 方法區中靜態屬性引用的對象,常量引用的對象?
3. 本地方法棧中JNI(即一般說的Native方法)引用的對象。
友情鏈接:Java GC的那些事(上)
友情鏈接:Java GC的那些事(下)
友情鏈接:CMS垃圾收集器介紹
垃圾回收算法有哪些?
?
答:
引用計數 :原理是此對象有一個引用,即增加一個計數,刪除一個引用則減少一個計數。垃圾回收時,只用收集計數為 0 的對象。此算法最致命的是無法處理循環引用的問題;
標記-清除 :此算法執行分兩階段。第一階段從引用根節點開始標記所有被引用的對象,第二階段遍歷整個堆,把未標記的對象清除;
此算法需要暫停整個應用,同時,會產生內存碎片;
復制算法 :此算法把內存空間劃為兩個相等的區域,每次只使用其中一個區域。垃圾回收時,遍歷當前使用區域,把正在使用中的對象復制到另外一個區域中;
此算法每次只處理正在使用中的對象,因此復制成本比較小,同時復制過去以后還能進行相應的內存整理,不會出現 “碎片” 問題。當然,此算法的缺點也是很明顯的,就是需要兩倍內存空間;(8:1:1需要內存擔保)
標記-整理 :此算法結合了 “標記-清除” 和 “復制” 兩個算法的優點。也是分兩階段,第一階段從根節點開始標記所有被引用對象,第二階段遍歷整個堆,把清除未標記對象并且把存活對象 “壓縮” 到堆的其中一塊,按順序排放。
此算法避免了 “標記-清除” 的碎片問題,同時也避免了 “復制” 算法的空間問題。
類似-Xms、-Xmn 這些參數的含義:
答:
堆內存分配:
JVM 初始分配的內存由-Xms 指定,默認是物理內存的 1/64;
JVM 最大分配的內存由-Xmx 指定,默認是物理內存的 1/4;
默認空余堆內存小于 40% 時,JVM 就會增大堆直到-Xmx 的最大限制;空余堆內存大于 70% 時,JVM 會減少堆直到 -Xms 的最小限制;
因此服務器一般設置-Xms、-Xmx 相等以避免在每次 GC 后調整堆的大小。對象的堆內存由稱為垃圾回收器的自動內存管理系統回收。
非堆內存分配:
JVM 使用-XX:PermSize 設置非堆內存初始值,默認是物理內存的 1/64;
由 XX:MaxPermSize 設置最大非堆內存的大小,默認是物理內存的 1/4;
-Xmn2G:設置年輕代大小為 2G;
-XX:SurvivorRatio,設置年輕代中 Eden 區與 Survivor 區的比值。
System.gc()能保證gc一定發生嗎??
查看源碼
當我們調用System.gc()的時候,其實并不會馬上進行垃圾回收,甚至不一定會執行垃圾回收,查看系統源碼可以看到
??? /**
???? * Indicates to the VM that it would be a good time to run the
???? * garbage collector. Note that this is a hint only. There is no guarantee
???? * that the garbage collector will actually be run.
???? */
??? public static void gc() {
??????? boolean shouldRunGC;
??????? synchronized(lock) {
??????????? shouldRunGC = justRanFinalization;
??????????? if (shouldRunGC) {
??????????????? justRanFinalization = false;
??????????? } else {
??????????????? runGC = true;
??????????? }
??????? }
??????? if (shouldRunGC) {
??????????? Runtime.getRuntime().gc();
??????? }
??? }
?
也就是justRanFinalization=true的時候才會執行
查找發現當調用runFinalization()的時候justRanFinalization變為true
下面是runFinalization()的源碼
/**
* Provides a hint to the VM that it would be useful to attempt
* to perform any outstanding object finalization.
*/
public static void runFinalization() {
??????? boolean shouldRunGC;
??????? synchronized(lock) {
??????????? shouldRunGC = runGC;
??????????? runGC = false;
??????? }
??????? if (shouldRunGC) {
??????????? Runtime.getRuntime().gc();
??????? }
??????? Runtime.getRuntime().runFinalization();
??????? synchronized(lock) {
??????????? justRanFinalization = true;
??????? }
}
1234567891011121314151617181920
?
其實當我們直接調用System.gc()只會把這次gc請求記錄下來,等到runFinalization=true的時候才會先去執行GC,runFinalization=true之后會在允許一次system.gc()。之后在call System.gc()還會重復上面的行為。
所以System.gc()要跟System.runFinalization()一起搭配使用才好。
查看ZygoteInit.java 里面 gc()和runFinalizationSync()是配合使用的,這樣才有效果
static void gcAndFinalize() {
??? final VMRuntime runtime = VMRuntime.getRuntime();
??? /* runFinalizationSync() lets finalizers be called in Zygote,
??? * which doesn't have a HeapWorker thread.
??? */
??? System.gc();
??? runtime.runFinalizationSync();
??? System.gc();
}12345678910
?
解決方案
由此可見,當我們需要調用的System.gc()的時候 要這樣才會執行
System.gc();
runtime.runFinalizationSync();
System.gc();123
其實這個gc()函數的作用只是提醒虛擬機:程序員希望進行一次垃圾回收。但是它不能保證垃圾回收一定會進行,而且具體什么時候進行是取決于具體的虛擬機的,不同的虛擬機有不同的對策。
?
不過個人建議不到萬不得已不要調用,因為jvm有自己的gc策略,根本不需要我們來手動
總結
以上是生活随笔為你收集整理的JVM:gc什么时候开始?System.gc()能保证gc一定发生吗?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: JVM:永久代 以及jdk1.8为什么将
- 下一篇: JVM:堆与栈的比较