🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
### 相关概念 * Minor GC:新生代GC,指发生在新生代空间(包括Eden和Survivor区域)的垃圾收集动作,所有的Minor GC都会触发STW,停止应用程序的线程,不过这个过程非常短暂; * Major GC:针对所有分代区域(新生代、年老代)的GC; * Full GC:而Full GC是对整个堆来说的,在最近几个版本的JDK里默认包括了对永久代\(Hotspot GC覆盖了永久代--在未配置为采用CMS GC的情况下也会执行Full GC\)即方法区的回收(JDK8移除了PermGen); 我们可以认为Major GC == Full GC,他们是一个概念,就是针对老年代/永久代进行GC。因为取名叫Full就会让人疑惑,到底会不会先Minor GC。事实上Full GC本身不会先进行Minor GC,我们可以配置,让Full GC之前先进行一次Minor GC,因为老年代很多对象都会引用到新生代的对象,先进行一次Minor GC可以提高老年代GC的速度。比如老年代使用CMS时,设置CMSScavengeBeforeRemark优化,让CMS remark之前先进行一次Minor GC; 虚拟机给每个对象定义了一个对象年龄(Age)计数器。如果对象在 Eden 出生并经过第一次 Minor GC 后仍然存活,并且能被 Survivor 容纳的话,将被移动到 Survivor 空间中,并将对象年龄设为 1。对象在 Survivor 区中每熬过一次 Minor GC,年龄就增加 1 岁,当它的年龄增加到一定程度(默认为 15 岁,CMS GC默认值为6)时,就会被晋升到老年代中。对象晋升老年代的年龄阈值,可以通过参数 -XX:MaxTenuringThreshold 来设置; 特性: * 出现Full GC的时候经常伴随至少一次的Minor GC,但非绝对的; * Major GC的速度一般会比Minor GC慢10倍以上; #### Minor GC 新生代GC:指发生在新生代的垃圾收集动作,因为 Java 对象大多都具备朝生夕灭的特性,所以 Minor GC非常频繁,一般回收速度也比较快,因此基于Copying算法来进行回收,所谓Copying算法就是扫描出存活的对象,并复制到一块新的完全未使用的空间中,对应于新生代就是在Eden和FromSpace或ToSpace之间copy。新生代采用空闲指针的方式来控制GC触发,指针保持最后一个分配的对象在新生代区间的位置,当有新的对象要分配内存时,用于检查空间是否足够,不够就触发GC #### 老年代内存溢出 老年代空间只有在新生代对象转入及创建为大对象、大数组时才会出现不足的现象,当执行Full GC后空间仍然不足,则抛出如下错误:java.lang.OutOfMemoryError: Java heap space 为避免以上两种状况引起的Full GC,调优时应尽量做到让对象在Minor GC阶段被回收、让对象在新生代多存活一段时间及不要创建过大的对象及数组 #### 永久代内存溢出 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 #### CMS GC时出现promotion failed和concurrent mode failure 对于采用CMS进行老年代GC的程序而言,尤其要注意GC日志中是否有promotion failed和concurrent mode failure两种状况,当这两种状况出现时可能会触发Full GC。 * promotion failed是在进行Minor GC时,survivor space放不下、对象只能放入老年代,而此时老年代也放不下造成的; * concurrent mode failure是在执行CMS GC的过程中同时有对象要放入老年代,而此时老年代空间不足造成的(有时候“空间不足”是CMS GC时当前的浮动垃圾过多导致暂时性的空间不足触发Full GC)。 应对措施:增大survivor space、老年代空间或调低触发并发GC的比率 #### 堆中分配很大的对象 所谓大对象,是指需要大量连续内存空间的java对象,例如很长的数组,此种对象会直接进入老年代,而老年代虽然有很大的剩余空间,但是无法找到足够大的连续空间来分配给当前对象,此种情况就会触发JVM进行Full GC