GC 算法类型

GC复制算法和标记-压缩算法的疑问:https://hllvm-group.iteye.com/group/topic/28594

复制收集:扫描对象,如果判断这个对象是存活的,那么将这个对象复制到一个空闲位置。这个算法需要提前预留空闲内存,因为复制的时候需要确保目的内存是空闲的,由于是对一个个对象进行扫描,没办法判断目的内存是否空闲。
标记压缩:扫描整个回收的区域,在回收前先标记所有存活的对象,将存活的对象一个个复制到一片连续的内存区域。这种情况下就不需要预留空闲内存,因为对象的存活状态都是已知的,不存活的对象可以直接覆盖。

特点

  1. 将内存分为多个小块区域,以区域作为粒度来划定 Eden、Survivor、Old。区域的大小是 1MBi ~ 32MBi,默认的区域数量是 2048 左右。
  2. 包含一个独特的 Mixed GC,既回收年轻代也回收老年代。
  3. 回收内存时会考虑 GC 停顿时间,按最有回收价值的区域按顺序回收。

怎么分配内存的

将内存分为多个小块区域,以区域作为粒度来划定 Eden、Survivor、Old。区域的大小是 1MBi ~ 32MBi,默认的区域数量是 2048 左右。

当某个对象的大小超过区域 size 的一半,那么会给这个对象分配专用的连续的 region 来存储它(Humongous Object)。

怎么回收内存的?

Young GC(YGC)

只回收年轻代,使用复制收集算法。

当 Eden 区满了之后触发。

将存活对象复制到空闲区域,并将空闲区域设置为 Survivor.

Mixed GC(OGC, 实际是 Old 区的回收)

当 Old 区使用量的占整个堆的比例为 -XX:InitiatingHeapOccupancyPercent 时,并且进入 YGC,那么会进行 Mixed GC。

此时的 YGC 会执行初始标记(initial marking),YGC 后存活对象都在 survivor 中,所以接着的并发标记(concurrent marking)只标记 old 区。

并发标记完毕后,会执行重新标记(remark 需要 STW),最后将部分 old 区的存活对象复制到连续的空闲区域,并且将这些对象对应的 region 设置为空闲。

Full GC(FGC)

使用 Serial Old 单线程方式回收整个堆,整个过程都需要 STW。

相关的 JVM 参数

https://www.oracle.com/technetwork/java/javase/tech/vmoptions-jsp-140102.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#  JDK 9开始为默认垃圾回收器
-XX:+UseG1GC

# 响应时间优先,建议值,设置最大GC停顿时间(GC pause time)指标(target). 这是一个软性指标(soft goal)
#  JVM 会尽力去达成这个目标. 所以有时候这个目标并不能达成
# G1会尝试调整Young区的块数来达到这个值,如果这个值很小,那么为了达到这个目标,G1可能会让Young区大小减小。
-XX:MaxGCPauseMillis
# 响应时间优先,GC的停顿间隔时间,默认0
-XX:GCPauseIntervalMillis
# 吞吐量优先,设置JVM吞吐量要达到的目标值, GC时间占用程序运行时间的百分比的差值,默认是 99
# 也就应用程序线程应该运行至少99%的总执行时间,GC占 1%
-XX:GCTimeRatio=99

# 并发回收器(STW   YGC)的工作线程数量,默认CPU所支持的线程数,如果CPU所支持的线程数大于8,则 默认 8 + (logical_processor -8)*(5/8)
-XX:ParallelGCThreads
# G1 并发标记线程数量
-XX:ConcGCThreads

# 启动并发GC时的堆内存占用百分比. G1用它来触发并发GC周期,基于整个堆的使用率,而不只是某一代内存的使用比例。默认45%
# 当堆存活对象占用堆的45%,就会启动G1 中Mixed GC
# 这个参数貌似有bug,会基于`老年代使用量/总堆内存`的占比而不是`堆使用量/总堆内存`的占比。https://bugs.java.com/bugdatabase/view_bug.do?bug_id=8151176
-XX:InitiatingHeapOccupancyPercent
# G1 分区大小,建议逐渐增大该值,1 2 4 8 16 32。
# 随着size增加,垃圾的存活时间更长,GC间隔更长,但每次GC的时间也会更长。ZGC做了改进(动态区块大小)
-XX:G1HeapRegionSize

# 新生代最小比例,默认为5%
# -XX:G1NewSizePercent (JDK8u23已经移除 https://www.oracle.com/technical-resources/articles/java/g1gc.html)
# 新生代最大比例,默认为60%
# -XX:G1MaxNewSizePercent (JDK823已经移除 https://www.oracle.com/technical-resources/articles/java/g1gc.html)
# G1 新生代初始大小,默认为5%
-XX:NewSize
# G1 新生代最大大小
-XX:MaxNewSize

参考

https://ylgrgyq.com/understanding-g1-gc.html