进行jvm调优的目的就是为了降低full gc的频次和每次full gc的时间,从而提高吞吐率(处理请求的时间/(处理请求的时间+垃圾回收的处理时间))或者缩短响应时间。
1.虚拟机参数
-Xmx //最大堆空间
-Xms //初始堆空间大小;如果初始堆空间耗尽,jvm会对堆空间扩容,其扩展上限为最大堆空间。通常-Xms与-Xmx设置为同样大小,避免扩容造成性能损耗
-Xmn //新生代大小,一般设置为整个堆空间的1/3或者1/4
-XX:SurvivorRatio //设置新生代中eden空间和from/to空间的比例关系。即-XX:SurvivorRatio=eden/from=eden/to
//例如: -Xmx20m -Xms20m -Xmn1m -XX:SurvivorRation=2 新生代大小为512kb(eden)+256kb(from)+256(to)=1MB,总可用新生代为512kb(eden)+256kb(from或者to)=768kb。(新生成的对象会分配在eden区,如果eden区占满会触发minor gc,将不可回收的对象放入from或to,并且年龄+1)
-XX:NewRation //设置新生代和老年待的比例。 -XX:NewRatio=老年代/新生代 例如:-Xmx20m -Xms20m --XX:NewRatio=2 新生代大小为20MB*1/3=6mb,老年代为13mb左右
//设置时应尽可能将对象预留在新生代,减少老年代GC的次数(FullGC)
-XX:MaxTenuringThreshold //设置垃圾最大年龄。如果设置为0的话,则年轻代对象不经过survivor区,直接进入年老代。对于年老代比较多的应用,可以提高效率。如果将此值设置为一个较大值,则年轻代对象会在survivor区进行多次复制,可以增加对象在年轻代的存活时间,增加在年轻代即被回收的概率。
-XX:PretenureSizeThreshold //另大于这个设置值的对象直接在老年代分配,避免在eden区及两个survivor区之间发生大量的内存复制。pretenureSizeThreshold参数只对serial和parnew两款收集器有效,parallel scavenge收集器一般并不需要设置。如果遇到必须使用此参数的场合,可以考虑ParNew加CMS的收集器组合。
-XX:+HeapDumpOnOutOfMemoryError //当发生内存溢出时导出堆信息
-XX:+HeapDumpPath //当发生内存溢出时导出堆信息指定输出的存放路径。 -XX:+HeapDumpPath=/opt/OOM.dump
-XX:OnOutOfMemoryError //当发生内存溢出时可以执行指定脚本 如:-XX:OnOutOfMemoryError=$JAVA_HOME/bin/Printstack
参考博客:https://www.cnblogs.com/hanlinhu/p/9487049.html
2.典型垃圾回收器
- Serial/Serial Old收集器是最基本最古老的收集器,它是一个单线程收集器,并且在它进行垃圾收集时,必须暂停所有用户线程。Serial收集器是针对新生代的收集器,采用的是Copying算法,Serial Old收集器是针对老年代的收集器,采用的是Mark-Compact算法。它的优点是实现简单高效,但是缺点是会给用户带来停顿。
- ParNew:ParNew收集器是Serial收集器的多线程版本,使用多个线程进行垃圾收集。
- Parallel Scavenge收集器是一个新生代的多线程收集器(并行收集器),它在回收期间不需要暂停其他用户线程,其采用的是Copying算法,该收集器与前两个收集器有所不同,它主要是为了达到一个可控的吞吐量。
- Parallel Old是Parallel Scavenge收集器的老年代版本(并行收集器),使用多线程和Mark-Compact算法。
- CMS(Current Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器,它是一种并发收集器,采用的是Mark-Sweep算法。
- G1收集器是当今收集器技术发展最前沿的成果,它是一款面向服务端应用的收集器,它能充分利用多CPU、多核环境。因此它是一款并行与并发收集器,并且它能建立可预测的停顿时间模型。
说明:
-XX:+UseParallelGC
指 定在 New Generation 使用 parallel collector, 并行收集 , 暂停 app threads, 同时启动多个垃圾回收 thread, 不能和 CMS gc 一起使用 . 系统吨吐量优先 , 但是会有较长长时间的 app pause, 后台系统任务可以使用此 gc。UseParallelGC是jdk1.7选择parallel 回收器默认开启的。
-XX:+UseParNewGC
指定在 New Generation 使用 parallel collector, 是 UseParallelGC 的 gc 的升级版本 , 有更好的性能或者优点 , 可以和 CMS gc 一起使用。UseParNewGC需要用户自己手动开启。
parallel scavenge与parnew区别:parallel scavenge同样是一个使用复制算法的并行的多线程新生代收集器,但是它的关注点与其他收集器不同,CMS等收集器的关注点是尽可能地缩短垃圾收集时用户线程的停顿时间,而Parallel Scavenge收集器的目标则是达到一个可控制的吞吐量(Throughput)。
停顿时间越短就越适合需要与用户交互的程序,良好的响应速度能提升用户体验,而高吞吐量则可以高效率地利用CPU时间,尽快完成程序的运算任务,主要适合在后台运算而不需要太多交互的任务
Parallel Scavenge收集器提供了两个参数用于精确控制吞吐量,分别是控制最大垃圾收集停顿时间的-XX:MaxGCPauseMillis参数以及直接设置吞吐量大小的-XX:GCTimeRatio参数。
G1回收器的优势和不足:
与其他GC收集器相比,G1使用了全新的分区算法。G1在回收期间,可以有多个GC线程同时工作,有效利用多核计算能力。此时用户线程STW(并行)。 G1拥有与应用程序交替执行的能力,部分工作可以和应用程序同时执行,因此,一般来说,不会在整个回收阶段发生完全阻塞应用程序的情况(并发)。
从分代上看,G1依然属于分代型垃圾回收器
,它会区分年轻代和老年代,年轻代依然有Eden区和Survivor区。但从堆的结构上看,它不要求整个Eden区、年轻代或者老年代都是连续的,也不再坚持固定大小和固定数量。
相较于CMS,G1还不具备全方位、压倒性优势。比如在用户程序运行过程中,G1无论是为了垃圾收集产生的内存占用(Footprint)还是程序运行时的额外执行负载(Overload)都要比CMS要高。从经验上来说,在小内存应用上CMS的表现大概率有优先与G1,而G1在大内存应用上则发挥其优势。平衡点在6-8GB之间。
参考博文:https://www.jianshu.com/p/8832fd7e2289
3.调优策略
对于JVM的垃圾回收器影响因素较多,例如:IO、临时变量、常驻对象、对象大小、CPU等各种软硬件配置,需要满足的场景也存在差异,例如:吞吐量量优先、响应耗时优先,
- 如果程序中较多的是“临时变量”的话,可以通过设置适当的年轻代大小,是多数垃圾对象在minor gc时进行回收
- 设置合理的from/to区大小,避免临时对象(如果一批对象大于了from区的50%)直接进入老年代
- 年轻代设置过大的话,进行minor gc耗时就会增加。
推荐博文:JVM垃圾收集器选择策略-https://blog.csdn.net/weixin_43230063/article/details/86582050
JVM调优之垃圾回收器选择-https://www.cnblogs.com/dengq/p/13687713.html
4.调优工具arthas
jps工具:查看当前系统中有哪些java进程。
jmap:查看堆内存占用情况。 jmap -heap 进程id (只能检测某个时间点)
jconsole工具:图形界面的、多功能的监测工具,可以连续监测
jvisualvm:升级版的jConsole。
举例:先jps查看进程id ,再jmap -heap 进程id查看堆内存情况。cmd中输入jconsole出现图形界面的
arthas阿尔萨斯的使用:
-
下载arthas-boot.jar,并通过java -jar arthas-boot.jar启动
-
在控制台输入编号,选择要绑定的具体java程序,进入交互模式
-
常用的主要命令:
dashboard 仪表盘 thread 查看当前线程, 如: thread -b 查看当前阻塞其他线程的线程 jvm 查看当前jvm信息 sc 查看JVM已加载的类信息 ,“Search-Class” 的简写,这个命令能搜索出所有已经加载到 JVM 中的 Class 信息 sm 查看已加载类的方法信息,“Search-Method” 的简写,这个命令能搜索出所有已经加载了 Class 信息的方法信息 jad 反编译class文件,如: jad java.lang.String monitor 监控方法执行情况,如: monitor -c 5 demo.MathGame primeFactors,统计该方法5个周期没的执行状况 watch方法执行数据观测,如: watch demo.MathGame primeFactors "{params,returnObj}"-x 2,-x代表遍历深度 trace 方法内部调用路径,并输出方法路径上的每个节点上耗时,如: trace demo.MathGame run '#cost > 10',展示耗时大于4ms的调用路径 stack 输出当前方法被调用的调用路径,如: stack demo.MathGame primeFactors tt TimeTunnel,时空隧道,法执行数据的时空隧道,记录下指定方法每次调用的入参和返回信息,并能对这些不同的时间下调用进行观测.使用 -p参数可以重新模拟请求 watch/stack/trace这个三个命令都支持#cost
参考博文:https://blog.csdn.net/li646495946/article/details/106970270/?utm_medium=distribute.pc_relevant.none-task-blog-2defaultbaidujs_baidulandingword~default-0.no_search_link&spm=1001.2101.3001.4242.0
https://blog.csdn.net/hhj13978064496/article/details/110876005