---
# System prepended metadata

title: JAVA GC
tags: [JAVA]

---

---
title: JAVA GC
---
###### tags: `JAVA`

[TOC]

# 待看
1. GC 分區 - den regions、Survivor regions、Old regions、Archive regions、Humongous regions、Metaspace

# java19 GC

# 必看

https://cloud.tencent.com/developer/article/1812722


1、调优原则

JVM调优听起来很高大上，但是要认识到，JVM调优应该是Java性能优化的最后一颗子弹。

![](https://i.imgur.com/Mg2LR19.png)

比较认可廖雪峰老师的观点，要认识到JVM调优不是常规手段，性能问题一般第一选择是优化程序，最后的选择才是进行JVM调优。

![](https://i.imgur.com/TB1zpUt.png)

JVM的自动内存管理本来就是为了将开发人员从内存管理的泥潭里拉出来。即使不得不进行JVM调优，也绝对不能拍脑门就去调整参数，一定要全面监控，详细分析性能数据。

2、JVM调优的时机

不得不考虑进行JVM调优的是那些情况呢？

Heap内存（老年代）持续上涨达到设置的最大内存值；
Full GC 次数频繁；
GC 停顿时间过长（超过1秒）；
应用出现OutOfMemory 等内存异常；
应用中有使用本地缓存且占用大量内存空间；
系统吞吐量与响应性能不高或下降。
3、JVM调优的目标

吞吐量、延迟、内存占用三者类似CAP，构成了一个不可能三角，只能选择其中两个进行调优，不可三者兼得。

延迟：GC低停顿和GC低频率；
低内存占用；
高吞吐量;
选择了其中两个，必然会会以牺牲另一个为代价。

下面展示了一些JVM调优的量化目标参考实例：

Heap 内存使用率 <= 70%;
Old generation内存使用率<= 70%;
avgpause <= 1秒;
Full gc 次数0 或 avg pause interval >= 24小时 ;
注意：不同应用的JVM调优量化目标是不一样的。

4、JVM调优的步骤

一般情况下，JVM调优可通过以下步骤进行：

分析系统系统运行情况：分析GC日志及dump文件，判断是否需要优化，确定瓶颈问题点；
确定JVM调优量化目标；
确定JVM调优参数（根据历史JVM参数来调整）；
依次确定调优内存、延迟、吞吐量等指标；
对比观察调优前后的差异；
不断的分析和调整，直到找到合适的JVM参数配置；
找到最合适的参数，将这些参数应用到所有服务器，并进行后续跟踪。
以上操作步骤中，某些步骤是需要多次不断迭代完成的。一般是从满足程序的内存使用需求开始的，之后是时间延迟的要求，最后才是吞吐量的要求，要基于这个步骤来不断优化，每一个步骤都是进行下一步的基础，不可逆行。

![](https://i.imgur.com/cMkC8Qf.png)

6、主要工具

6.1、JDK工具

JDK自带了很多性能监控工具，我们可以用这些工具来监测系统和排查内存性能问题。
![](https://i.imgur.com/9BUATqh.png)
6.2、Linux 命令行工具

进行性能监控和问题排查的时候，常常是结合操作系统本身的命令行工具来进行。
![](https://i.imgur.com/cJ6DkUK.png)

其它还有一些第三方的监控工具，同样是性能分析和故障排查的利器，如MAT、GChisto、JProfiler、arthas。

7、常用调优策略

这里还是要提一下，及时确定要进行JVM调优，也不要陷入“知见障”，进行分析之后，发现可以通过优化程序提升性能，仍然首选优化程序。

7.1、选择合适的垃圾回收器

CPU单核，那么毫无疑问Serial 垃圾收集器是你唯一的选择。

CPU多核，关注吞吐量 ，那么选择PS+PO组合。

CPU多核，关注用户停顿时间，JDK版本1.6或者1.7，那么选择CMS。

CPU多核，关注用户停顿时间，JDK1.8及以上，JVM可用内存6G以上，那么选择G1。

参数配置：
![](https://i.imgur.com/d6oBZiG.png)

7.2、调整内存大小

现象：垃圾收集频率非常频繁。

原因：如果内存太小，就会导致频繁的需要进行垃圾收集才能释放出足够的空间来创建新的对象，所以增加堆内存大小的效果是非常显而易见的。

注意：如果垃圾收集次数非常频繁，但是每次能回收的对象非常少，那么这个时候并非内存太小，而可能是内存泄露导致对象无法回收，从而造成频繁GC。

参数配置：
![](https://i.imgur.com/odFd5th.png)

7.3、设置符合预期的停顿时间

现象：程序间接性的卡顿

原因：如果没有确切的停顿时间设定，垃圾收集器以吞吐量为主，那么垃圾收集时间就会不稳定。

注意：不要设置不切实际的停顿时间，单次时间越短也意味着需要更多的GC次数才能回收完原有数量的垃圾.

参数配置：
![](https://i.imgur.com/jeUY1ue.png)

7.4、调整内存区域大小比率

现象：某一个区域的GC频繁，其他都正常。

原因：如果对应区域空间不足，导致需要频繁GC来释放空间，在JVM堆内存无法增加的情况下，可以调整对应区域的大小比率。

注意：也许并非空间不足，而是因为内存泄造成内存无法回收。从而导致GC频繁。

参数配置：
![](https://i.imgur.com/poxoKdB.png)

7.5、调整对象升老年代的年龄

现象：老年代频繁GC，每次回收的对象很多。

原因：如果升代年龄小，新生代的对象很快就进入老年代了，导致老年代对象变多，而这些对象其实在随后的很短时间内就可以回收，这时候可以调整对象的升级代年龄，让对象不那么容易进入老年代解决老年代空间不足频繁GC问题。

注意：增加了年龄之后，这些对象在新生代的时间会变长可能导致新生代的GC频率增加，并且频繁复制这些对象新生的GC时间也可能变长。

配置参数：
![](https://i.imgur.com/rWPaU0U.png)

7.6、调整大对象的标准

现象：老年代频繁GC，每次回收的对象很多,而且单个对象的体积都比较大。

原因：如果大量的大对象直接分配到老年代，导致老年代容易被填满而造成频繁GC，可设置对象直接进入老年代的标准。

注意：这些大对象进入新生代后可能会使新生代的GC频率和时间增加。

配置参数：
![](https://i.imgur.com/YwHmQYg.png)

7.7、调整GC的触发时机

现象：CMS，G1 经常 Full GC，程序卡顿严重。

原因：G1和CMS  部分GC阶段是并发进行的，业务线程和垃圾收集线程一起工作，也就说明垃圾收集的过程中业务线程会生成新的对象，所以在GC的时候需要预留一部分内存空间来容纳新产生的对象，如果这个时候内存空间不足以容纳新产生的对象，那么JVM就会停止并发收集暂停所有业务线程（STW）来保证垃圾收集的正常运行。这个时候可以调整GC触发的时机（比如在老年代占用60%就触发GC），这样就可以预留足够的空间来让业务线程创建的对象有足够的空间分配。

注意：提早触发GC会增加老年代GC的频率。

配置参数：
![](https://i.imgur.com/tlwrEOJ.png)







# GC 顯示LOG

G1 模式下总计有 3 種日志级别，分别被称为：fine，finer，finest。
fine：fine模式打开方式是-verbose:gc，等价于-XX:+PrintGC。
finer：推荐，finer模式的打开方式是-XX:+PrintGCDetails。
finest：finest模式打开方式是-XX:+UnlockExperimentalVMOptions -XX:G1LogLevel=finest。

G1模拟下主要有四种回收方式：
Young GC：所有Eden区域满了后触发，并行收集，且完全STW。
并发标记周期：它的第一个阶段初始化标记和YGC一起发生，这个周期的目的就是找到回收价值最大的Region集合（垃圾很多，存活对象很少），为接下来的Mixed GC服务。
Mixed GC：回收所有年轻代的Region和部分老年代的Region，Mixed GC可能连续发生多次。
Full GC：非常慢，对OLTP系统来说简直就是灾难，会STW且回收所有类型的Region。

## IDEA配置
1. 第一種 vm options处加入
2. 第二種 修改vmoptions文件 /Applications/IntelliJIDEA.app/Contents/bin/idea.vmoptions

## jdk全局配置

# GC LOG解讀
1. Pause Young (Normal) 
针对年轻代的数据迁移产生的停顿，如果是 (mixed) 则指的是混合gc(年轻代+老年代)

2. (G1 Evacuation Pause) 
(G1 Evacuation Pause) 指的是产生本次gc的原因，空间使用达到阈值，如果是young gc则意味着年轻代已满，如果是mixed gc则大多数情况下是老年代空间占整个堆空间比例达到阈值（默认45%）。

3. Using 4 workers of 4 for evacuation
4 线程并行回收，一共4个工作线程

4. 



收藏：https://blog.csdn.net/weixin_42340670/article/details/121743655

# 常用GC配置 
1. 初始和最大设置成一样，避免堆内存在应用运行过程中自动扩容而影响服务稳定性 
-Xms4G【初始堆内存4G】
-Xmx4G【最大堆内存4G】

2. 对于响应的低延迟需求，在选择垃圾收集器上自然就选用G1
-XX:+UseG1GC

3. 打印GC日志
 -XX:+PrintGCDetails
 
 4. 打印GC日志附带时间戳
 -XX:+PrintGCDateStamps
 
5. GC之前和GC之后的堆的内存使用情况要打印出来 
-XX:+PrintHeapAtGC

6. GC日志文件开启滚动存储
-XX:+UseGCLogFileRotation

7. GC日志文件开启滚动存储
-XX:+UseGCLogFileRotation

8. GC日志文件最多保留5个
-XX:NumberOfGCLogFiles=5

9. 每个GC日志文件最大30M，超过30M，生成新的文件
-XX:GCLogFileSize=30M

10. 当发生内存溢出时dump堆转储文件
-XX:+HeapDumpOnOutOfMemoryError

11. 堆转储文件的存放地址
-XX:HeapDumpPath=/data/logs/java/heap_dump_%p.log

12. 设置用于Java堆的大页面尺寸
-XX:LargePageSizeInBytes=4m 

13. GC后java堆中空闲量占的最大比例
-XX:MaxHeapFreeRatio=70 

14. 新生成对象能占用内存的最大值
-XX:MaxNewSize=size 

15. 老生代对象能占用内存的最大值
-XX:MaxPermSize=64m 

16. GC后java堆中空闲量占的最小比例
-XX:MinHeapFreeRatio=40 

17. 新生代内存容量与老生代内存容量的比例
-XX:NewRatio=2 

18. 新生代对象生成时占用内存的默认值
-XX:NewSize=2.125m 

19. 保留代码占用的内存容量
-XX:ReservedCodeCacheSize=32m 

20. 设置线程栈大小，若为0则使用系统默认值
-XX:ThreadStackSize=512 

21. 使用大页面内存
-XX:+UseLargePages 
 

# 待看好文
https://heapdump.cn/article/233563
