内存回收与分配重点关注的是堆内存和方法区内存(程序计数器占用小,虚拟机栈和本地方法栈随线程有相同的生命周期)。
优势:实现简单,效率高。 致命缺陷:无法解决对象相互引用的问题——会导致对象的引用虽然存在,但是已经不可能再被使用,却无法被回收。
对象到 GC Roots 没有引用链,则回收。
GC Roots 包括:
JDK1.2 之后,Java 对引用进行了扩充。
在对象被 JVM 回收之前,有一个低优先级的线程去执行。只有覆盖了 finalize 方法,才会执行,且只会执行一次。
永久代的垃圾收集主要回收两部分内容:废弃常量和无用的类。
将内存分为两个半区,将区 A 中的存活对象全部复制到 B 区的连续空间,然后清理 A 中所有空间。
缺点:内存实际空间减半。在对象存活率较高时需要进行较多的复制操作。
实际应用:将堆内存分为新生代和老年代。由于新生代中的对象 98% 都是可回收的,故将新生代又划分为 Eden 空间和两块较小的 Survivor 空间,默认 Eden:Survivor(单个)=8:1
这样每次垃圾收集时,将 Eden 和 S1 中的存活对象复制到 S2,然后清空 Eden 和 S1 区。
为了防止 S2 中空间不足以存储 Eden 和 S1 的所有剩余存活对象,提供老年代作为保障(Handle Promotion:分配担保)。
将标记后的存活对象进行移动,清除剩余对象。
应用:老年代
垃圾收集中的并行与并发:
单线程、Stop the World.
Client 模式下的默认新生代收集器(一个 Client 分配给 JVM 管理的内存一般不会很大,收集时间一般很快)。简单高效(相对于其他单线程收集器)
主要参数:
-XX:SurvivoRatio:新生代中 Eden 区域和 Survivor 区域(单个 Survivor)的容量比值,默认为 8
-XX:PretenureSizeThreshold:直接晋升到老年代的对象大小,大于该值的对象直接在老年代分配。
-XX:HandlePromotionFailure:允许老年代分配担保失败,开启后可以冒险 YGC。
Serial 的多线程版本(多条垃圾收集线程)。
Server 模式下首选的新生代收集器。
除 Serial 外,只有它能与 CMS 收集器配合工作。
新生代、复制算法、多线程。
注重吞吐量,适合后台进程。
-XX:MacGCPauseMillis:最大垃圾收集停顿时间
-XX:GCTimeRatio:吞吐量大小
GCTimeRatio=99,意味着允许最大垃圾收集时间占比为 1/(1+99)=1%,GCTimeRatio = 用户代码运行时间 / GC 时间。
-XX:+UseAdaptiveSizePolicy:动态自适应调整 JVM 参数(-Xmn、SurvivorRatio 等)
Serial 的老年代版本,使用 "标准—整理" 算法,适合 client 端。
Parallel Scavenge 的老年代版本
老年代、GC 系统停顿时间最短
"标记 - 清除"
四个阶段:
(1)(3):stop the world
(2)(4):与用户线程并发
缺陷:
最前沿成果。削弱新生代与老年代概念,将整个堆划分为独立的不同 Region。根据各 Region 的回收价值,确定优先列表。
从整体来看:"标记 - 整理" 算法
从局部(两个 Region 之间)来看:"复制" 算法
如果 Eden 区满,则触发一次 Minor GC(也称 Young GC)
-XX:+PrintGCDetails
在 JDK8 中,PermGen(永久代)被 Metaspace(元空间)取代了。
-XX:PretenureSizeThreshold:直接进入老年代的对象大小
-XX:MaxTenuringThreshold:设置对象在新生代中能存活的最大年龄,默认 15
-XX:+PrintTenuringDistribution:打印老年代内的各年龄对象内存分配情况
若 Survivor 中相同年龄的所有对象大小总和超过 Survivor 的一半,则年龄大于或等于该年龄的对象就可以直接进入老年代。
- /**
- * @author zni.feng
- */
- import java.lang.management.ManagementFactory;
- public class YGCTest {
- /**
- * VM参数: -verbose:gc -XX:+UseSerialGC -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8
- */
- private static final int _1MB = 1024 * 1024;
- public static void main(String[] args) {
- System.out.println(ManagementFactory.getRuntimeMXBean().getInputArguments()); //打印JVM参数
- byte[] allocation1,
- allocation2,
- allocation3,
- allocation4;
- allocation1 = new byte[2 * _1MB];
- allocation2 = new byte[2 * _1MB];
- allocation3 = new byte[2 * _1MB];
- allocation4 = new byte[4 * _1MB]; //出现一次YGC
- System.out.println("exit");
- }
- }
测试结果:
- [ - verbose: gc, -XX: +UseSerialGC, -Xms20M, -Xmx20M, -Xmn10M, -XX: +PrintGCDetails, -XX: SurvivorRatio = 8, -Dfile.encoding = UTF - 8][GC(Allocation Failure)[DefNew: 6815K - >282K(9216K), 0.0077121 secs] 6815K - >6426K(19456K), 0.0077785 secs][Times: user = 0.01 sys = 0.01, real = 0.01 secs] exit Heap def new generation total 9216K,
- used 4620K[0x00000007bec00000, 0x00000007bf600000, 0x00000007bf600000) eden space 8192K,
- 52 % used[0x00000007bec00000, 0x00000007bf03c8d8, 0x00000007bf400000) from space 1024K,
- 27 % used[0x00000007bf500000, 0x00000007bf546800, 0x00000007bf600000) to space 1024K,
- 0 % used[0x00000007bf400000, 0x00000007bf400000, 0x00000007bf500000) tenured generation total 10240K,
- used 6144K[0x00000007bf600000, 0x00000007c0000000, 0x00000007c0000000) the space 10240K,
- 60 % used[0x00000007bf600000, 0x00000007bfc00030, 0x00000007bfc00200, 0x00000007c0000000) Metaspace used 2695K,
- capacity 4486K,
- committed 4864K,
- reserved 1056768K class space used 294K,
- capacity 386K,
- committed 512K,
- reserved 1048576K
测试结果分析:
从结果可以看到,发生了一次 YGC,GC 后新生代(DefNew:Default New Generaion)从 6815K 降到了 282K,即基本上全部回收,而整个堆内存的大小并没有显著减小(从 6815K 降到了 6426K),这是因为 allocation1、allocation2、allocation3 对象仍存在(强引用),并没有被真正回收,只是从新生代进入了老年代(Survivor 区不足以容纳 6M 的对象)。并且,从程序结束时的各区域内存分配情况可以看到,老年代占用了约 6M(tenured generation total 10240K,used 6144K);新生代 Eden 区占用了 52%,约 4M(total 8192K,即 8M),是用来存放 allocation4 的对象。
- /**
- * @author zni.feng
- */
- public classPretenureSizeThresholdTest {/**
- * VM参数: -verbose:gc -XX:+UseSerialGC -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8
- * -XX:PretenureSizeThreshold=3145728
- * (3145728=3*1024*1024=3MB)
- */
- private static final int_1MB=1024*1024;public static void main(String[] args) {byte[] allocation;
- allocation =new byte[4*_1MB];
- }
- }
测试结果:
- Heap def new generation total 9216K,
- used 835K[0x00000007bec00000, 0x00000007bf600000, 0x00000007bf600000) eden space 8192K,
- 10 % used[0x00000007bec00000, 0x00000007becd0f90, 0x00000007bf400000) from space 1024K,
- 0 % used[0x00000007bf400000, 0x00000007bf400000, 0x00000007bf500000) to space 1024K,
- 0 % used[0x00000007bf500000, 0x00000007bf500000, 0x00000007bf600000) tenured generation total 10240K,
- used 4096K[0x00000007bf600000, 0x00000007c0000000, 0x00000007c0000000) the space 10240K,
- 40 % used[0x00000007bf600000, 0x00000007bfa00010, 0x00000007bfa00200, 0x00000007c0000000) Metaspace used 2621K,
- capacity 4486K,
- committed 4864K,
- reserved 1056768K class space used 286K,
- capacity 386K,
- committed 512K,
- reserved 1048576K
测试结果分析:可以看到,allocation 对象直接进入了老年代。
来源: http://www.cnblogs.com/znicy/p/6918767.html