带你入门jvm性能优化

缘起

我司项目现在的java项目都是基于springboot开发,通过Jenkins与ansible直接把项目编译的jar在阿里云主机上部署运行,几台云主机的配置大都是通用型2核8G,java项目难免遇到诸如内存溢出这类错误,为了让程序在服务器上有最佳运行效果,需要对每个项目进行jvm调优,JDK本身提供了很多方便的JVM性能调优监控工具,除了集成式的VisualVM和jConsole外,还有jps、jstack、jmap、jhat、jstat等小巧的工具,本文希望能起抛砖引玉之用,让大家能开始对JVM性能调优的常用工具有所了解

带你入门jvm性能优化

java

  1. jps:查看所有java项目进程

    一般我都用: jps -m -l PID 这样会列出

    21996 /usr/local/platform/patient/bin/patient.jar –spring.profiles.active=test

    21996是进程id

    /usr/local/platform/patient/bin/patient.jar是详细的进程名称

    –spring.profiles.active=test是传入main类或者jar包的参数

  2. jstack :查看某个java进程内的堆栈信息,根据堆栈信息,我们可以定位到具体代码,在jvm性能调优中使用的特别多!

    我一般这么用: jstack -m -l PID

    其中 -m 参数不仅会列出java对战信息,还会列出系统级别native方法的c/c++堆栈信息

    -l 会打印出额外的锁信息

    栗子:我们找我patient这个java进程最消耗CPU的线程

  • 首先用jps获取patient这个java进程的ID

    21996 /usr/local/platform/patient/bin/patient.jar –spring.profiles.active=test

  • 得到进程ID为21996,第二步找出该进程内最耗费CPU的线程,可以用top -Hp 21996 得到如下:

Threads: 32 total, 0 running, 32 sleeping, 0 stopped, 0 zombie

%Cpu(s): 0.0 us, 0.3 sy, 0.0 ni, 99.7 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st

KiB Mem : 1867248 total, 94444 free, 1592828 used, 179976 buff/cache

KiB Swap: 2097148 total, 2064196 free, 32952 used. 73720 avail Mem

PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND

21996 root 20 0 2564180 241912 7580 S 0.0 13.0 0:00.00 java

21999 root 20 0 2564180 241912 7580 S 0.0 13.0 0:05.11 java

22000 root 20 0 2564180 241912 7580 S 0.0 13.0 0:16.09 java

22001 root 20 0 2564180 241912 7580 S 0.0 13.0 0:00.00 java

22002 root 20 0 2564180 241912 7580 S 0.0 13.0 0:00.01 java

22003 root 20 0 2564180 241912 7580 S 0.0 13.0 0:00.00 java

22004 root 20 0 2564180 241912 7580 S 0.0 13.0 0:15.24 java

22005 root 20 0 2564180 241912 7580 S 0.0 13.0 0:06.93 java

22006 root 20 0 2564180 241912 7580 S 0.0 13.0 0:00.00 java

22007 root 20 0 2564180 241912 7580 S 0.0 13.0 5:06.79 java

22081 root 20 0 2564180 241912 7580 S 0.0 13.0 0:01.29 java

22082 root 20 0 2564180 241912 7580 S 0.0 13.0 0:01.67 java

22101 root 20 0 2564180 241912 7580 S 0.0 13.0 0:00.43 java

22121 root 20 0 2564180 241912 7580 S 0.0 13.0 0:11.48 java

22123 root 20 0 2564180 241912 7580 S 0.0 13.0 0:01.42 java

22149 root 20 0 2564180 241912 7580 S 0.0 13.0 0:13.73 java

22150 root 20 0 2564180 241912 7580 S 0.0 13.0 0:17.81 java

22232 root 20 0 2564180 241912 7580 S 0.0 13.0 0:08.78 java

22233 root 20 0 2564180 241912 7580 S 0.0 13.0 0:00.10 java

22234 root 20 0 2564180 241912 7580 S 0.0 13.0 0:00.03 java

22235 root 20 0 2564180 241912 7580 S 0.0 13.0 0:00.00 java

22236 root 20 0 2564180 241912 7580 S 0.0 13.0 0:00.00 java

22237 root 20 0 2564180 241912 7580 S 0.0 13.0 0:00.00 java

22238 root 20 0 2564180 241912 7580 S 0.0 13.0 0:00.00 java

22239 root 20 0 2564180 241912 7580 S 0.0 13.0 0:00.00 java

22240 root 20 0 2564180 241912 7580 S 0.0 13.0 0:00.00 java

22241 root 20 0 2564180 241912 7580 S 0.0 13.0 0:00.00 java

22242 root 20 0 2564180 241912 7580 S 0.0 13.0 0:00.00 java

22243 root 20 0 2564180 241912 7580 S 0.0 13.0 0:07.24 java

22244 root 20 0 2564180 241912 7580 S 0.0 13.0 0:00.02 java

22245 root 20 0 2564180 241912 7580 S 0.0 13.0 0:14.57 java

36964 root 20 0 2564180 241912 7580 S 0.0 13.0 0:00.00 java

其中TIME+这一列是指耗时时间,这一列里面22007 最耗时,我们在命令行里用printf “%x ” 22007 得到16进制55f7

  • 这时候用jstack 21996 55f7 来输出堆栈信息如下:

    “VM Periodic Task Thread” os_prio=0 tid=0x00007ff3980f0800 nid=0x55f7 waiting on condition

    经过发现这个堆栈信息显示的线程状态是waiting on condition,意思是正在等待网络读写,这可能是一个网络瓶颈征兆。

    如果这里列出来的是一个java类名,则就可以找到项目里的类,结合堆栈信息,进行性能改进

  1. jmap: 一般结合jhat用来查看堆栈内存使用情况,

    jmap -heap 21996 显示堆内存如下:

Attaching to process ID 21996, please wait

Debugger attached successfully.

Server compiler detected.

JVM version is 25.141-b16

using thread-local object allocation.

Mark Sweep Compact GC

Heap Configuration:

MinHeapFreeRatio = 40

MaxHeapFreeRatio = 70

MaxHeapSize = 478150656 (456.0MB)

NewSize = 10485760 (10.0MB)

MaxNewSize = 159383552 (152.0MB)

OldSize = 20971520 (20.0MB)

NewRatio = 2

SurvivorRatio = 8

MetaspaceSize = 21807104 (20.796875MB)

CompressedClassSpaceSize = 1073741824 (1024.0MB)

MaxMetaspaceSize = 17592186044415 MB

G1HeapRegionSize = 0 (0.0MB)

这里我们可以看到我们堆内存各个区的内存大小。注意:这个是jdk1.8的,其中MetaspaceSize代替了PermGen

带你入门jvm性能优化

java

优化

上面介绍了用jvm的工具查看java进程,并查找相应堆栈信息,根据堆栈信息优化程序,也列出了堆栈内存的大小信息,要避免内存溢出,在程序在承受较大访问量的时候,程序仍然可以健康运行,就要根据自己机器情况,多做几次抗压试验,找出性能最好的参数设置

首先jdk8 运行时通常包含 计数器,栈,堆,本地方法栈,方法区,堆。每个部分的优化参数名称如下:

Xss:每个线程的栈内存大小

Xmx:JAVA HEAP的最大值、默认为物理内存的1/4

Xms:JAVA HEAP的初始值,server端最好Xms与Xmx一样

Xmn:JAVA HEAP young区的大小

XX:PermSize:设定内存的永久保存区域

XX:MaxPermSize:设定最大内存的永久保存区域

我基于jdk1.8 做了一个优化参数,我司单台服务器的配置(2核8G),单台服务器我部署2台服务,配置如下(主要是堆栈)

exec java

-Dfile.encoding=UTF-8

-Xss1024K//栈内存1m

-Xms1024m//堆内存最小1g

-Xmx2048m//堆内存最大2g

-Xmn256m //yong

-XX:MaxMetaspaceSize=512m //方法区512m

带你入门jvm性能优化

java

###常规项目优化

-XX:+DisableExplicitGC //忽略手动调用GC, System.gc()的调用就会变成一个空调用,完全不触发GC

-XX:+UseConcMarkSweepGC //并发标记清除(CMS)收集器

-XX:+CMSParallelRemarkEnabled //降低标记停顿

-XX:LargePageSizeInBytes=128m //内存页的大小

-XX:+UseFastAccessorMethods //原始类型的快速优化

-XX:+UseCMSInitiatingOccupancyOnly //使用手动定义初始化定义开始CMS收集

-XX:CMSInitiatingOccupancyFraction=70 //使用cms作为垃圾回收使用70%后开始CMS收集-Duser.timezone=GMT+8 //避免CentOS坑爹的时区设置

-XX:+UseG1GC

-XX:MaxGCPauseMillis=100

-XX:InitiatingHeapOccupancyPercent=35

######gc信息打印

-verbose:gc

-XX:+PrintGCDetails -XX:+PrintGCDateStamps

-XX:+PrintGCTimeStamps

-XX:+PrintGCApplicationStoppedTime

-Xloggc:${BASE_DIR}/logs/jvm_gc.log

-XX:ErrorFile=${BASE_DIR}/logs/jvm_err.log

-XX:+HeapDumpOnOutOfMemoryError

-XX:HeapDumpPath=${BASE_DIR}/logs/jvm_dump_pid%p.hprof

-jar /usr/local/platform/patient/bin/patient.jar --spring.profiles.active=prod

各位项目还是要根据自己公司服务器配置情况来进行相应优化测试

java学习+QQ:1638812475