- JVM的内存区域中, 程序计数器 、 虚拟机栈 、 本地方法栈 是 线程私有 ,随线程的创建而创建,销毁而销毁
- 栈中的栈帧随着方法的进入和退出进行 入栈 和 出栈 操作,每个栈帧分配多少内存基本是在 类结构 确定下来时就已知
- 因此,这三个区域的内存 分配 和 回收 都是具有 确定性 的
- 堆 中的回收主要是 对象 回收, 方法区 的回收主要是 废弃常量 和 无用类 的回收
回收时机
- 当一个对象 不再被引用 ,就代表该对象 可以被回收
- 引用计数法 :实现简单,判断效率高,但存在 循环引用 的问题
- 可达性分析算法 :HotSpot VM
引用类型 |
功能特点 |
强引用 (Strong Reference) |
被强引用关联的对象, 永远 不会被垃圾回收器回收 |
软引用 (Soft Reference) |
被软引用关联的对象,只有当系统将要发生 内存溢出 时,才会去回收软引用关联的对象 |
弱引用 (Weak Reference) |
只 被弱引用关联的对象, 只要发生GC事件 ,就会被回收 |
虚引用 (Phantom Reference) |
被虚引用关联的对象, 唯一作用 是在这个对象被回收时收到一个 系统通知 |
回收特性
- 自动性
- Java提供了一个 系统级 的线程来 跟踪 每一块分配出去的内存空间
- 当JVM处于 空闲循环 时,GC线程会自动检查每一块分配出去的内存空间,然后自动回收每一块空闲的内存块
- 不可预期性
- 很难确定一个没有被引用的对象是否会被立即回收,有可能当程序结束时,该对象仍在内存中
- GC线程在JVM中是 自动执行 的, Java程序无法强制执行 ,
System.gc
也只是 建议 执行垃圾回收
GC算法
JVM提供了不同的GC算法来实现上面的GC机制
GC算法类型 |
优点 |
缺点 |
Mark-Sweep |
不需要移动对象,简单高效 |
效率低 、产生 内存碎片 |
Copying |
简单高效,不会产生内存碎片 |
内存使用率低 ,有可能 频繁复制 |
Mark-Compact |
综合了前两种算法的优点 |
仍需要 移动局部对象 |
垃圾回收器
分类
组合
GC性能指标
- 吞吐量
- 吞吐量 = 应用程序耗时 / (应用程序耗时 + GC耗时)
- 吞吐量一般 不能低于95%
- 停顿时间
- 对于串行回收器来说,停顿时间可能会比较长
- 而使用 并发 回收器,由于垃圾收集器和应用程序 交替运行 ,程序的 停顿时间会变短
- 但 效率 很可能 不如独占垃圾收集器 , 吞吐量 也可能 降低
- GC频率
- 增大 堆内存 空间可以有效降低GC频率,但也意味着堆积的回收对象越多,会增加回收时的停顿时间
- 可以适当地加大堆内存空间,保证正常的GC频率即可
GC日志分析
- JVM参数
-XX:+PrintGC
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-XX:+PrintGCDateStamps
-XX:+PrintHeapAtGC
-Xloggc:../logs/gc.log
- 分析工具
GC调优策略
降低Minor GC频率
- 由于新生代空间较小,Eden区很快被填满,就会导致频繁Minor GC,可以通过 增大新生代空间 来 降低Minor GC频率
- 单次Minor GC时间由两部分组成:T1( 扫描新生代 )+ T2( 复制存活对象 )
- 假设一个对象在Eden区的存活时间为500ms,Minor GC的时间间隔为300ms
- 由于该对象依然存活,Minor GC的时间为 T1+T2
- 如果增大新生代空间,Minor GC的时间间隔扩大到600ms
- 此时存活时间为500ms的对象会在Eden区被回收, 不存在复制存活对象 ,此时Minor GC时间为 2T1
- 新生代扩容后,Minor GC增加了T1,但减少了T2,通常在JVM中, T2远大于T1
- 小结
- 如果堆内存中 长期对象很多 ,扩容新生代,反而会增加Minor GC的时间
- 如果堆内存中 短期对象很多 ,扩容新生代, 单次Minor GC的时间不会显著增加
- 单次Minor GC的时间更多地取决于 GC后存活对象的数量 ,而非Eden区的大小
原文
http://zhongmingmao.me/2019/09/11/java-performance-gc/