Java 8 内存管理原理解析及内存故障排查实践( 三 )


文章插图
(3)复制算法
为了解决标记-清除算法面对大量可回收对象时执行效率低的问题,将存活对象复制到一块空置的空间里,然后将原来的区域全部清理,缺点是需要额外空间存放存活对象 。

Java 8 内存管理原理解析及内存故障排查实践

文章插图
2.2.4 分代垃圾回收模型概念和原理
堆内存分代模型图
 
Java 8 内存管理原理解析及内存故障排查实践

文章插图
当JVM进行GC(垃圾回收)时,JVM会发起“Stop the world”,所有的业务线程都进行停止,进入SafePoint状态 , JVM回收垃圾线程开始进行标记和追溯,如何解决这种停止和如何减少STW的时间呢?
目前主流垃圾收集器采用分代垃圾回收方式,大部分对象的声明周期都比较短,只有少部分的对象才存活的比较长,分代垃圾回收会在逻辑上把堆内存空间分为两部分 , 一部分为年轻代,一部分为老年代 。
(1)年轻代空间
年轻代主要是存放新生成的对象,一般占用堆空间的三分之一空间,因为会频繁创建对象,所以年轻代GC频率是最高的 。
分为Eden空间、Survivor1(from)区、Survivor2(to)区,S1和S2总要有一块空间是空的,为了方便年轻代存活对象来回存放 , 晋升存活对象年龄 。
三个区的默认比例是8:1:1,可以通过配置参数调整比例 。
年轻代回收发起Minor GC(YongGC),当Eden内存区域被占满之后就发起GC , 短暂的STW,基于垃圾收集器 。
(2)老年代空间
是堆内存中最大的空间,,里面的对象都是比较稳定或者老顽固,GC频率不会频繁执行 。
Java 8 内存管理原理解析及内存故障排查实践

文章插图
老年代对象:
  1. 正常提升:由年轻代存活对象年龄到达阈值时,这个对象则会被移动到老年代中 。
  2. 分配担保:如果年轻代中的空间不足时 , 此时有新的对象需要分配对象空间,需要依赖其它内存进行分配担保,老年代担保直接创建 。
  3. 大对象:当创建需要大量连续内存空间的对象时,如长字符串或者数组等,大小超过了阈值时,直接在老年代分配 。
  4. 动态年龄对象:有的垃圾收集器不需要到达指定年龄大小直接晋升老年代,比如相同年龄的对象的大小总和 > Survivor空间的50%,年龄大于等于该年龄对象直接移动老年代,无需等待正常提升 。
老年代回收发起Major GC / FULL GC , 当老年代满时会触发MajorGC,通常至少经历过一次Minor GC,再紧接着进行Major GC ,  Major GC清理Tenured区,用于回收老年代(CMS才能单独清理) 。
FUll GC:清除整个堆空间,一般来说是针对整个新生代、老生代、元空间的全局范围的清理 。
不管是Major GC还是 Full GC ,  STW的耗时都是Ygc的十倍以上,所以说对象能在年轻代被回收是最优的 。
Full GC触发条件:
  • 老年代空间不足 。
  • 元空间不足扩容导致 。
  • 程序代码执行System.gc时可能会执行 。
  • 当程序创建一个大对象时 , Eden区域放不下大对象,老年代内存担保分配,老年代也不足空间时 。
  • 年轻代存留对象晋升老年代时,老年代空间不足时 。
2.2.5 Java对象内存分配过程
Java 8 内存管理原理解析及内存故障排查实践

文章插图
 对象的分配过程
  1. 编译器通过逃逸分析优化手段,确定对象是否在栈上分配还是堆上分配 。
  2. 如果在堆上分配,则确定是否大对象 , 如果是则直接进入老年代空间分配,不然则走3 。
  3. 对比tlab,如果tlab_top + size <= tlab_end, 则在tlab上直接分配 , 并且增加tlab_top值,如果tlab不足以空间放当前对象,则重新申请一个tlab尝试放入当前对象,如果还是不行则往下走4 。
  4. 分配在Eden空间,当eden空间不足时发生YGC,幸存者区是否年龄晋升、动态年龄、老年代剩余空间不足发生Full GC。
  5. 当YGC之后仍然不足当前对象放入,则直接分配老年代 。


    推荐阅读