Java的内存分配与回收全部由JVM垃圾回收进程自动完成。与C语言不同,Java开发者不需要自己编写代码实现垃圾回收。这是Java深受大家欢迎的众多特性之一,能够帮助程序员更好地编写Java程序。

Java关键术语

JavaAPI:一系列帮助开发者创建Java应用程序的封装好的库。

Java开发工具包(JDK):一系列工具帮助开发者创建Java应用程序。JDK包含工具编译、运行、打包、分发和监视Java应用程序。

Java虚拟机(JVM):JVM是一个抽象的计算机结构。Java程序根据JVM的特性编写。JVM针对特定于操作系统并且可以将Java指令翻译成底层系统的指令并执行。JVM确保了Java的平台无关性。

Java运行环境(JRE):JRE包含JVM实现和JavaAPI。

JVM体系结构

下面图片总结了JVM的关键组件。在JVM体系结构中,与垃圾回收相关的两个主要组件是堆内存和垃圾回收器。堆内存是内存数据区,用来保存运行时的对象实例。垃圾回收器也会在这里操作。现在我们知道这些组件是如何在框架中工作的。

 

 

 

Java堆内存

堆主要用于存放各种类的实例对象和数组。在java中被分为两个区域:年轻代和老年代。

 

新建的对象会存活在在Eden中。Eden区如果没有足够的空间时会引发一次young区的GC。

在经历一次MinorGC之后,Eden中的存活对象就会被移动到第一块survivor space-S0,此时Eden被清空;

等Eden区再次填满,就再触发一次Minor GC,Eden和S0中的存活对象又会被复制送入第二块survivor space-s1;此时S0和Eden被清空,然后下一轮S0与S1交换角色。 如果 Survivor的空间不足或经历16次Minor GC还能在新生代中存活的对象会通过分配担保机制被送入老年代。

老年代负责分配担保让Survivor无法容纳的对象直接进入老年代。如果剩余空间小于转移对象大小,将直接进行 FullGc

进入老年代的对象

  • 大对象会直接进入老年代(避免频繁复制)
  • 在程序中长期持有了对象的引用(对象年龄达到指定阈值也会进入老年代)
  • survivor 区太小,只能进入老年代

FullGC

执行 Minor GC(年轻代GC) 的时候,JVM 会检查老年代中最大连续可用空间是否大于了当前新生代所有对象的总大小 如果大于,则直接执行 Minor GC(年轻代GC)(这个时候执行是没有风险的) 如果小于,JVM 会检查是否开启了空间分配担保机制,如果没有开启则直接改为执行Full GC 如果开启担保机制,则 JVM 会检查老年代中最大连续可用空间是否大于历次晋升到老年代中的平均大小,如果小于则执行改为执行Full GC 如果大于则会执行 Minor GC(年轻代GC),如果 Minor GC(年轻代GC) 执行失败则会执行 Full GC

出现Full GC的时候经常伴随至少一次的Minor GC,但不绝对。Major GC的速度一般会比Minor GC慢10倍以上

内存溢出

老年代只有在新生代对象转入及创建大对象、大数组时才会出现空间不足的现象。当执行Full GC后空间仍然不足,则会抛出如下错误: java.lang.OutOfMemoryError: Java heap space

full GC频繁出现的原因

  • 对象引用长期未释放
  • survivor 区太小
  • old 区太小

Full GC调优办法

1:让对象在Minor GC阶段被回收、让对象在新生代多存活一段时间及不要创建过大的对象及数组

2:年轻代小对象尽量多,大对象则尽可能直接进入老年代。年轻代由于使用标记复制算法进行回收内存,速度很快

3:Eden区如果没有足够的空间时会引发一次young区的GC,通过-XX:SurvivorRatio 进行调整 Eden 和 Survivor 比例大小。少量对象的存活,适合复制算法(年轻代),大量对象存活,适合标记清理或者标记压缩(年老代)。

 

 

FullGC 的执行

  1. 执行 Minor GC 的时候,JVM 会检查老年代中最大连续可用空间是否大于了当前新生代所有对象的总大小
  2. 如果大于,则直接执行 Minor GC(这个时候执行是没有风险的)
  3. 如果小于了,JVM 会检查是否开启了空间分配担保机制,如果没有开启则直接改为执行 Full GC
  4. 如果开启了,则 JVM 会检查老年代中最大连续可用空间是否大于历次晋升到老年代中的平均大小,如果小于则执行改为执行 Full GC
  5. 如果大于则会执行 Minor GC,如果 Minor GC 执行失败则会执行 Full GC

进入老年代的对象

  1. 大对象会直接进入老年代(避免频繁复制)
  2. 在程序中长期持有了对象的引用(对象年龄达到指定阈值也会进入老年代)
  3. survivor 区太小,只能进入老年代

频繁出现排查方法

开启 -XX:+HeapDumpBeforeFullGC
用 Java VisualVM 工具分析

频繁出现可能原因

  1. 对象引用长期未释放
  2. survivor 区太小
  3. old 区太小