思考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版)》 - 第三章