01、线上JVM调优
1.主要参数
#JVM x参数 #非标准化参数 -Xint: 解释执行 -Xcomp:第一次使用就编译成本地代码 -Xmixed:混合模式,JVM自己来决定是否编译成本代码 #XX参数分类 格式:-XX:[+-]<name>表示启用或者禁用name属性比如: -XX:+UseConcMarkSweepGC -XX:UseG1GC 非Boolean类型格式:-XX:<name> = <value>表示name属性的值是value比如: -XX:MaxGCPauseMillis=500 XX:GCTimeRatio=19 不是X参数,而是XX参数 -Xms等价于-XX:InitialHeapSize-Xmx等价于-XX:MaxHeapSize 查看JVM运行时参数 -XX:+PrintFlagsInitial-XX:+PrintFlagsFinal-XX:+UnlockExperimentalIVMOptions解锁实验参数2.怎么查看项目呢?
这里主要讲Java项目,其它项目不在考虑范围之内。
jps --查看有哪些线程 jps -l --查看详细的线程名称
启动项目:
jinfo -flag MaxHeapSize 22244 --查看jvm参数 jinfo -flags 22244 --查看jvm所有的参数 jstat 查看JVM统计信息--JIT编译jstat -compiler 22244 --类装载jstat -class 22244 1000 10 --垃圾回收jstat -gc 22244 1000 3 #如何导出内存映象文件#内存溢出字段导出-XX:+HeapDumpOnOutOfMemoryError-XX:HeapDumpPath=./ #使用jmap命令手动导出:jmap -dump:format=b,file=heap.hprof 22244 #查看死锁或者线程的状态jstack 22244 > 22244.txt #使用jdk里面的jvisualvm 监控Java程序: --可以监控本地tomcat,也可以远程,但是必须开发IP地址和端口。 #生成字节码javap -verbose Test.class > test.txt
docker环境下怎么查看项目JVM信息,通过上面的命令?
首先对spring项目进行打包:
同时创建一个Dockerfile文件和把Jar上传到服务上:
# Dockerfile文件内容FROM openjdk:alpine RUN echo "Asia/Shanghai" > /etc/timezone#RUN cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtimeENV TZ=Asia/Shanghai# 添加项目构建出来的 Jar 包ADD spring-0.0.1-SNAPSHOT.jar /root/bin/starter.jar WORKDIR /root/bin EXPOSE 8080 # 设定镜像的主命令CMD ["java", "-jar", "starter.jar", "--spring.profiles.active=dev", "-Djava.security.egd=file:/dev/./urandom"]构建镜像,命令如下:
# 注意后面有个“.”号docker build -t appliaction .如果出现如下错误,表明是镜像地址有问题:
构建成功的镜像,如下:
启动容器,命令如下:
docker run -d -p 8080:8080 appliaction执行上面的命令,结果如下:
一直报这个错误:
1: Unable to get pid of LinuxThreads manager thread很让人无语,pid线程为1明明存在,就是说get不到,然后查询资料,百度说是openjdk不支持上面哪些命令,这怎么可能。然后去官网找了资料,说是要把Dockfile的启动方式改为下面的:
ENTRYPOINT ["/bin/bash", "-c", "set -e && java -Xmx100m -jar /demo.jar"]这个方式我没有试过,因为不想动Dockerfile。所以就继续查找,官网说,这其实不是什么 Bug,而是 Docker 自 1.10 版本开始加入的安全特性。类似于 jmap 这些 JDK 工具依赖于 Linux 的 PTRACE_ATTACH,而是 Docker 自 1.10 在默认的 seccomp 配置文件中禁用了 ptrace。
这篇文章介绍了整个的缘由以及应对方法:
JVM in Docker and PTRACE_ATTACH(
https://jarekprzygodzki.wordpress.com/2016/12/19/jvm-in-docker-and-ptrace_attach/)
解决方案如下,主要提及三种:
1.1 –security-opt seccomp=unconfined
简单暴力(不推荐),直接关闭 seccomp 配置。用法:
docker run --security-opt seccomp:unconfined ...1.2 –cap-add=SYS_PTRACE
使用 --cap-add 明确添加指定功能:
docker run --cap-add=SYS_PTRACE ...1.3 Docker Compose 的支持
Docker Compose 自 version 1.1.0 (2015-02-25) 起支持 cap_add。官方文档:cap_add, cap_drop。用法:
docker-compose.yml 改写后文件内容如下(内容部分省略):
version: '2'services: mysql: ... api: ... cap_add: - SYS_PTRACE所以,我打算采用下面的方式:
docker run --cap-add=SYS_PTRACE -d -p 8080:8080 appliaction得出的结论还是,同样的结果:
1: Unable to get pid of LinuxThreads manager thread
最后思考了一下,能不能把pid为1,改成不是1,改成项目的那样,22244。然后怎么设置docker 程序的pid呢?命令如下:
docker run --security-opt seccomp:unconfined -d -p 8080:8080 --pid=host appliaction生成内存文件,OK。
02、怎么分析内存泄露?
1.调用spring项目中的接口:
然后,使用top命令进行查看,内存CPU已经爆炸了,结果如下:
哪怎么导出,项目的内存相关信息的文件呢?
首先,使用命令生成内存溢出的文件:
jmap -dump:format=b,file=heap1.hprof 23427然后,把文件拷贝到服务器上,如下:
docker cp 438559ba685e:/root/bin/heap1.hprof /usr/local/docker/appliaction/heap1.hprof回到最初的问题,我们怎么分析内存泄露文件呢?
我们将使用Eclipse Memory Analysis进行分析内存文件。下载地址:
http://www.eclipse.org/mat/downloads.php
第二步:开始分析hprof文件
导入成功之后,如下显示:
将hprof文件导入Eclipse Memory Analyzer可看到上图:
1.Dominator Tree:可以列出占用内存最大的线程,以及线程下面的那些对象占用的空间
2.Leak Suspects:MA分析出的可能导致内存溢出的地方。
通过Dominator Tree,我们可以看到,MemoryController方法上有问题,user对象太多导致内存溢出,CPU飙升。
Shallow Heap :一个对象所占用的内存,不包含对其他对象的引用
Retained Heap :是shallow Heap的总和(单个对象占用内存*此对象的个数),也就是该对象被GC之后所能回收到内存的总和分析方式:
1.查找线程下占用内存较大的对象(上图右边)
2.定位对象在代码里出现的位置(上图左边)
第四步:Leak Suspects
查看线程的相关日志,定位导致内存溢出的代码位置。
通过图形知道,在MemoryController上有个userList上内存暴涨。
代码如下:
上面的代码就是用来做测试的,没有分析的必要。项目实际中,还需要分析为啥会出这个问题。
内存死锁的分析也是这样的。
jstack 23427 > 23427.txt docker cp 438559ba685e:/root/bin/23427.txt /usr/local/docker/appliaction/23427.txt sz 23427.txt
03、使用jdk的jvisualvm查看使用内存使用情况
第一步,找到jdk的安装目录,如下:
启动项目,本地查看。找到相对应的pid,如果是多个项目中,我们可以查看jvm的相关参数等信息,还可以查看图形化界面。
还可以下载堆内存等信息。
执行以下程序,内存飙升:
http://localhost:8080/heap点击堆,也可以查看是哪个user,出现了问题。
如果是实际项目呢?我们可以选择远程连接,只要是Java项目的都可以:
如果出现了以下,错误:
需要在构建的时候,指定环境变量,如下: