GC和GC Tuning
GCC基础知识
什么是垃圾
C语言申请内存:malloc free
C++: new delete
C/C++: 手动回收内存
Java:
自动内存回收,编程简单,系统不易出错,容易出现两种类型问题:
- 忘记回收
- 多次回收
如何定位垃圾
- 引用计数(RefrenceCount), 无法解决对象循环引用问题
- 根可达算法(RootSearching)
常见的垃圾回收算法
- 标记清除(mark sweep)---位置不连续,容易产生内存碎片,碎片多的情况下导致空间利用率不高
- copy算法---没有碎片,但是浪费空间
- 标记压缩算法(mark compact)---没有碎片,效率低(两边扫描,指针需要调整)
JVM内存分代模型
- 部分垃圾回收器使用的模型
除Epsilon ZGC Shenandoah之外的GC都使用逻辑分代模型
G1是逻辑分代,物理不分代
除此之外不仅逻辑分代,而且物理分代
-
新生代+老年代+永久代(1.7)Perm Generation/元数据区(1.8) Metaspace
- 永久代 元数据---Class
- 永久代必须指定大小限制,元数据可以设置,也可以不设置,无上限(受限于物理内存)
- 字符串常量1.7--永久代 1.8--堆
- MethodArea逻辑概念--永久代、元数据
-
新生代=Eden+2个survivor区
- YGC回收后,大多数对象会被回收,或者进入S0
- 再次YGC,活着对象Eden+S0->S1
- 再次YGC, Eden+S1->S0
- 年龄足够-->老年代(PS 15 CMS 6)
- s区装不下-->老年代
-
老年代
- 顽固分子
- 老年代满了则FGC /Full GC
-
GC Tunning(Generation)
- 尽量减少FGC
- MinorGC=YGC
- MajorGC=FGC
-
对象分配过程
-
动态年龄(不重要)
-
分配担保(不重要)
YGC期间survivor区空间不够了,空间担保直接进入老年代
常见的垃圾回收器
-
垃圾回收器的发展路线是随着内存越来越大的过程而演进
从分代算法演化到不分代算法
Serial算法 几十M
Parallel算法 几个G
CMS 几十个G---承上启下,开始并发回收
G1-上百个G
ZGC-- 4T-16T (JDK13)
三色标记
-
JDK1.0只有Serial,后来为了提高效率诞生并发垃圾回收器,发展史
-
Serial采用copy方式回收年轻代
-
Serial-Old采用标记压缩回收老年代
-
Parallel Scavenge 使用多线程采用copy方式回收垃圾
-
Parallel old采用多线程压缩算法进行回收
-
ParNew与PS一样,只是为了配合CMS使用
-
CMS
- concurrent mark sweep
- a mostly concurrent, low-pause collector
- initial mark(find GC ROOT), concurrent mark(可能会标错), remark(剔除标记错误的对象,不应该被标记), concurrent sweep
- 算法: 采用三色标记法+Incremental Update
缺点:
内存碎片,加入-XX:CMSFullGCsBeforeCompaction
浮动垃圾
Concurrent Model Failure -XX: CMSInitiatingOccupancyFraction 92%
当内存碎片具多时,CMS将采用Serial old方式回收垃圾
-
G1(200ms-10ms)
算法: 采用三色标记法+SATB
-
ZGC(100ms-1ms) PK C++
算法: ColoredPointers + LoadBarrier
-
Shenandoah
算法: ColoredPointers + WriteBarrier
三色标记法
白色:未被标记
灰色:自身被标记但成员未被标记
黑色:自身和成员都被标记
产生漏标:
- 标记进行时增加一个黑到白的引用,如果不重新对黑色进行处理,则会漏标
- 标记进行时删除了灰色对象到白对象的引用,那么这个白对象有可能被漏标
常见的垃圾回收器组合参数设定
- -XX:+UseSerialGC ===Serial New + Serial Old
- -XX:+UseParNewGC ===ParNew + Serial Old
- -XX:+UseConcurrentMarkSweepGC ===ParNew + CMS + Serial Old
- -XX:+UseParallelOldGC ===PS + PO
- -XX:+UseParallelGC ===PS + PO
- -XX:+UseG1GC ===G1
CMS三色标记
JVM调优
JVM命令行参数:https://blog.csdn.net/zhyhang/article/details/105037987
试用程序
public class HelloGC {
public static void main(String[] args) {
System.out.println("Hello GC!");
LinkedList<Object> objects = new LinkedList<>();
while (true) {
byte[] b = new byte[1024*1024];
objects.add(b);
}
}
}
- 区分概念内存泄漏 memory leak,内存溢出out of memory
- java -Xmn10M -Xms40M -Xmx60M --XX:+PrintGC HelloGC
- java -XX:+PrintFlagsInitial 显示默认参数
- java -XX:+PrintFlagsFinal 显示最终参数
调优的基础概念
吞吐量:用户代码执行时间/(用户代码执行时间+垃圾回收时间)
响应时间:STW越短,响应时间越好
调优需要明确是吞吐量优先,还是响应时间优先,或者满足一定响应时间下,吞吐量越大越好。
科学计算、数据挖掘一般要求吞吐量优先,可以选择PS+PO
Web网站,RestAPI一般要求响应优先
什么是调优
根据需要进行JVM规划和预调优
优化JVM运行环境(慢、卡顿等)
解决JVM运行过程中出现的各种问题
调优步骤
-
调优从业务场景开始,没有业务场景的调优就是耍流氓
-
无监控,不调优,系统只有通过压力测试,看到结果,才能明确调优方向
-
根据实际业务选择垃圾回收
-
计算内存需求
-
选定CPU,主频越高越好
-
设置年代大小,年龄升级的阈值
-
设置日志参数 -Xloggc:/path/to/xxx-%t.log -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5
-XX:GCLogFileSize=20M -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCCause
-
观察日志
优化环境
有一个50w PV的资料网站(从磁盘读取文档到内存)原服务器32位、1.5G的堆内存,用户反馈网站反应慢,因此公司将服务升级为64位、16G堆内存,结果用户反映有时反应比以前更慢了?
网站慢是因为很多用户浏览网站,需要将数据从文件load到内存,而内存过小导致频繁GC,STW时间过长,导致应用反应慢;换大内存反应还慢是因为内存大了后FGC的时间过长,因此需要调整垃圾回收器
系统CPU经常100%,如何优化?
- 首先找出那个进程CPU使用率高(top)
- 找出该进程那个线程CPU使用率高(top -Hp)
- 导出该线程堆栈(jstack)
- 查看那个方法消耗时间(jstack)
- 工作线程占比高还是垃圾回收器占比高
系统内存飙高,如何查找原因?
- 导出堆内存(jmap)
- 分析(jhat、jvisualvm、mat)
解决JVM运行中的问题
通过一个案例理解工具
public class HelloGC {
private static ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(50,
new ThreadPoolExecutor.DiscardOldestPolicy());
public static void main(String[] args) {
executor.setMaximumPoolSize(50);
while (true) {
modelFit();
try {
TimeUnit.MILLISECONDS.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private static class CardInfo {
BigDecimal price = new BigDecimal(0.0);
String name = "Liming";
int age = 25;
Date birthday = new Date();
public void m() {
//do something
}
}
private static void modelFit() {
List<CardInfo> tasks = getAllCardInfo();
tasks.forEach(e->executor.scheduleWithFixedDelay(e::m, 2 ,3, TimeUnit.SECONDS));
}
private static List<CardInfo> getAllCardInfo() {
List<CardInfo> list = new ArrayList<>();
for (int i = 0; i < 100; i++) {
CardInfo cardInfo = new CardInfo();
list.add(cardInfo);
}
return list;
}
}
java -Xms200M -Xmx200M -XX:PrintGC HelloGC
问题排查流程与命令
- JPS 查看java进程
- jstack定位线程状况、重点关注:WAITING BLOCKED(容易出现死锁)
- jinfo pid
- jstat -gc 4655 500 每隔500毫秒打印GC的情况
- jmap -histo 4655 | head -20 查看堆中的对象,如果堆内存特别大时,该命令会对进程产生影响,甚至卡顿(生成环境不适合用),压测的时候可以使用
虚拟机启动时设定参数HeapDump, OOM的时候自动产生堆转储文件(不专业,因为有监控的话内存增长会报警)
很多服务器备份(高可用),停掉这台服务器对其他服务器不影响 - jmap -dump:format=b, file=dump.bin pid
- 使用MAT/jhat/jvisualvm进行dump文件分析
GC常用参数
https://www.cnblogs.com/farmersun/p/12439969.html