一、场景介绍
-
在线上运行的应用程序,如果出现
OOM等等JVM的异常,我们需要通过灾难现场来判断问题代码的所在 -
如果是传统的
Tomcat等服务器部署,则可以直接使用服务器的JDK环境变量自带的例如:jmapjstack这些内存分析工具进行问题的分析 -
如果是通过
Docker容器部署,想去查看容器内应用的堆栈信息,则需要根据不同容器化启动方式来具体分析
二、基于 JRE 环境运行 Docker 容器的 JVM 调优
-
场景一:基于
Alpine JRE基础镜像运行的Docker容器-
基于该场景的容器,由于采用的是精简版的
JRE,容器内部没有类似jmapjstack这些内存分析工具 -
如果想通过
jmapjstack这些内存分析工具进行JVM调优,我们必须在容器内部添加JDK的环境 -
添加方式可以是在线安装方式或者将
JDK环境复制到容器内部的方式,根据自己的实际情况来抉择,具体操作步骤如下:-
进入容器内部,查看该容器内部是否拥有类似
jmapjstack这些内存分析工具# 进入容器内部 [root@node42 ~]# docker exec -it example-service /bin/sh # 查看是否拥有调优命令 /opt/java/example-service # jmap /bin/sh: jmap: not found /opt/java/example-service # jstack /bin/sh: jstack: not found /opt/java/example-service # -
我们可以尝试在容器内部安装
OpenJDK来解决# 进入容器内部 docker exec -it container-name /bin/sh # 更新 apk 源 /opt/java/example-service # echo https://mirrors.aliyun.com/alpine/v3.14/main > /etc/apk/repositories && echo https://mirrors.aliyun.com/alpine/v3.14/community >> /etc/apk/repositories # 安装 OpenJDK /opt/java/example-service # apk update && apk upgrade && apk add openjdk8 # 查看 OpenJDK 是否安装成功 /opt/java/example-service # ls -l /usr/lib/jvm/java-1.8-openjdk total 184 -r--r--r-- 1 root root 1522 Apr 20 15:03 ASSEMBLY_EXCEPTION -r--r--r-- 1 root root 19274 Apr 20 15:03 LICENSE -r--r--r-- 1 root root 155003 Apr 20 15:03 THIRD_PARTY_README drwxr-xr-x 1 root root 4096 Jul 19 16:05 bin drwxr-xr-x 3 root root 132 Jul 19 16:05 include drwxr-xr-x 1 root root 95 Jul 19 16:04 jre drwxr-xr-x 1 root root 126 Jul 19 16:05 lib -rw-r--r-- 1 root root 84 Apr 20 15:03 release # 进入 OpenJDK 二进制目录(由于在线安装的没有配置环境变量,这里就直接在二进制目录进行 JVM 参数排查操作) /opt/java/example-service # cd /usr/lib/jvm/java-1.8-openjdk/bin -
通过新增的
OpenJDK来分析内存信息/usr/lib/jvm/java-1.8-openjdk/bin # ps -ef PID USER TIME COMMAND 1 root 2h16 java -jar ./example-service.jar 891 root 0:00 sh 1106 root 0:00 ps -ef /opt/java/example-service # /usr/lib/jvm/java-1.8-openjdk/bin/./jstack 6 2021-07-19 16:28:09 Full thread dump OpenJDK 64-Bit Server VM (25.212-b04 mixed mode): "Keep-Alive-Timer" #49 daemon prio=8 os_prio=0 tid=0x00007f76b08ff000 nid=0x62 waiting on condition [0x00007f768925d000] java.lang.Thread.State: TIMED_WAITING (sleeping) at java.lang.Thread.sleep(Native Method) at sun.net.www.http.KeepAliveCache.run(KeepAliveCache.java:172) at java.lang.Thread.run(Thread.java:748) -
其它问题分析和解决方案
-
考虑到以后可能需要多次使用本次下载下来的
OpenJDK,我们可以将容器中的OpenJDK拷贝出来,方便后续再次使用时,再次拷贝到容器内部使用# 将 OpenJDK 拷贝到宿主机 docker cp example-service:/usr/lib/jvm/java-1.8-openjdk . # 将宿主机的 OpenJDK 拷贝到容器内部 docker cp java-1.8-openjdk example-service:/usr/lib/jvm/ -
如果分析过程中出现如下问题:
/usr/lib/jvm/java-1.8-openjdk/bin # ps -ef PID USER TIME COMMAND 1 root 2h16 java -jar ./example-service.jar 891 root 0:00 sh 1106 root 0:00 ps -ef /usr/lib/jvm/java-1.8-openjdk/bin # ./jstack 1 1: Unable to get pid of LinuxThreads manager thread /usr/lib/jvm/java-1.8-openjdk/bin # ./jmap -histo 1 1: Unable to get pid of LinuxThreads manager thread /usr/lib/jvm/java-1.8-openjdk/bin #请采用
docker --init的方式启动容器后,再次尝试即可
-
-
-
-
场景二: 基于宿主机的
JRE环境运行的Docker容器-
基宿主机的
JRE环境运行的Docker容器,同样也没有jmapjstack这些内存分析工具[root@node42 ~]# docker exec -it example-service /bin/sh /opt/java/example-service # jmap /bin/sh: jmap: not found /opt/java/example-service # jstack /bin/sh: jstack: not found /opt/java/example-service # -
我们可以通过更换宿主机的
JRE为JDK环境,或者进入容器内部安装OpenJDK来进行JVM调优 -
以上方案一可以参考网上资料。搜索关键字
Linux 安装配置 JDK 环境,方案二可以参考本段场景一
-
三、基于 JDK 环境运行 Docker 容器的 JVM 调优
-
场景一:基于
Alpine OpenJDK基础镜像运行的Docker容器-
基于
OpenJDK基础镜像运行的Docker容器,本身拥有jmapjstack这些内存分析工具 -
我们可以直接采用
jmapjstack这些内存分析工具来进行应用的JVM调优 -
我们还可以通过阿里开源的 Arthas 来进行
JVM调优
P.S
-
Arthas也是通过JDK中的jps去查找JAVA进程的,所以运行时JAVA环境变量必须要有jps这些命令 -
很多时候,应用在
Docker里出现Arthas无法工作的问题,是因为应用没有安装JDK,而是安装了JRE。如果只安装了JRE,则会缺少很多JAVA的命令行工具和类库,Arthas也没办法正常工作。下面介绍两种常见的在Docker里使用JDK的方式FROM openjdk:8-jdk # 或者 FROM openjdk:8-jdk-alpine -
Docker容器内部的应用的PID=1可能会出现无法Attach,请尽量采用非PID=1的方式运行应用,应用PID=1为系统默认进程docker run --init .........
-
-
场景二:基于宿主机的
JDK环境运行的Docker容器-
基于宿主机的
JDK环境变量运行的Docker容器,本身拥有jmapjstack这些内存分析工具 -
我们可以直接采用
jmapjstack这些内存分析工具来进行应用的JVM调优 -
我们还可以通过阿里开源的 Arthas 来进行
JVM调优 -
切记在此场景下通过
Arthas进行调优,请采用和宿主机拥有相同root权限的命令启动容器,不然会出现无法Attach的情况,不管内部应用的PID是否为1[INFO] Try to attach process 1 Error: A JNI error has occurred, please check your installation and try again Exception in thread "main" java.io.IOException: Bad pathname at java.lang.ClassLoader.findBootstrapClass(Native Method) at java.lang.ClassLoader.findBootstrapClassOrNull(ClassLoader.java:1008) at java.lang.ClassLoader.loadClass(ClassLoader.java:407) at java.lang.ClassLoader.loadClass(ClassLoader.java:405) at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:355) at java.lang.ClassLoader.loadClass(ClassLoader.java:351) at sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:601) [ERROR] attach fail, targetPid: 1docker run --privileged=true .........
P.S
-
Docker容器内部的应用请尽量采用非PID=1的方式运行应用,应用PID=1为系统默认进程docker run --init --privileged=true .........
-
本文详细介绍了在Docker环境下基于JRE和JDK运行的应用如何进行JVM调优。针对JRE环境,通过在容器内部安装OpenJDK以使用jmap和jstack等工具进行内存分析;对于JDK环境,直接利用内置工具进行调优。同时提到了Arthas在Docker中的使用,并强调了避免应用以PID 1运行以确保工具的正常使用。
2905

被折叠的 条评论
为什么被折叠?



