JVM 之 GC

一、堆内存图

新生区由于对象产生的比较多并且大都是朝生夕灭的,所以直接采用复制算法。

而养老区生命力很强,则采用标记-清理算法,针对不同情况使用不同算法。

二、各种GC以及触发条件

Minor GC:从年轻代回收内存

触发条件:

  1.   Eden区域满
  2. ​ 新创建的对象大小 > Eden所剩空间

对象分配原则:什么时候进老年代?

  1. 大对象(需要大量连续内存空间的对象)直接进入老年代(可以配置数值)【避免大量内存拷贝】
  2. 长期存活对象进入老年代(经历一次MinorGC年龄加一,到达阈值进入老年)
  3. 动态判断年龄(survivor中相同年龄大小总和超过survivor空间一半,大于等于年龄的对象进入老年)

=>  前提:老年有空间

=>  如果发现统计数据说之前Minor GC的平均晋升大小比目前old gen剩余的空间大,则不会触发Minor GC而是转为触发full GC

Full GC触发条件: 整个堆内存的回收

(1)System.gc()方法的调用

此方法的调用是建议JVM进行Full GC,虽然只是建议而非一定,但很多情况下它会触发 Full GC,从而增加Full GC的频率,也即增加了间歇性停顿的次数。强烈影响系建议能不使用此方法就别使用,让虚拟机自己去管理它的内存,可通过通过-XX:+ DisableExplicitGC来禁止RMI(Java远程方法调用)调用System.gc。

(2)老年代空间不足

旧生代空间只有在新生代对象转入及创建为大对象、大数组时才会出现不足的现象,当执行Full GC后空间仍然不足,则抛出如下错误: java.lang.OutOfMemoryError: Java heap space 为避免以上两种状况引起的FullGC,调优时应尽量做到让对象在Minor GC阶段被回收、让对象在新生代多存活一段时间及不要创建过大的对象及数组。

(3)方法区/永生区空间不足

JVM规范中运行时数据区域中的方法区,在HotSpot虚拟机中又被习惯称为永生代或者永生区,Permanet Generation中存放的为一些class的信息、常量、静态变量等数据,当系统中要加载的类、反射的类和调用的方法较多时,Permanet Generation可能会被占满,在未配置为采用CMS GC的情况下也会执行Full GC。如果经过Full GC仍然回收不了,那么JVM会抛出如下错误信息:
java.lang.OutOfMemoryError: PermGen space
为避免Perm Gen占满造成Full GC现象,可采用的方法为增大Perm Gen空间或转为使用CMS GC。

(4)历来通过Minor GC后进入老年代的平均大小大于老年代的可用内存

如果发现统计数据说之前Minor GC的平均晋升大小比目前old gen剩余的空间大,则不会触发Minor GC而是转为触发full GC

(5)由Eden区、From Space区向To Space区复制时,对象大小大于To Space可用内存,则把该对象转存到老年代,且老年代的可用内存小于该对象大小

三、什么是"Stop-The-World"?

  1. 在垃圾回收过程中经常涉及到对对象的挪动,进而导致需要对对象引用进行更新。为了保证引用更新的正确性,Java将暂停所有其他的线程,这种情况被称为“Stop-The-World”,导致系统全局停顿。
  2. Stop-The-World对系统性能存在影响,因此垃圾回收的一个原则是尽量减少“Stop-The-World”的时间。
  3. 所有垃圾回收器的所有垃圾回收策略都会触发STW,只是时间长短不同而已。

如何让STW时间短?=> FC次数少 => 老生代更晚到达阈值=> 年龄大再进老生代 (长期存活对象进入老年代)

JVM调优是为了调优gc,主要就是两个点,一个是减少Full gc的次数,一个是缩短一次Full gc的时间

调优案例:https://blog.csdn.net/weixin_44704538/article/details/108222022
原文地址:https://www.cnblogs.com/sabertobih/p/13813347.html