jvm调优
jvm调优
目的:减少STW(停止世界) FULL GC 几个小时几周正常
GC ROOT 可达性分析(本地方法栈JNI引用,局部变量,静态变量) 垃圾收集
为什么要设计STW机制?
gc root
如果寻找非垃圾不停顿,当确定线程对象为非垃圾之后,线程执行完毕,但是这个时候已经把无用的对象赋值了。(导致一直循环)
问题:
minor gc快是因为内存小
设置为64G 服务端STW 容易引发客户端发送消息超时
怎样解决问题呢? 设置最大STW时间
根据垃圾收集器选择来解决
-XX:MaxGCPauseMillis=300 最大停顿时间 会增加GC次数 需要平衡一下
-server
-Xmx 最大堆内存
-Xms 最小堆内存
-Xmn 新生代大小
-Xss 栈大小
-XX:PretenureSizeThreshold=3145728 多大对象直接进入老年代
-XX:MaxTenuringThreshold=0 设置垃圾最大年龄
-XX:NewRatio=n:设置年轻代和年老代的比值。如:为3,表示年轻代与年老代比值为1:3,年轻代占整个年轻代年老代和的1/4
-XX:MaxPermSize=n:设置持久代大小
收集器设置
-XX:+UseSerialGC:设置串行收集器
-XX:+UseParallelGC:设置并行收集器
-XX:+UseParalledlOldGC:设置并行年老代收集器
-XX:+UseConcMarkSweepGC:设置并发收集器
设置异常打印堆dump文件:
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/app
设置输出gc日志:
-XX:+PrintGCDetails
-XX:+PrintGCDateStamps
-Xloggc:/app/scripts/gc.log
-XX:+DisableExplicitGC 代码中手动调用System.gc()就不会生效
-XX:+ExplicitGCInvokesConcurrent 允许systemGC且使用CMS GC时有效
-XX:ParallelGCThreads=8 垃圾回收线程数
-XX:+UseConcMarkSweepGC CMS收集器
-XX:CMSInitiatingOccupancyFraction=70 老年代超过70%执行FULL GC
-XX:+UseParNewGC 新生代并行收集器
-XX:+CMSParallelRemarkEnabled 在使用UseParNewGC 的情况下, 尽量减少 mark 的时间
-XX:+UseCMSCompactAtFullCollection 在使用concurrent gc 的情况下, 防止 memoryfragmention, 对live object 进行整理, 使 memory 碎片减少。
-XX:CMSFullGCsBeforeCompaction=0 设置在执行多少次Full GC后对内存空间进行压缩整理。
-XX:+CMSClassUnloadingEnabled 垃圾回收会清理持久代,移除不再使用的classes。这个参数只有在 UseConcMarkSweepGC 也启用的情况下才有用
-XX:+PrintPromotionFailure 打开了就知道是多大的新生代对象晋升到老生代失败从而引发Full GC时的
并发线程数计算:
ParallelGCThreads=8+( Processor - 8 ) ( 5/8 );
ConcGCThreads = (ParallelGCThreads + 3)/4
垃圾收集器
Serial 单线程 适用于单核cpu 小系统 缺点 垃圾收集时停顿时间长
(串行收集器)
对应JVM参数是:-XX:+UseSerialGC
开启后会使用:Serial(young区用)+SerialOld(Old区用)的收集器组合(参考图一)
表示:新生代、老年代都会使用串行回收收集器,新生代使用复制算法,老年代使用标记-整理算法
SerialOld收集器
单线程的收集器,使用标记-整理算法,这个收集器也主要是运行在Client默认的java虚拟机默认的年老代垃圾收集器。
ParNew(并行收集器)
JVM参数:-XX:+UseParNewGC,启用ParNew收集器,只影响新生代的收集,不影响老年代,
开启上述参数后,会使用:ParNew(Young区用)+SerialOld的收集器组合,新生代使用复制算法,老年代采用标记-整理算法
但是,ParNew+Tenured这样的搭配,java8已经不再推荐:
-XX:ParallelGCThreads 限制线程数量,默认开启和CPU数目相同的线程数。
Parallel Scavenge收集器
Parallel Scavenge收集器类似ParNew也是一个新生代垃圾收集器,使用复制算法,也是一个并行的多线程的垃圾收集器,俗称吞吐量优先收集器。一句话:串行收集器在新生代和老年代的并行化
它重点关注的是:可控制的吞吐量〈Thoughput=运行用户代码时间/(运行用户代码时间+垃圾收集时间),也即比如程序运行100分钟,垃圾收集时间1分钟,吞吐量就是99%)。高吞吐量意味着高效利用CPU的时间,它多用于在后台运算而不需要太多交互的任务
JVM参数:-XX:+UseParaIIeIGC或-XX:+UseParaIIeIOldGC(可互相激活)使用ParallelScanvenge收集器开启该参数后:新生代使用复制算法,老年代使用标记·整理算法
-XX:ParaIIeIGCThreads=数字N,表示启动多少个GC线程
cpu>8,N=5/8
cpu<8,N=实际个数
ParallelOld收集器
ParallelOld收集器是ParaScavenge的老年代版本,使用多线程的标记-整理算法,Parallel Old收集器在JDKI.6才开始提供。
ParallelOld正是为了在年老代同样提供吞吐量优先的垃圾收集器,如果系统对吞吐量要求比较高,JDK1.8后可以优先考虑新生代ParallelScavenge和年老代Parallel Old收集器的搭配策略。在JDK1.8及后(Parallel Scavenge+Parallel Old)
JVM常用参数:
-XX:+UseParallelOldGC使用ParallelOld收集器,设置该参数后,新生代Parallel+老年代ParallelOld
CMS(如果搞不定 就直接升级G1)
CMS收集器(ConcurrentMarkSweep:并发标记清除)是一种以获取最短回收停顿时间为目标的收集器。适合应用在互联网站或者B/S系统的服务器上,这类应用尤其重视服务器的响应速度,希望系统停顿时间最短。CMS非常适合堆内存大、CPU核数多的服务器端应用,也是G1出现之前大型应用的首选收集器。
JVM参数:-XX:+UseConcMarkSreepGC,开启该参数后会自动将-XX:+UseParNewGC打开
使用ParNew(Young区用)+CMS(Old区用)+SerialOld的收集器组合,SerialOld将作为CMS出错的后备收集器。
CMS四步骤:
- 初始标记(CMS initial mark):只是标记一下GC Roots能直接关联的对象,速度很快,仍然需要暂停所有的工作线程
- 并发标记(CMS concurrent mark)和用户线程一起:进行GC Roots跟踪的过程,和用户线程一起工作,不需要暂停工作线程。主要标记过程,标记全部对象
- 重新标记(CMS remark):为了修正并发标记期间,因用户程序继续运行而导致标记产生变动的那一部分对象的标记记录,仍然需要暂停所有的工作线程。由于并发标记时,用户线程仍然运行,因此在正式清理前,再做修正。
- 并发清除(CMS concurrent sweep)和用户线程一起:清除GC Roots不可达对象,和用户线程一起工作,不需要暂停工作线程。基于标记结果,直接清理对象
CMS优点:并发收集低停顿
CMS缺点:
并发执行,对cpu资源压力大:由于并发进行,CMS在收集与应用线程会同时会增加对堆内存的占用,也就是说,CMS必须要在老年代堆内存用尽之前完成垃圾回收,否则CMS回收失败时,将触发担保机制,串行老年代收集器将会以STW的方式进行上次GC,从而造成较大停顿时间。
采用的标记清除算法会导致大量碎片:标记清除算法无法整理空间碎片,老年代空间会随着应用时长被逐步耗尽,最后将不得不通过担保机制对堆内存进行压缩。CMS也提供了参数 -XX:MSFuIIGCsBeForeCompaction (默认0,即每次都进行内存整理)来指定多少次CMS收集之后,进行一次压缩的FullGC。
G1收集器
以前收集器特点
- 年轻代和老年代是各自独立且连续的内存块
- 年轻代收集必须使用单个eden+S0+S1进行复制算法
- 老年代收集扫描整个老年代区域
- 都是以尽可能少而快速地执行GC为设计原则
G1是一种服务器端的垃圾收集器,应用在多处理器和大容量内存环境中,在提高吞吐量的同时,尽可能的满足垃圾收集暂停时间的要求。另外,它还具有以下特性:
- 像CMS收集器一样,能与应用程序线程并发执行
- 整理空闲空间更快
- 需要更多的时间来预测GC停顿时间
- 不希望牺牲大量的吞吐性能
- 不需要更大的Java Heap
G1收集器的设计目标是取代CMS收集器,它同CMS相比,在以下方面表现更出色:
主要改变是Eden,Survivor和Tenured等内存区域不再是连续的了,而是变成了一个个大小一样的region,每个region从1M到32M不等。一个region有可能属于Eden,Survivor或者Tenured内存区域。
- G1能充分利用多CPU、多核环境硬件优势,尽量缩短STW
- GI整体上采用标记-整理算法,局部是通过复制算法,不会产生内存碎片。
- 宏观上看G1之中不再区分年轻代和老年代。把内存划分成多个独立的了区域(Region),可以近似理解为一个围棋的棋盘。
- G1收集器里面讲整个的内存区都混合在一起了,但其本身依然在小范围内要进行年轻代和老年代的区分,保留了新生代和老年代,但它们不再是物理隔离的,而是一部分Region的集合且不需要Region是连续的,也就是说依然会采用不同的GC方式来处理不同的区域。GI虽然也是分代收集器,但整个内存分区不存在物理上的年轻代与老年代的区别,也不需要完全独立的survivor(tospace)堆做复制
- 准备。GI只有逻辑上的分代概念,或者说每个分区都可能随GI的运行在不同代之间前后切换;
比起CMS有两个优势:
- G1不会产生内存碎片。
- G1可以精确控制停顿。该收集器是把整个堆(新生代、老年代)划分成多个固定大小的区域,每次根据运行 停顿的时间去收集垃圾最多的区域。
本文来自博客园,作者:苏子墨,转载请注明原文链接:https://www.cnblogs.com/li-xiaotian/p/16602626.html
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· 【.NET】调用本地 Deepseek 模型
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库
· 上周热点回顾(2.17-2.23)
· DeepSeek “源神”启动!「GitHub 热点速览」