为了账号安全,请及时绑定邮箱和手机立即绑定

JVM性能调优实践——JVM篇

标签:
Java

前言

在遇到实际性能问题时,除了关注系统性能指标。还要结合应用程序的系统的日志、堆栈信息、GClog、threaddump等数据进行问题分析和定位。关于性能指标分析可以参考前一篇JVM性能调优实践——性能指标分析。

JVM的调优和故障处理可以使用JDK的几个常用命令工具。因为本文是基于Docker容器内部的Springboot服务。需要调整一下docker容器的启动参数,才可以使用jmap等工具。jmap命令需要使用Linux的Capability的PTRACE_ATTACH权限。而Docker自1.10在默认的seccomp配置文件中禁用了PTRACE_ATTACH。目前使用的Docker version是17.04.0-ce。支持的Capability列表可以详看runtime-privilege-and-linux-capabilities。

调整Capability的方式也比较方便。可以如下直接在运行参数后面加 cap_add,cap-drop

$docker run --cap-add=ALL --cap-drop=MKNOD ...1

也可以在compose中增加:

cap_add: - ALL cap_drop: - NET_ADMIN - SYS_ADMIN12345

Docker容器中的服务进程

在排查问题时,一般是先通过JVM性能调优实践——性能指标分析中的几个命令来分析基础的服务器状态和信息。在微服务架构中,每台服务器部署着若干运行着服务的容器。在不能通过应用日志或者问题现象定位问题服务时,需要找到问题容器。

先通过TOP命令找到耗费关键资源的进程。

top - 11:45:13 up 318 days, 20:43, 2 users, load average: 0.15, 0.19, 0.18Tasks: 172 total, 1 running, 171 sleeping, 0 stopped, 0 zombie%Cpu(s): 3.1 us, 1.9 sy, 0.0 ni, 94.7 id, 0.0 wa, 0.0 hi, 0.3 si, 0.0 stKiB Mem: 8175392 total, 7868636 used, 306756 free, 204400 buffersKiB Swap: 0 total, 0 used, 0 free. 849564 cached Mem PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 31399 root 20 0 3585612 806804 12228 S 3.0 9.9 548:20.94 java 6331 root 20 0 3445612 925660 15784 S 2.7 11.3 41:40.29 java 31122 root 20 0 3460712 888776 11568 S 2.0 10.9 484:19.31 java 31147 root 20 0 3288180 811476 12748 S 1.3 9.9 263:44.73 java 8506 root 20 0 3254088 750880 6116 S 1.0 9.2 760:45.19 java 22940 root 20 0 1029012 70584 23396 S 0.7 0.9 0:10.68 node 24550 root 20 0 1229088 43096 8712 S 0.7 0.5 160:15.74 node 7 root 20 0 0 0 0 S 0.3 0.0 606:49.74 rcu_sched 454 sshd 20 0 32792 1924 188 S 0.3 0.0 29:15.40 nginx 13721 root 20 0 25396 1956 1324 S 0.3 0.0 56:29.17 AliYunDunUpdate 16225 root 20 0 3072752 429296 6848 S 0.3 5.3 42:51.01 java 20795 root 20 0 2408848 75344 3960 S 0.3 0.9 2361:22 java 23581 root 20 0 16736 2676 2196 R 0.3 0.0 0:00.01 top 31352 root 20 0 206920 1488 1024 S 0.3 0.0 1:20.48 docker-containe 32000 root 20 0 3061760 403708 6548 S 0.3 4.9 127:01.39 java ... 省略其他信息1234567891011121314151617181920212223

因为Docker容器中还有java进程,所以需要找到具体的父子进程id.用ps -ef命令如下所示。第二列是PID(进程ID),第三列是PPID(父进程ID)。

$ps -ef |grep java root 6310 6293 0 May21 ? 00:00:00 /bin/sh -c java -Dcontainer.host.ip=...root 6331 6310 2 May21 ? 00:41:51 java -Dcontainer.host.ip= -server ... root 8482 8465 0 Apr16 ? 00:00:00 /bin/sh -c java -Dcontainer.host.ip...root 8506 8482 1 Apr16 ? 12:40:53 java -Dcontainer.host.ip= -server...... 省略其他信息123456

可以使用docker inspect查看容器内部信息,找到对应的容器实例的进程信息。如下即可打印当前宿主机的所有运行的容器实例的PID,为了方便映射,可以打印对应容器名字,或者容器ID:

## 打印容器pid和容器id$docker ps -q | xargs docker inspect --format '{{.State.Pid}}, {{.ID}}' | grep "^${PID}"## 打印容器pid和容器name $ docker ps -q | xargs docker inspect --format '{{.State.Pid}}, {{.Name}}' | grep "^${PID}" 6310, /service-item31369, /gateway-api31094, /service-resource31025, /service-trade30916, /service-user16204, /service-analytics8482, /service-financial... 省略其他信息12345678910111213

如果要分析最消耗内存的进程,对应的pid= 6331,其所在的docker进程id也即父进程id= 6310,可以定位出service-item服务最消耗内存资源。定位到服务之后,即可使用docker exec -it service-item ‘/bin/sh’查看容器内部信息。

JVM调优基础命令

在容器内部,就可以进一步使用jdk提供的jps、jstack、jstat、jmap等工具来进行jvm问题排查和调优。

jps[options] [hostid]

jps主要用来输出JVM中运行的进程状态信息。

-q 输出类名、Jar名和传入main方法的参数

-m 输出传入main方法的参数

-l 输出main类或Jar的全限名

-v 输出传入JVM的参数

如下查看运行的java进程信息,打印jar名以及运行main方法传入的参数:

/opt/app # jps -l -m6 /opt/app/app.jar --server.port=8080327 sun.tools.jps.Jps -l -m1234

jstat

jstat - [-t] [-h]  [ [] 1

jstat命令可以用于持续观察虚拟机内存中各个分区的使用率以及GC的统计数据。vmid是Java虚拟机ID,在Linux/Unix系统取进程ID。

如下面输出的信息,采样时间间隔为1000ms,采样5次:

/opt/app # jstat -gc 6 1000 5 S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT 1536.0 1536.0 1233.7 0.0 171520.0 169769.2 249344.0 57018.6 93912.0 91906.8 11264.0 10853.7 6224 47.439 5 3.423 50.8631536.0 1536.0 1233.7 0.0 171520.0 169805.4 249344.0 57018.6 93912.0 91906.8 11264.0 10853.7 6224 47.439 5 3.423 50.8631536.0 1536.0 0.0 1536.0 171520.0 3527.9 249344.0 60347.4 96728.0 94808.1 11520.0 11174.7 6225 47.453 5 3.423 50.8761536.0 1536.0 0.0 1536.0 171520.0 4742.1 249344.0 60347.4 96728.0 94808.1 11520.0 11174.7 6225 47.453 5 3.423 50.8761536.0 1536.0 0.0 1536.0 171520.0 7589.3 249344.0 60347.4 96728.0 94808.1 11520.0 11174.7 6225 47.453 5 3.423 50.87612345678

上述各个列的含义:

S0C、S1C、S0U、S1U:young代的Survivor 0/1区容量(Capacity)和使用量(Used)。0是FromSurvivor,1是ToSurvivor。

EC、EU:Eden区容量和使用量

OC、OU:年老代容量和使用量

MC、MU:元数据区(Metaspace)已经committed的内存空间和使用量

CCSC、CCSU:压缩Class(Compressed class space)committed的内存空间和使用量。

YGC、YGT:young代GC次数和GC耗时

FGC、FGCT:Full GC次数和Full GC耗时

GCT:GC总耗时

可以通过分区占用量上看到,在第2-3秒之间发生了一次YGC。YGC次数+1,并且Survivor from区的内存空间从1233.7->0,Survivor from从0->1536。Eden区也释放了很多内存空间。其他变化的空间占用也有元数据区以及元数据区的压缩Class区。Compressed class space也是元数据区的一部分,默认是1G,也可以关闭。具体的jvm8内存分布不再详述。下一篇GC优化会再展开整理下。

如果只看gc的总统计信息,也可以用jstat -gcutil vmid查询:

/opt/app # jstat -gcutil 6 S0 S1 E O M CCS YGC YGCT FGC FGCT GCT 0.00 100.00 73.76 24.20 98.02 97.00 6225 47.453 5 3.423 50.876 123

jmap [option] pid

jmap可以用来查看堆内存的使用详情。内存各个分区可以通过jmap -heap pid来查看。得到的输出如下:

$jmap -heap 6Attaching to process ID 6, please wait...Debugger attached successfully.Server compiler detected.JVM version is 25.121-b13using thread-local object allocation.Parallel GC with 2 thread(s)Heap Configuration: MinHeapFreeRatio = 0 MaxHeapFreeRatio = 100 MaxHeapSize = 536870912 (512.0MB) NewSize = 44564480 (42.5MB) MaxNewSize = 178782208 (170.5MB) OldSize = 89653248 (85.5MB) NewRatio = 2 SurvivorRatio = 8 MetaspaceSize = 21807104 (20.796875MB) CompressedClassSpaceSize = 1073741824 (1024.0MB) MaxMetaspaceSize = 17592186044415 MB G1HeapRegionSize = 0 (0.0MB)Heap Usage:PS Young GenerationEden Space: capacity = 170393600 (162.5MB) used = 99020080 (94.43290710449219MB) free = 71373520 (68.06709289550781MB) 58.11255821814904% usedFrom Space: capacity = 4194304 (4.0MB) used = 786432 (0.75MB) free = 3407872 (3.25MB) 18.75% usedTo Space: capacity = 4194304 (4.0MB) used = 0 (0.0MB) free = 4194304 (4.0MB) 0.0% usedPS Old Generation capacity = 255328256 (243.5MB) used = 65264912 (62.24147033691406MB) free = 190063344 (181.25852966308594MB) 25.561178783126927% used39531 interned Strings occupying 4599760 bytes.1234567891011121314151617181920212223242526272829303132333435363738394041424344454647



作者:Java小铺
链接:https://www.jianshu.com/p/bfa8c1433a21


点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
全栈工程师
手记
粉丝
228
获赞与收藏
996

关注作者,订阅最新文章

阅读免费教程

  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消