關(guān)于JVM垃圾回收的java.lang.ref.Finalizer問(wèn)題
JVM垃圾回收的java.lang.ref.Finalizer
基礎(chǔ)知識(shí)
Shallow Size
- 對(duì)象自身占用的內(nèi)存大小,不包括它引用的對(duì)象
Retained Size
- Retained Size=當(dāng)前對(duì)象大小+當(dāng)前對(duì)象可直接或間接引用到的對(duì)象的大小總和

可達(dá)性分析算法:
基本思路是通過(guò)一系列成為GC ROOTS 的對(duì)象作為起點(diǎn),當(dāng)一個(gè)對(duì)象到 GC ROOTS 沒(méi)有任何相連,證明此對(duì)象是不可達(dá),即被判斷為可回收的對(duì)象。

之后的過(guò)程是:
被標(biāo)記不可達(dá)的對(duì)象以后,進(jìn)行第一次標(biāo)記,和第一次篩選,條件是該對(duì)象 有沒(méi)有必要執(zhí)行finalize方法
沒(méi)有必要執(zhí)行finalize方法的情況是
1.finalize已經(jīng)執(zhí)行過(guò)(finalize 方法只會(huì)被執(zhí)行一次)
2.該對(duì)象沒(méi)有重寫finalize方法
如果要執(zhí)行finalize方法,該對(duì)象進(jìn)入一個(gè)F-Queue隊(duì)列,稍后有 一個(gè)優(yōu)先級(jí)為8的 finalizer線程來(lái)執(zhí)行(注意:如果一個(gè)對(duì)象在 finalize 方法中運(yùn)行緩慢,將會(huì)導(dǎo)致隊(duì)列后的其他對(duì)象永遠(yuǎn)等待,嚴(yán)重時(shí)將會(huì)導(dǎo)致系統(tǒng)崩潰)
GC對(duì)隊(duì)列中進(jìn)行第二次標(biāo)記,如果在執(zhí)行finalize方法的時(shí)候?qū)⒆约汉虶C ROOTS關(guān)聯(lián)上,該對(duì)象即可逃離回收,否則,被回收掉
重要
對(duì)象是在已經(jīng)被GC識(shí)別為是垃圾后才丟到Queue中的,在queue中依然占用內(nèi)存
引用對(duì)象的類型
引用對(duì)象的類型第一篇已說(shuō)過(guò),分為以下類型
強(qiáng)引用>軟引用>弱引用>虛引用,
涉及到的相關(guān)類:
- 軟引用:softReference
- 弱引用:weekRefernce
- 虛引用:pathomReference
但還有一個(gè)特殊的引用:FinalReference類,該類也是JVM調(diào)優(yōu)的常見(jiàn)的調(diào)優(yōu)場(chǎng)景之一。
要了解FinalReference,先了解final()
final,finally和finalize()的區(qū)別?
前兩者我相信大多數(shù)人都能答出,但finalize()雖然也有人答出,但可能只是硬搬面試題答案。
1.final:修飾詞,作用于成員變量,方法和類。
- 成員變量,基本數(shù)據(jù)類型表示該成員變量不可變更數(shù)值,對(duì)象表示該對(duì)象引用地址不可改變。
- 方法,表示該方法不可重載
- 類,表示該類不可被繼承
2.finally:是異常處理機(jī)制的一部分,表示總是執(zhí)行這段代碼,先執(zhí)行異常,后執(zhí)行finally
@Slf4j
public class FinallyDemo {
public static void main(String[] args) {
try {
int i = 1 / 0;
log.info("1/0");
} catch (Exception e) {
log.info("執(zhí)行異常");
} finally {
log.info("執(zhí)行finally片段");
}
}
}
// 打印結(jié)果:
// 10:36:09.777 [main] INFO com.gc.demo.finalize.finlly.FinallyDemo - 執(zhí)行異常
// 10:36:09.780 [main] INFO com.gc.demo.finalize.finlly.FinallyDemo - 執(zhí)行finally片段
3.finalize():是object的一個(gè)方法。簡(jiǎn)簡(jiǎn)單單的一句話,我想大多數(shù)人懂得,但其實(shí)我們應(yīng)該繼續(xù)深入。
Finalize()
Object源碼
public class Object {
private static native void registerNatives();
static {
registerNatives();
}
.......
@Deprecated(since="9")
protected void finalize() throws Throwable { }
}
上述代碼可以看出:finalize()已被棄用,不建議使用
為什么finalize()已被棄用,不建議使用?
原因:容易導(dǎo)致堆內(nèi)存溢出。
為什么不直接刪除呢?
1. 兼容老舊項(xiàng)目
2. 是GC回收對(duì)象前,對(duì)該對(duì)象生前(被回收前)必須執(zhí)行的邏輯業(yè)務(wù),保證程序正常運(yùn)行。例如:FileInputStream,源碼如下
public class FileInputStream extends InputStream{
......
static class AltFinalizer {
private final FileInputStream fis;
AltFinalizer(FileInputStream fis) {
this.fis = fis;
}
@Override
@SuppressWarnings("deprecation")
protected final void finalize() {
try {
if ((fis.fd != null) && (fis.fd != FileDescriptor.in)) {
/* if fd is shared, the references in FileDescriptor
* will ensure that finalizer is only called when
* safe to do so. All references using the fd have
* become unreachable. We can call close()
*/
fis.close();
}
} catch (IOException ioe) {
// ignore
}
}
}
FileInputStream類的close()方法大家都不陌生,用于關(guān)閉流輸入,但他被寫到finalize()方法內(nèi)。
原理:
JVM啟動(dòng)時(shí),檢測(cè)所有對(duì)象,是否實(shí)現(xiàn)了finalize()方法,如果實(shí)現(xiàn)了,就會(huì)該對(duì)象標(biāo)記為finalizer類,且交由finalizerThread線程管理,當(dāng)GC回收時(shí),finalizerThread線程就會(huì)處理finalizer類,檢測(cè)是否有執(zhí)行finalize()方法,如果有,則可以把finalizer從線程中去除,對(duì)應(yīng)的對(duì)象就可以被回收;如果沒(méi)有,則一直交由finalizerThread線程管理。
詳細(xì)流程
JVM啟動(dòng)時(shí),檢測(cè)對(duì)象是否實(shí)現(xiàn)了finalize()方法,檢測(cè)方法:判斷has_finalizer_flag和registerfinalizerrsaltInit字段
A對(duì)象實(shí)現(xiàn)了finalize()方法,就會(huì)把A對(duì)象的finalize()函數(shù)注冊(cè)到finalizerthread線程和referencequeue隊(duì)列中,注冊(cè)完畢后,且稱該函數(shù)注冊(cè)的B對(duì)象稱為Finalizer類,指向原有的A對(duì)象。
GC回收垃圾時(shí),啟動(dòng)FinalizerThread線程,檢測(cè)Finalizer類對(duì)應(yīng)的A對(duì)象的finalize()方法是否已執(zhí)行,已調(diào)用,則可以把Finalizer類去除,回收Finalizer類,從而回收A對(duì)象;如果沒(méi)有調(diào)用,則繼續(xù)交由FinalizerThread線程管理,不回收。
從上面原因引申出:為什么堆內(nèi)存溢出?
GC回收時(shí),用戶線程、GC回收線程和FinalizerThread線程都在運(yùn)行,但由于FinalizerThread線程優(yōu)先度比GC回收線程和用戶線程低,所以導(dǎo)致回收速度比對(duì)象創(chuàng)建速度慢,最終導(dǎo)致堆內(nèi)存溢出。
示例程序:
JDK11、G1垃圾收集器
JVM設(shè)置
-Xms10m -Xmx10m -Xlog:ref*=debug -Xlog:gc:gc.log -Xlog:gc+heap=trace -verbose:gc -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=E:\mycode\gc-demo\a.dump
public class FinalizeGcDemo {
private static class GcDemo {
private byte[] content = new byte[1024 * 1024];
@Override
protected void finalize() throws Throwable {
System.out.println("執(zhí)行了finalize方法");
}
}
public static void main(String[] args) {
for (int i = 0; i < 10000; i++) {
GcDemo gcDemo = new GcDemo();
}
}
}
日志如下:
[0.453s][debug][gc,heap ] GC(23) Heap after GC invocations=21 (full 13): garbage-first heap total 10240K, used 10123K [0x00000000ff600000, 0x0000000100000000)
[0.453s][debug][gc,heap ] GC(23) region size 1024K, 0 young (0K), 0 survivors (0K)
[0.453s][debug][gc,heap ] GC(23) Metaspace used 6220K, capacity 6395K, committed 6528K, reserved 1056768K
[0.453s][debug][gc,heap ] GC(23) class space used 538K, capacity 606K, committed 640K, reserved 1048576K
[0.453s][info ][gc ] GC(23) Pause Full (G1 Evacuation Pause) 9M->9M(10M) 5.420ms
[0.455s][debug][gc,heap ] GC(24) Heap before GC invocations=21 (full 13): garbage-first heap total 10240K, used 10123K [0x00000000ff600000, 0x0000000100000000)
[0.455s][debug][gc,heap ] GC(24) region size 1024K, 0 young (0K), 0 survivors (0K)
[0.455s][debug][gc,heap ] GC(24) Metaspace used 6220K, capacity 6395K, committed 6528K, reserved 1056768K
[0.455s][debug][gc,heap ] GC(24) class space used 538K, capacity 606K, committed 640K, reserved 1048576K
[0.455s][debug][gc,ref ] GC(24) Skipped phase1 of Reference Processing due to unavailable references
[0.455s][debug][gc,ref ] GC(24) Skipped phase2 of Reference Processing due to unavailable references
[0.455s][debug][gc,ref ] GC(24) Skipped phase3 of Reference Processing due to unavailable references
[0.455s][debug][gc,ref ] GC(24) Skipped phase4 of Reference Processing due to unavailable references
[0.455s][debug][gc,phases,ref ] GC(24) Reference Processing: 0.0ms
[0.455s][debug][gc,phases,ref ] GC(24) Reconsider SoftReferences: 0.0ms
[0.455s][debug][gc,phases,ref ] GC(24) SoftRef (ms): skipped
[0.455s][debug][gc,phases,ref ] GC(24) Notify Soft/WeakReferences: 0.0ms
[0.455s][debug][gc,phases,ref ] GC(24) SoftRef (ms): skipped
[0.455s][debug][gc,phases,ref ] GC(24) WeakRef (ms): skipped
[0.455s][debug][gc,phases,ref ] GC(24) FinalRef (ms): skipped
[0.455s][debug][gc,phases,ref ] GC(24) Total (ms): skipped
[0.455s][debug][gc,phases,ref ] GC(24) Notify and keep alive finalizable: 0.0ms
[0.455s][debug][gc,phases,ref ] GC(24) FinalRef (ms): skipped
[0.455s][debug][gc,phases,ref ] GC(24) Notify PhantomReferences: 0.0ms
[0.455s][debug][gc,phases,ref ] GC(24) PhantomRef (ms): skipped
[0.455s][debug][gc,phases,ref ] GC(24) SoftReference:
[0.455s][debug][gc,phases,ref ] GC(24) Discovered: 0
[0.455s][debug][gc,phases,ref ] GC(24) Cleared: 0
[0.455s][debug][gc,phases,ref ] GC(24) WeakReference:
[0.455s][debug][gc,phases,ref ] GC(24) Discovered: 0
[0.455s][debug][gc,phases,ref ] GC(24) Cleared: 0
[0.455s][debug][gc,phases,ref ] GC(24) FinalReference:
[0.455s][debug][gc,phases,ref ] GC(24) Discovered: 0
[0.455s][debug][gc,phases,ref ] GC(24) Cleared: 0
[0.455s][debug][gc,phases,ref ] GC(24) PhantomReference:
[0.455s][debug][gc,phases,ref ] GC(24) Discovered: 0
[0.455s][debug][gc,phases,ref ] GC(24) Cleared: 0
[0.455s][info ][gc,heap ] GC(24) Eden regions: 0->0(1)
[0.455s][trace][gc,heap ] GC(24) Used: 0K, Waste: 0K
[0.455s][info ][gc,heap ] GC(24) Survivor regions: 0->0(1)
[0.455s][trace][gc,heap ] GC(24) Used: 0K, Waste: 0K
[0.455s][info ][gc,heap ] GC(24) Old regions: 2->2
[0.455s][trace][gc,heap ] GC(24) Used: 1931K, Waste: 116K
[0.455s][info ][gc,heap ] GC(24) Humongous regions: 8->8
[0.455s][trace][gc,heap ] GC(24) Used: 8192K, Waste: 0K
[0.455s][debug][gc,heap ] GC(24) Heap after GC invocations=22 (full 13): garbage-first heap total 10240K, used 10123K [0x00000000ff600000, 0x0000000100000000)
[0.455s][debug][gc,heap ] GC(24) region size 1024K, 0 young (0K), 0 survivors (0K)
[0.455s][debug][gc,heap ] GC(24) Metaspace used 6220K, capacity 6395K, committed 6528K, reserved 1056768K
[0.455s][debug][gc,heap ] GC(24) class space used 538K, capacity 606K, committed 640K, reserved 1048576K
[0.455s][info ][gc ] GC(24) Pause Young (Concurrent Start) (G1 Evacuation Pause) 9M->9M(10M) 0.585ms
[0.455s][debug][gc,heap ] GC(25) Heap before GC invocations=22 (full 13): garbage-first heap total 10240K, used 10123K [0x00000000ff600000, 0x0000000100000000)
[0.455s][debug][gc,heap ] GC(25) region size 1024K, 0 young (0K), 0 survivors (0K)
[0.455s][debug][gc,heap ] GC(25) Metaspace used 6220K, capacity 6395K, committed 6528K, reserved 1056768K
[0.455s][debug][gc,heap ] GC(25) class space used 538K, capacity 606K, committed 640K, reserved 1048576K
[0.455s][info ][gc ] GC(26) Concurrent Cycle
[0.457s][debug][gc,ref ] GC(25) Skipped phase3 of Reference Processing due to unavailable references
[0.457s][debug][gc,phases,ref ] GC(25) Reference Processing: 0.0ms
[0.457s][debug][gc,phases,ref ] GC(25) Reconsider SoftReferences: 0.0ms
[0.457s][debug][gc,phases,ref ] GC(25) SoftRef (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0, Workers: 1
[0.457s][debug][gc,phases,ref ] GC(25) Notify Soft/WeakReferences: 0.0ms
[0.457s][debug][gc,phases,ref ] GC(25) SoftRef (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0, Workers: 1
[0.457s][debug][gc,phases,ref ] GC(25) WeakRef (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0, Workers: 1
[0.457s][debug][gc,phases,ref ] GC(25) FinalRef (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0, Workers: 1
[0.457s][debug][gc,phases,ref ] GC(25) Total (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0, Workers: 1
[0.457s][debug][gc,phases,ref ] GC(25) Notify and keep alive finalizable: 0.0ms
[0.457s][debug][gc,phases,ref ] GC(25) FinalRef (ms): skipped
[0.457s][debug][gc,phases,ref ] GC(25) Notify PhantomReferences: 0.0ms
[0.457s][debug][gc,phases,ref ] GC(25) PhantomRef (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0, Workers: 1
[0.457s][debug][gc,phases,ref ] GC(25) SoftReference:
[0.457s][debug][gc,phases,ref ] GC(25) Discovered: 6
[0.457s][debug][gc,phases,ref ] GC(25) Cleared: 6
[0.457s][debug][gc,phases,ref ] GC(25) WeakReference:
[0.457s][debug][gc,phases,ref ] GC(25) Discovered: 5
[0.457s][debug][gc,phases,ref ] GC(25) Cleared: 5
[0.457s][debug][gc,phases,ref ] GC(25) FinalReference:
[0.457s][debug][gc,phases,ref ] GC(25) Discovered: 0
[0.457s][debug][gc,phases,ref ] GC(25) Cleared: 0
[0.457s][debug][gc,phases,ref ] GC(25) PhantomReference:
[0.457s][debug][gc,phases,ref ] GC(25) Discovered: 66
[0.457s][debug][gc,phases,ref ] GC(25) Cleared: 66
[0.460s][info ][gc,heap ] GC(25) Eden regions: 0->0(1)
[0.460s][trace][gc,heap ] GC(25) Used: 0K, Waste: 0K
[0.460s][info ][gc,heap ] GC(25) Survivor regions: 0->0(1)
[0.460s][trace][gc,heap ] GC(25) Used: 0K, Waste: 0K
[0.460s][info ][gc,heap ] GC(25) Old regions: 2->2
[0.460s][trace][gc,heap ] GC(25) Used: 1931K, Waste: 116K
[0.460s][info ][gc,heap ] GC(25) Humongous regions: 8->6
[0.460s][trace][gc,heap ] GC(25) Used: 6144K, Waste: 0K
[0.460s][debug][gc,heap ] GC(25) Heap after GC invocations=23 (full 14): garbage-first heap total 10240K, used 8075K [0x00000000ff600000, 0x0000000100000000)
[0.460s][debug][gc,heap ] GC(25) region size 1024K, 0 young (0K), 0 survivors (0K)
[0.460s][debug][gc,heap ] GC(25) Metaspace used 6220K, capacity 6395K, committed 6528K, reserved 1056768K
[0.460s][debug][gc,heap ] GC(25) class space used 538K, capacity 606K, committed 640K, reserved 1048576K
[0.460s][info ][gc ] GC(25) Pause Full (G1 Evacuation Pause) 9M->7M(10M) 5.032ms
[0.461s][info ][oopstorage,ref] StringTable weak: allocated 0x00000187729f28c8
執(zhí)行了finalize方法
執(zhí)行了finalize方法
執(zhí)行了finalize方法
[0.461s][info ][gc ] GC(26) Concurrent Cycle 5.981ms
[0.462s][info ][oopstorage,ref] StringTable weak: allocated 0x00000187729f28d0
[0.462s][info ][oopstorage,ref] StringTable weak: allocated 0x00000187729f28d8
[0.462s][info ][oopstorage,ref] JNI Weak: allocated 0x00000187729f44b8
[0.462s][info ][oopstorage,ref] JNI Global: allocated 0x000001877265b710
[0.462s][info ][oopstorage,ref] JNI Global: allocated 0x000001877265b718
[0.462s][info ][oopstorage,ref] JNI Weak: allocated 0x00000187729f29c0
[0.462s][info ][oopstorage,ref] JNI Global: allocated 0x000001877265b720
[0.462s][info ][oopstorage,ref] JNI Global: allocated 0x000001877265b728
[0.462s][info ][oopstorage,ref] StringTable weak: allocated 0x00000187729f28e0
[0.462s][info ][oopstorage,ref] StringTable weak: allocated 0x00000187729f28e8
[0.463s][info ][oopstorage,ref] JNI Weak: allocated 0x00000187729f29c8
[0.463s][info ][oopstorage,ref] JNI Global: allocated 0x000001877265b730
[0.463s][info ][oopstorage,ref] JNI Global: allocated 0x000001877265b740
[0.463s][info ][oopstorage,ref] StringTable weak: allocated 0x00000187729f28f0
[0.463s][info ][oopstorage,ref] StringTable weak: allocated 0x00000187729f28f8
[0.463s][info ][oopstorage,ref] StringTable weak: allocated 0x00000187729f2900
[0.463s][info ][oopstorage,ref] StringTable weak: allocated 0x00000187729f2908
[0.463s][info ][oopstorage,ref] JNI Weak: allocated 0x00000187729f29d0
[0.463s][info ][oopstorage,ref] JNI Global: allocated 0x000001877265b748
[0.463s][info ][oopstorage,ref] JNI Global: allocated 0x000001877265b750
[0.463s][info ][oopstorage,ref] JNI Weak: allocated 0x00000187729f29d8
[0.463s][info ][oopstorage,ref] JNI Global: allocated 0x000001877265b758
[0.463s][info ][oopstorage,ref] JNI Global: allocated 0x000001877265b760
[0.463s][info ][oopstorage,ref] JNI Global: allocated 0x000001877265b768
[0.463s][info ][oopstorage,ref] JNI Global: released 0x000001877265b670
[0.464s][info ][oopstorage,ref] JNI Global: allocated 0x000001877265b670
[0.464s][info ][oopstorage,ref] JNI Global: allocated 0x000001877265b770
[0.464s][info ][oopstorage,ref] JNI Weak: allocated 0x00000187729f29e0
[0.464s][info ][oopstorage,ref] JNI Global: allocated 0x000001877265b778
[0.464s][info ][oopstorage,ref] JNI Global: allocated 0x000001877265b780
[0.464s][info ][oopstorage,ref] JNI Weak: allocated 0x00000187729f29e8
[0.464s][info ][oopstorage,ref] JNI Global: allocated 0x000001877265b788
[0.464s][info ][oopstorage,ref] JNI Global: allocated 0x000001877265b790
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at com.gc.demo.finalize.FinalizeGcDemo.main(FinalizeGcDemo.java:26)
日志最后拋出堆內(nèi)存溢出(java.lang.OutOfMemoryError: Java heap space)異常。
通過(guò)MAT工具分析剛才程序運(yùn)行的dump文件
上面的分析報(bào)告,明確指出了Finalizer類已經(jīng)占用堆內(nèi)存的51.88%以上,且主要因?yàn)镚cDemo類下finalize()方法。
打印Reference類的JVM參數(shù)設(shè)置
- JDK1.9之前 -XX:+PrintReferenceGC
- JDK1.9(含1.9)以后 -Xlog:ref*=debug
日志打印
[0.323s][info ][gc ] GC(3) Pause Full (G1 Humongous Allocation) 8M->7M(10M) 5.099ms
[0.324s][debug][gc,ref ] GC(4) Skipped phase3 of Reference Processing due to unavailable references
[0.324s][debug][gc,phases,ref ] GC(4) Reference Processing: 0.0ms
[0.324s][debug][gc,phases,ref ] GC(4) Reconsider SoftReferences: 0.0ms
[0.324s][debug][gc,phases,ref ] GC(4) SoftRef (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0, Workers: 1
[0.324s][debug][gc,phases,ref ] GC(4) Notify Soft/WeakReferences: 0.0ms
[0.324s][debug][gc,phases,ref ] GC(4) SoftRef (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0, Workers: 1
[0.324s][debug][gc,phases,ref ] GC(4) WeakRef (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0, Workers: 1
[0.324s][debug][gc,phases,ref ] GC(4) FinalRef (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0, Workers: 1
[0.324s][debug][gc,phases,ref ] GC(4) Total (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0, Workers: 1
[0.324s][debug][gc,phases,ref ] GC(4) Notify and keep alive finalizable: 0.0ms
[0.324s][debug][gc,phases,ref ] GC(4) FinalRef (ms): skipped
[0.324s][debug][gc,phases,ref ] GC(4) Notify PhantomReferences: 0.0ms
[0.324s][debug][gc,phases,ref ] GC(4) PhantomRef (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0, Workers: 1
[0.324s][debug][gc,phases,ref ] GC(4) SoftReference:
[0.324s][debug][gc,phases,ref ] GC(4) Discovered: 34
[0.324s][debug][gc,phases,ref ] GC(4) Cleared: 5
[0.324s][debug][gc,phases,ref ] GC(4) WeakReference:
[0.324s][debug][gc,phases,ref ] GC(4) Discovered: 5
[0.324s][debug][gc,phases,ref ] GC(4) Cleared: 5
[0.324s][debug][gc,phases,ref ] GC(4) FinalReference:
[0.324s][debug][gc,phases,ref ] GC(4) Discovered: 0
[0.324s][debug][gc,phases,ref ] GC(4) Cleared: 0
[0.324s][debug][gc,phases,ref ] GC(4) PhantomReference:
[0.324s][debug][gc,phases,ref ] GC(4) Discovered: 62
[0.324s][debug][gc,phases,ref ] GC(4) Cleared: 62
[0.324s][info ][oopstorage,ref] VM Weak Oop Handles: released 0x0000018bb1d48748
[0.324s][info ][oopstorage,ref] VM Weak Oop Handles: released 0x0000018bb1d487b0
[0.324s][info ][oopstorage,ref] VM Weak Oop Handles: released 0x0000018bb1d48660
總結(jié)
1. finalize占用內(nèi)存搞這和GC的機(jī)制有關(guān)
實(shí)現(xiàn)了object的finalize()的類在創(chuàng)建時(shí)會(huì)新建一個(gè)FinalizerReference,這個(gè)對(duì)象是強(qiáng)引用類型,封裝了override finalize()的對(duì)象,下面直接叫原對(duì)象。
原對(duì)象沒(méi)有被其他對(duì)象引用時(shí)(FinalizeReference除外),執(zhí)行GC不會(huì)馬上被清除掉,而是放入一個(gè)靜態(tài)鏈表中(ReferenceQueue),有一個(gè)守護(hù)線程專門去維護(hù)這個(gè)鏈表,如何維護(hù)呢?
就是輪到該線程執(zhí)行時(shí)就彈出里面的對(duì)象,執(zhí)行它們的finalize(),對(duì)應(yīng)的FinalizerReference對(duì)象在下次執(zhí)行GC時(shí)就會(huì)被清理掉。
一個(gè)堆的FinalizerReference會(huì)組成一條雙向鏈表,垃圾回收器應(yīng)該會(huì)持有鏈表頭(鏈表頭在FinalizerReference中為一個(gè)靜態(tài)成員)。
2. 為什么會(huì)泄漏
直接原因就是守護(hù)線程優(yōu)先級(jí)比較低,運(yùn)行的時(shí)間比較少。
如果較短時(shí)間內(nèi)創(chuàng)建較多的原對(duì)象,就會(huì)因?yàn)槭刈o(hù)線程來(lái)不及彈出原對(duì)象而使FinalizerReference和原對(duì)象都得不到回收。
無(wú)論怎樣調(diào)用GC都沒(méi)有用的,因?yàn)橹灰瓕?duì)象沒(méi)有被守護(hù)線程彈出執(zhí)行其finalize()方法,F(xiàn)inalizerReference對(duì)象就不會(huì)被GC回收。
3. 知道這些機(jī)制后,應(yīng)該注意以下幾點(diǎn)
(1)緊缺資源不要依賴finalize()來(lái)釋放。
(2)盡量不要重載finalize()。
(3)如果必須重載finalize(),一定要記得調(diào)用super.finalize,也建議把類實(shí)現(xiàn)成單例模式(減少FinalizerReference占用)。
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
java socket接收保證能讀完數(shù)據(jù)的實(shí)例
這篇文章主要介紹了java socket接收保證能讀完數(shù)據(jù)的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-10-10
大數(shù)據(jù)Kafka:消息隊(duì)列和Kafka基本介紹
本文對(duì)消息隊(duì)列的應(yīng)用場(chǎng)景,優(yōu)缺點(diǎn),消息隊(duì)列的兩種方式,常見(jiàn)的消息隊(duì)列產(chǎn)品以及Kafka的特點(diǎn)和應(yīng)用場(chǎng)景做了詳細(xì)的講解,需要的朋友可以參考下,希望可以對(duì)大家有所幫助2021-08-08
Javaweb使用getPart接收表單文件過(guò)程解析
這篇文章主要介紹了Javaweb使用getPart接收表單文件過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-07-07
JAVA基于SnakeYAML實(shí)現(xiàn)解析與序列化YAML
這篇文章主要介紹了JAVA基于SnakeYAML實(shí)現(xiàn)解析與序列化YAML,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-12-12
Java實(shí)現(xiàn)獲取內(nèi)網(wǎng)的所有IP地址
這篇文章主要介紹了如何利用Java語(yǔ)言實(shí)現(xiàn)獲取內(nèi)網(wǎng)的所有IP地址,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)有一定的參考價(jià)值,快跟隨小編一起學(xué)習(xí)一下吧2022-06-06
SpringBoot項(xiàng)目中使用騰訊云發(fā)送短信的實(shí)現(xiàn)
本文主要介紹了SpringBoot項(xiàng)目中使用騰訊云發(fā)送短信的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-04-04

