-
什么时候会发生 GC
深入理解 Java 虚拟机 3.5 内存分配与回收策略
- 大多数情况下,对象在新生代 Eden 区中分配,当 Eden 区没有足够的空间进行分配时,虚拟机将发起一次 Minor GC
- 在发生 Minor GC 时,虚拟机会检测之前每次晋升到老年代的平均大小是否大于老年代的剩余空间大小,如果大于,则改为直接进行一次 Full GC;如果小于,则查看
HandlePromotionFailure
设置是否允许担保失败,如果允许则只会进行 Minor GC,否则进行 Full GC
-
什么时候会发生 OOM (JDK1.8)
-
堆内存溢出
java.lang.OutOfMemoryError: Java heap space
- 指定的对内存无法满足应用需求
- 参数:
-Xms
-Xmx
-
GC效率不高
java.lang.OutOfMemoryError: GC Overhead limit exceeded
- GC 始终在运行,当 Java 进程花费了超过 98% 的时间来进行 GC ,却只恢复了不到 2% 的堆内存,且最近 5 次连续的 GC 都发生这种情况,则抛出该异常
- 通常是总的活动数据勉强能放入堆内,无法分配新的空间导致的
- 需要增大堆内存
- 可通过参数
-XX: -UseGCOverheadLimit
来关闭改异常
-
请求数组大小超过 VM 限制
java.lang.OutOfMemoryError: Requested array size exceeds VM limit
- 应用尝试分配一个大于堆大小的数组时抛出改异常
- 通常是配置问题,或应用逻辑问题
-
元数据区耗尽
java.lang.OutOfMemoryError: Metaspace
- metadata 在本地内存中分配,当 class 的元数据区耗尽时抛出
- 元数据区与堆内存分配自同一个内存空间,减少堆内存可以给元数据留有更多空间;需要权衡
- 参数:
MaxMetaSpaceSize
-
向本地堆申请空间失败
java.lang.OutOfMemoryError: request size bytes for reason. Out of swap space?
- 抛出该错误消息时,虚拟机触发致命错误处理机制(产生log文件,包含线程、进程等有用信息)
- 需要分析操作系统来诊断问题(本地工具)
-
java.lang.OutOfMemoryError: Compressed class space
- 当时用了
UseCompressedClassPointers
选项时,类元数据空间的大小是固定的(CompressedClassSpaceSize
),当请求空间超出该固定值,则抛出异常 - 可增加
CompressedClassSpaceSize
来关闭UseCompressedClassPointers
;但该值也有边界范围 1048576 - 3221225472
- 当时用了
-
本地方法分配内存失败
java.lang.OutOfMemoryError: reason stack_trace_with_native_method
- 需使用系统本地工具来进一步分析问题
-
-
如何判断一个对象是否存活
- 根据已知的存活对象(根对象)通过对象的引用链,将每个根对象可达的对象标记为存活对象
- 在并发标记清理法中,存在两次标记行为/暂停行为,第二次标记是因为在第一次标记过程中对象状态发生变化,确保这部分对象也被正确处理
-
GC 算法
-
CMS 过程
- 并发标记清理法
- 响应时间优先回收器(程序运行的同时,使用单独的垃圾回收线程跟踪存活对象来减少暂停时间)
- 适用于有大量长期存活数据、且运行于多个处理器上的应用
- 参数选项
-XX:+UseConcMarkSweepGC
- 使用标记清除算法,基于分代回收,在回收期间 minor 和 major GC 都会发生
- 每次在 major 阶段时, CMS 在收集前和收集中都会短暂停止所有应用线程
- 2次暂停(初始标记暂停、再标记暂停),第 2 次时间更长
- 每次暂停有多个线程来进行收集工作
- minor 阶段可穿插在 major 阶段,类似于并行收集器,通常在收集时会暂停应用线程
- 每次在 major 阶段时, CMS 在收集前和收集中都会短暂停止所有应用线程
- 几个过程
- 初始标记,停止所有应用线程,标识可从根对象访问的对象集合,恢复应用线程
- 并发标记,并发跟踪可达对象图,使用一到多个处理器,应用线程正常运行
- 重新标记
- 并发重新跟踪再上一次跟踪过程中被修改的对象图区域,使用一个处理器
- 停止所有应用线程,再次跟踪跟对象和对象图中可能在最后一次检查中被修改的部分,恢复应用线程
- 并发清除,并发清理所有不可达对象,使用一个处理器
- 并发重置堆大小,准备下一次收集的数据结构,使用一个处理器
-
并发模式失败
- 即无法完成并发收集,此时需要调整 CMS 参数
- 当 CMS 收集器在老年代填满之前还未完成对象回收,或无法在老年代分配可用空间时,那么应用会暂停,所有应用线程停止垃圾回收结束
- 显式 GC 或诊断工具触发 GC 导致并发收集被中断,也会报告并发模式中断
- 并发跟踪阶段,如果计算资源有限的情况下,也会影响系统的吞吐量
- 一次收集循环结束后,收集器等待下一次收集循环,期间几乎不占用计算资源
-
浮动垃圾
- major 阶段应用线程和收集线程并发运行,被收集线程跟踪的对象可能在收集进程结束时变的不可达,这些对象称为浮动垃圾
- 尝试调整老年代大小,增加 20% 用于浮动垃圾
- 一次收集结束后留下的浮动垃圾,会在下一次处理
-
-XX:CMSInitiatingOccupancyFraction
设置当老年代占用多少后,激活 CMS 收集器,默认68%
-
何时会触发
- 动态预估,基于最近的历史数据,预测老年代多久被耗尽以及需要进行收集的时间
- 老年代达到一定大小,默认值
92%
;调节参数-XX:CMSInitiatingOccupancyFraction=<N>
, 0-100 的整数
-
计划中断
- 年轻代和老年代回收独立发生,不会交叠,但可能会快速连续发生,会出现单个长久的暂停
- CMS 通过在两次年轻代中间安排一次老年代收集来解决
-
i-cms
渐进模式 / 增量式并发收集器 (incremental concurrent mark sweeep)- 1.8 中已过时
- 通过周期性停止并发阶段释放处理器资源来减轻长时间的并发
- 将任务分为小块时间穿插在年轻代回收之间,自动计算作业周期
- 对于运行在较少处理器的机器上且需要低暂停时间的应用来说比较有用
- 参数
-
-XX:+UseConcMarkSweepGC -XX:+CMSIncrementalMode
启用 CMS 和 i-cms -
-XX:+PrintGCDetails -XX:+PrintGCTimeStamps
打印诊断信息
-
-
CMS 产生的内存碎片如何控制
- CMS 基于标记-清理算法,而该算法有一个空间问题,即标记清理之后会产生大量不连续的内存碎片,如果在后续需要为大对象分配内存则无法满足,会促发另一次垃圾收集
-
-XX:+UseCMSCompactAtFullCollection
开关,在 Full GC 之后进行碎片整理过程(停顿时间变长) -
-XX:CMSFullGCsBeforeCompaction
设置在多少次不压缩的 Full GC 之后执行一次带压缩的
参考
Concurrent Mark Sweep (CMS) Collector
Memory Management in the Java HotSpot Virtual Machine- 深入理解 Java 虚拟机 3 垃圾收集器与内存分配策略
-
网友评论