本文内容
1 优化层面
2 JVM调优
1 优化层面
1.1 设计层面
系统的设计层面是要最先考虑的问题。一个糟糕的设计,会直接导致系统性能出现问题。
例如,对于支付宝和微信的红包系统,若设计出现问题,即便程序优秀、硬件强劲,也于事无补。反之,若设计良好,则事半功倍。
1.2 程序层面
在一个良好的设计下,程序本身的优劣就决定了性能的高低。高质量的代码会提高资源的利用率,减少不必要的操作,从而提升性能。
1.3 设置层面
排除设计和程序层面的原因,出现性能问题时,可以通过一些系统参数设置,以匹配现实情况,使得资源使用最优化,从而提升性能。
1.4 硬件层面
当上述三个层面都没有问题的时候,就要考虑扩展硬件设备了。
2 JVM调优(设置层面)
2.1 工作模式
2.1.1 client
占用内存小,启动速度快;
适合短时间运行。
2.1.2 server
与client模式相比,占用内存大,启动速度慢,但启动后,执行速度快;
适合长时间运行。
2.1.3 设置工作模式
配置文件:%JAVA_HOME%/jre/lib/amd64/jvm.cfg
单次启动:java -client/-server
2.2 堆内存
2.2.1 Max Memory
-Xmx 可用的最大内存数(Max Memory),即JVM可占用的内存上限。
通常,服务器是专机专用,除了运行指定的服务程序,一般情况下,不会同时运行其它的程序。
所以,预留部分内存给操作系统、元空间内存、线程内存等,其余内存尽可能分配给JVM,以提高内存利用率和性能。
2.2.2 Total Memory & Virtual Memory
-Xms 初始分配内存(Total Memory)。
其余作为伸缩区(Virtual Memory),即 Max Memory = Total Memory + Virtual Memory。
当初始分配的内存不足时,会从伸缩区申请部分内存,当可用内存充裕时,再将申请来的内存释放。
通常,服务器是专机专用,除了运行执行的服务程序,不会同时运行其它的程序。
所以,一般会将Total Memory设置与Max Memory相等,即不设置伸缩区,以减少从伸缩区申请和释放内存带来的开销。
2.2.3 Young Memory (Eden + Survivor) &Tenured Memory
-Xmn 年轻代内存(Young Memory)
-XX:NewRatio=n 年轻代与老年代的内存比率(Young:Tenured = 1:n)
-XX:NewSize=n 存活代内存(Survivor Memory)
-XX:SurvivorRatio=n 存活代与伊甸园的内存比率(Survivor:Eden = 1:n)
-XX:PretenureSizeThreshold=n 直接晋升老年代的对象大小
-XX:MaxTenuringThreshold=n 存活代最大年龄(经历n次GC还存活的内存对象将晋升至老年代)
-XX:+UseAdaptiveSizePolicy 是否采用动态控制策略(堆中各区域大小及晋升老年代的年龄)
新创建的对象都会保存在Young Memory的Eden Memory中,满足一定条件的对象会被移至Young Memory的Survivor Memory中,进而再晋级至Tenured Memory中。
因此,需要根据程序实际运行情况,合理设置相应内存数值和比率。
2.3 线程内存
-Xss 每个线程分配的内存,JDK 5.0以后,每个线程内存大小默认为1M。
线程内存占用本机物理内存,而非JVM堆内存。
降低单个线程的内存,可以提高可并发的线程数量,但是,线程数量有一定限制(上限),不能无限上涨。
应合理设置线程堆栈内存,通常,将线程数控制在3000-5000范围内。
2.4 元空间内存
-XX:MaxMetaspaceSize 最大容量,默认无限制(受到本机物理内存限制)
-XX:MetaspaceSize 初始分配容量
-XX:MinMetaspaceFreeRatio 执行GC后,最小剩余元空间比例。
-XX:MaxMetaspaceFreeRatio 执行GC后,最大剩余元空间比例。
2.5 回收机制
-XX:+UseSerialGC 年轻代:串行、复制算法,适合单CPU环境Client模式,老年代支持CMS;
-XX:+UseParNewGC 年轻代:并行、复制算法,适合多CPU环境Server模式,老年代支持CMS;
-XX:+UseParallelGC 年轻代:并行、复制算法,适合后台运算而无太多交互任务情况,老年代仅支持Serial Old收集器;
-XX:+UseParalledlOldGC 老年代:并行、标记-整理算法,适合后台运算而无太多交互任务情况;
-XX:ParallelGCThreads=n 设置并行收集器使用的CPU数量;
-XX:+UseConcMarkSweepGC 老年代:并发、标记-清除算法,适合服务端应用;
-XX:+CMSParallelRemarkEnabled 开启并行Remark模式(减少Remark阶段暂停时间);
-XX:+CMSScavengeBeforeRemark 每次Remark之前,先进行一次Minor GC(减少Remark阶段暂停时间);
-XX:+UseCMSInitiatingOccupancyOnly 仅当内存使用情况达到指定阈值时,才进行CMS操作;
-XX:CMSInitiatingOccupancyFraction=n 指定CMS操作执行判断的内存阈值;
-XX:+CMSIncrementalMode 设置为增量模式,适用于单CPU情况;
-XX:+UseG1GC 年轻代+老年代:并发、年轻代复制/老年代标记-整理,适合服务端应用;
-XX:MaxGCPauseMillis=n 设置最大暂停时间目标(毫秒),JVM会尽量达到该目标,但不保证一定能达到目标;
-XX:+ScavengeBeforeFullGC 每次Full GC之前,先执行一次Minor GC(减少Full GC暂停时间)
-XX:+DisableExplicitGC 关闭显示执行GC代码功能(禁用System.gc()方法)
2.6 统计信息
2.6.1 GC
-Xloggc:/data/log/gc.log 指定GC日志文件路径
-XX:+UseGCLogFileRotation 开启GC滚动日志
-XX:GCLogFileSize=n 指定GC滚动日志文件大小
-XX:NumberOfGCLogFiles=n 指定GC滚动日志文件数量
-XX:+PrintGC 每次触发GC的时候打印相关日志(在JDK9中已经弃用)
-XX:+PrintGCDetails 更详细的GC日志
-XX:+PrintGCDateStamps 打印日期和时间信息
-XX:+PrintGCTimeStamps 打印启动时长信息(自JVM启动起计时)
-XX:+PrintHeapAtGC 每次GC时打印堆的详细详细信息
-XX:+PrintGCApplicationConcurrentTime 打印应用程序执行时间
-XX:+PrintGCApplicationAtoppedTime 打印应用程序由GC引起的停顿时间
-XX:+PrintReferenceGC 跟踪系统内的软引用,弱引用,虚引用和finallize队列。
2.6.2 类跟踪
-verbose:class 跟踪类的加载和卸载(-verbose:gc 相当于 -XX:+PrintGC)
-XX:+TraceClassLoading 单独跟踪类加载
-XX:+TraceClassUnloading 单独跟踪类卸载
-XX:+PrintClassHistogram 查看运行时类的分布情况
2.7 分析工具
2.7.1 jstat
jstat (Java Virtual Machine statistics monitoring tool) 是JDK自带的一个轻量级分析工具。
可通过 jstat 对GC情况进行打印输出和分析,常用格式:
jstat -gcutil <PID> [interval [limit]]
- <PID> 表示JVM进程ID,可通过 ps -ef | grep java 查询
- interval 表示打印间隔时间(毫秒)
- limit 表示打印次数(缺省表示无限打印)
例如:
jstat -gcutil 17 1000
每1000毫秒打印一次进程ID为17的JVM内存情况
2.7.2 jstack
jstack 是JDK自带的线程堆栈分析工具。
可通过 jstack 查看或导出 Java 应用程序中线程堆栈信息,常用格式:
jstack <PID>