java GC原理

doubleGC

记录一下

GC工作机制

GC为Garbage Collection,垃圾回收,在 JVM 区域总体分两类,heap 区和非 heap 区,不是很了解各种回收算法,可以通过点击当前链接查看

heap

heap区即堆内存,整个堆大小=年轻代大小 + 老年代大小。堆内存默认是物理内存的 1/64(<1GB),默认空余堆内存小于 40%时,JVM就会增大堆知道 -Xmx的最大限制,可以通过 MinHeapFreeRatio 参数进行调整,默认空余堆内存大于 70% 时,JVM 会减少堆直到 -Xms 的最小限制,可以通过 MaxHeapFreeRatio 参数进行调整。

  • Eden Space(新生园)
  • Survivor Space(幸存者区)
  • Old Gen(老年代)

Eden Space: 字面意思是伊甸园,对象被创建的时候首先放到这个区域,进行垃圾回收后,不能被回收的对象被放入到空的 survivor 区域。
Survivor Space: 幸存者区用于保存在 eden space 内存区域中经过垃圾回收后没有被回收的对象。Survivor 有两个,分别为 To Survivor、From Survivor,这两个区域的空间大小是一样的。执行垃圾回收的时候 Eden 区域不能被回收的对象被放入到空的 survivor(也就是To Survivor,同时 Eden 区域的内存会在内存回收的过程中全部释放),另一个 survivor(即 From Survivor)里不能被回收的对象也会被放入这个 survivor (即To Survivor),然后 To Survivor 和 From Survivor 的标记会互换,始终保证一个 survivor 是空的。

新生代

Eden Space 和 Survivor Space 都属于新生代,新生代中执行的垃圾回收被称为 Minor GC(因为是对新生代进行垃圾回收,所以又称为 Young GC),每一次 Yong GC 后留下来的对象 age 加1。

老年代

Old Gen 老年代用于存放新生代中经过多次垃圾回收仍然存活的对象,也有可能是新生代分配不了内存的大对象会直接进入老年代,以及经过多次垃圾回收都没有被回收的对象,这些对象的年代以及足够 old 了,就会放入到老年代。

当老年代被放满之后,虚拟机会进行垃圾回收,称之为 Major GC。由于 Major GC 除并发 GC 外均需对整个堆进行扫描和回收,因此又称为 Full GC。

younggeneration.png

非heap区

  • Code Cache(代码缓冲区): 它主用用于存放JIT所编译的代码
  • Perm Gen(永久代, 方法区): 该内存区域用于存放 class 和 Meta 的信息,Class 在被 Load 的时候被放入这个区域
  • Jvm Statck(java虚拟机栈)
  • Local Method Statack(本地方法栈)

JVM 内存分配图

jvmmodel.png

  • 程序计数器: 线程私有,是一块较小的内存,是当前线程所执行的字节码的行号指示器。是Java虚拟机规范中唯一没有规定OOM(OutOfMemoryError)的区域。
  • java栈:线程私有,生命周期和线程相同,是java方法执行的内存模型,执行每个方法都会创建一个栈帧,用于存储局部变量和操作数(对象引用)可用理解为压栈。局部变量所需要的内存空间大小在编译期间完成分配,所以栈帧的大小不会改变。存在两种异常情况:
    • 若线程请求深度大于栈的深度,抛出 StackOverFlowError
    • 若栈在动态扩展时无法请求足够内存则抛出 OOM
  • java堆:所有线程共享,虚拟机启动时创建存放对象实例和数组,所占内存最大,分为新生代(Young区)、老年代(Old区),新生代划分Eden区、Servior区,Servior区又划分为 From space 区和 to Space 区,Eden 区和 Servior 区的内存比为 8:1,当扩展内存大于可用内存抛出OOM。
  • 方法区:所有线程共享,用于存储已被虚拟机加载的类信息、常量、静态变量等数据。又称为非堆(Non - Heap),方法区又称"永久代",GC很少在这个区域进行,但不代表不会回收。这个区域回收目标主要是针对常量池的回收和对类型的卸载,当内存申请大于实际可用内存抛出 OOM。
  • 本地方法栈:线程私有,与Java栈类似,但不是为Java方法(字节码)服务,而是为本地非Java方法服务,也会抛 StackOverflowError 和 OOM。

Minor GC、Full GC 触发条件

Minor GC 触发条件:
当 Eden 区满时触发 Minor GC

Full GC触发条件:

  • 1)、调用 System.gc 时,系统建议执行 Full GC,但是不必然执行
  • 2)、老年代空间不足
  • 3)、方法区空间不足
  • 4)、通过 Minor GC 进入老年代的平均大小大于老年代的可用内存
  • 5)、由 Eden 区、From Space 区向 To Space 区复制时,对象大小大于 To Space 可用内存,则把该对象转存到老年代,且老年代的可用内存小于该对象大小

1、获取内存详情:

jmap -dump:format=b,file=e.bin pid

这种方式可以用 jvisualvm.exe 进行内存分析,或者采用 Eclipse Memory Analysis Tools(MAT)这个工具

2、获取内存dump:

jmap -histo:live pid

这种方式会先触发 fullgc,如果不希望触发 fullgc 可以使用 jmap -hiso pid

3、第三种方式: jdk 启动加参数

-XX:+HeapDumpBeforeFullGC
-XX:HeapDumpPath=/httx/logs/dump

这种方式会产生 dump 日志,在通过 jvisualvm.exe 或者 Eclipse Memory Analysis Tools 工具进行分析

扩展阅读