思考GC須要完成的3件事:java
爲何須要了解GC和內存分配?算法
不少教科書判斷對象是否存活的算法:緩存
引用計數法(Reference Counting)的實現簡單,斷定效率高,案例:併發
package com.leaf.u_jvm; /** * 引用計數算法的缺陷 * * testGC()方法執行後,objA、objB會不會被GC呢? * */ public class ReferenceCountingGC { public Object instance = null; private static final int _1MB = 1024 * 1024; /** * 這個成員屬性的惟一意義就是佔點內存,以便能在GC日誌中看清楚是否被回收過 */ private byte[] bigSize = new byte[2 * _1MB]; public static void testGC(){ ReferenceCountingGC objA = new ReferenceCountingGC(); ReferenceCountingGC objB = new ReferenceCountingGC(); objA.instance = objB; objB.instance = objA; objA = null; objB = null; //假設在這行發生GC,objA和objB是否能被回收? System.gc(); } public static void main(String[] args) { ReferenceCountingGC.testGC(); } }
[GC (System.gc()) [PSYoungGen: 5735K->824K(18944K)] 5735K->832K(62976K), 0.0012368 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [Full GC (System.gc()) [PSYoungGen: 824K->0K(18944K)] [ParOldGen: 8K->693K(44032K)] 832K->693K(62976K), [Metaspace: 2593K->2593K(1056768K)], 0.0070749 secs] [Times: user=0.06 sys=0.00, real=0.01 secs] Heap PSYoungGen total 18944K, used 164K [0x00000000eb180000, 0x00000000ec680000, 0x0000000100000000) eden space 16384K, 1% used [0x00000000eb180000,0x00000000eb1a90d0,0x00000000ec180000) from space 2560K, 0% used [0x00000000ec180000,0x00000000ec180000,0x00000000ec400000) to space 2560K, 0% used [0x00000000ec400000,0x00000000ec400000,0x00000000ec680000) ParOldGen total 44032K, used 693K [0x00000000c1400000, 0x00000000c3f00000, 0x00000000eb180000) object space 44032K, 1% used [0x00000000c1400000,0x00000000c14ad538,0x00000000c3f00000) Metaspace used 2599K, capacity 4486K, committed 4864K, reserved 1056768K class space used 286K, capacity 386K, committed 512K, reserved 1048576K
基本思路:框架
Java語言,可做爲GC Roots的對象包括:jvm
但願:當內存空間還足夠時,則能保留在內存中;若是內存空間在進行垃圾收集後仍是很是緊張,則可拋棄這些對象(緩存)
* JDK1.2以後,引用分ide
真正宣告一個對象死亡,至少要經歷兩次標記過程:高併發
若是對象在進行可達性分析後發現沒有與GC Roots相鏈接的引用鏈,那它將會被第一次標記而且進行一次篩選ui
若是對象要在finalize()中成功拯救本身this
package com.leaf.u_jvm; /** * 一次自我拯救的演示 * 1.對象能夠在被GC時自我拯救 * 2.這種自救的機會只有一次,由於對象的finalize()方法最多隻會被系統自動調用一次 * 正常運行結果: * finalize method excuted * yes, I am still alive * no, I am dead * * finalize()方法確實執行了,可是第一次拯救成功,第二次失敗了 * * * 注意:這個案例只作演示使用,切記在實際中使用,由於finalize()方法的不肯定性很大,它的優先級很低,容易受影響 * 能夠dubug看看運行結果 * * 能夠是try-finally或者其它方式 */ public class FinalizeEscapeGC { public static FinalizeEscapeGC SAVE_HOOK = null; public void isAlive(){ System.out.println("yes, I am still alive"); } @Override protected void finalize() throws Throwable { super.finalize(); System.out.println("finalize method excuted"); FinalizeEscapeGC.SAVE_HOOK = this; } public static void main(String[] args) throws Throwable { SAVE_HOOK = new FinalizeEscapeGC(); //對象第一次成功拯救本身 SAVE_HOOK = null; System.gc(); //由於finalize方法優先級很低,因此暫停0.5秒等待它 Thread.sleep(500); if(SAVE_HOOK != null){ SAVE_HOOK.isAlive(); } else { System.out.println("no, I am dead"); } //下面這段代碼和上面同樣,可是此次自救失敗了 SAVE_HOOK = null; System.gc(); //由於finalize方法優先級很低,因此暫停0.5秒等待它 Thread.sleep(500); if(SAVE_HOOK != null){ SAVE_HOOK.isAlive(); } else { System.out.println("no, I am dead"); } } }
斷定廢棄常量
斷定無用類
是否對類進行回收,HotSpot虛擬機提供了-Xnoclassgc參數進行控制
引用:《深刻理解Java虛擬機:JVM高級特性與最佳實踐(第2版)》 - 第三章