dockerfile指定jvm参数 Docker java jvm OOM内存溢出问题排查 诊断监控

Published on with 0 views and 0 comments

实例

FROM java:8
VOLUME /tmp
ADD app.jar app.jar
RUN bash -c 'touch /app.jar'
EXPOSE 9001
ENV JAVA_OPTS="
-server
-Xmx4g
-Xms4g
-Xmn2g
-XX:SurvivorRatio=8
-XX:MetaspaceSize=256m
-XX:MaxMetaspaceSize=512m
-XX:+UseParallelGC
-XX:ParallelGCThreads=4
-XX:+UseParallelOldGC
-XX:+UseAdaptiveSizePolicy
-XX:+PrintGCDetails
-XX:+PrintTenuringDistribution
-XX:+PrintGCTimeStamps
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/
-Xloggc:/gc.log
-XX:+UseGCLogFileRotation
-XX:NumberOfGCLogFiles=5
-XX:GCLogFileSize=10M"
ENTRYPOINT java ${JAVA_OPTS} -Djava.security.egd=file:/dev/./urandom -jar /app.jar

不过这种方式在dockerfile写死了不灵活,不是很推荐,特别是要区分env的应用来说。

docker run指定

docker run --rm -e JAVA_OPTS='-Xmx1g' tomcat

docker-compose方式指定

environment:
  - JVM_OPTS=-Xmx12g -Xms12g -XX:MaxPermSize=1024m

k8s指定

apiVersion: v1
kind: ReplicationController
metadata:
  labels:
    app: jenkins
    role: master
    version: v1
  name: jenkins-master
  namespace: jenkins
spec:
  replicas: 1
  selector:
    app: jenkins
    role: master
    version: v1
  template:
    metadata:
      labels:
        app: jenkins
        role: master
        version: v1
    spec:
      containers:
      - env:
        - name: JENKINS_OPTS
          value: --prefix=
        - name: JAVA_OPTS
          value: -Djava.awt.headless=true -Xmx200m -Dcom.sun.management.jmxremote=
            -Dcom.sun.management.jmxremote.port=1099 -Dcom.sun.management.jmxremote.ssl=false
            -Dcom.sun.management.jmxremote.authenticate=false
        image: jenkins:2.7.2
        name: jenkins
        ports:
        - containerPort: 8080
          name: web
        - containerPort: 50000
          name: leader
        resources:
          limits:
            cpu: 1000m
            memory: 800Mi
          requests:
            cpu: 100m
            memory: 400Mi
        volumeMounts:
        - mountPath: /var/jenkins_home
          name: jenkins-master
      nodeSelector:
        worker: "true"
      volumes:
      - name: jenkins-master
        nfs:
          path: /data/jenkins-master
          server: '{{nfs_server}}'

doc

dockerfile

docker环境无法执行jmap -heap 56命令

很奇怪的问题,但是jstack可以执行

解决方法:

docker 内部使用jmap 需要启动容器时候配置权限:docker run --privileged=true --cap-add=SYS_PTRACE

Docker容器内监控
查看docker运行状态
docker stats cid

top 找到耗费关键资源的进程
ps -ef|grep java 通过PID找到和top命令输出的PID

docker ps 找到进入的容器id
docker exec -it 1231sdf1323 /bin/sh 进入容器
jps 或 jps -l -m 找到java 进程pid

查看容器重启次数
docker inspect -f “{{ .RestartCount }}” container-id

查看容器最后一次的启动时间
docker inspect -f “{{ .State.StartedAt }}” container-id

docker 启动命令增加 内存泄露时导出heapdump文件
-e ‘JAVA_OPTS=-server -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/data/logs/heapdump.hprof’

jmap 内存分析
ps – ef | grep java 查看PID
jmap -heap 6 可以用来查看堆内存的使用详情
jmap -histo:live 6 | more 查看堆内存中的对象的数目,占用内存(单位是byte),如果带上live则只统计活对象
jmap -dump:live,format=b,file=heapLive.hprof 6 jmap把进程内存使用情况dump到文件中,或者dump**.hprof**文件,在本地使用MAT(Eclipse Memory Analyzer)进行分析。也可以直接用jhat分析查看
jmap -heap 10765
查看新生代,老生代堆内存的分配大小以及使用情况,看是否本身分配过小
jmap -histo:live 10765 | more
查找最耗内存的对象 以表格的形式显示存活对象的信息,并按照所占内存大小排序
jmap -dump:format=b,file=dumpfile.hprof pid
使用jmap生成堆转储文件 使用JProfiler、MAT内存分析。

jmap -dump:format=b, file=dumpfile.hprof pid
docker cp 29198c060396:/dumpfile.hprof .
导出jmap dump的文件,进一步分析,copy docker中的文件到宿主机

jmap -heap pid
查看进程堆内存使用情况,包括使用的GC算法、堆配置参数和各代中堆内存使用情况

jmap -histo[:live] pid
查看堆内存中的对象数目、大小统计直方图,如果带上live则只统计活对象

jmap -dump
把进程内存使用情况dump到文件中,再用jhat分析查看
jmap -dump:format=b,file=heapdump.hprof pid
dump出来的文件可以用MAT、VisualVM等工具查看,用jhat查看命令:
jhat -port 9998 /tmp/dump.dat
注意如果Dump文件太大,可能需要加上-J-Xmx512m这种参数指定最大堆内存,即jhat -J-Xmx512m -port 9998 /tmp/dump.dat。然后就可以在浏览器中输入主机地址:9998查看了

jstack 查看线程信息
jstack pid
jstack -l 6 用来查看Java进程内的线程堆栈信息。

jstat 性能分析 (JVM统计监测工具)
Docker容器中使用jstat过程
列出docker容器:docker ps
标准输入和关联终端:docker exec -it 容器ID /bin/bash
查找出java进程: ps – ef | grep java
统计gc信息统计: jstat –gcutil 466 3000 每三秒打印一次

语法格式如下:
jstat [ generalOption | outputOptions vmid [interval[s|ms] [count]] ]
vmid是虚拟机ID,在Linux/Unix系统上一般就是进程ID。
interval是采样时间间隔。
count是采样数目。比如下面输出的是GC信息,采样时间间隔为250ms,采样数为4
命令:
jstat -gc 21711 250 4
堆内存 = 年轻代 + 年老代 + 永久代
年轻代 = Eden区 + 两个Survivor区(From和To)

1.jstat -gc pid
可以显示gc的信息,查看gc的次数,及时间。
其中最后五项,分别是young gc的次数,young gc的时间,full gc的次数,full gc的时间,gc的总时间。
2.jstat -gccapacity pid
可以显示,VM内存中三代(young,old,perm)对象的使用和占用大小,
如:PGCMN显示的是最小perm的内存使用量,PGCMX显示的是perm的内存最大使用量,
PGC是当前新生成的perm内存占用量,PC是但前perm内存占用量。
其他的可以根据这个类推, OC是old内纯的占用量。
3.jstat -gcutil pid
统计gc信息统计。
4.jstat -gcnew pid
年轻代对象的信息。
5.jstat -gcnewcapacity pid
年轻代对象的信息及其占用量。
6.jstat -gcold pid
old代对象的信息。
7.stat -gcoldcapacity pid
old代对象的信息及其占用量。
8.jstat -gcpermcapacity pid
perm对象的信息及其占用量。
9.jstat -class pid
显示加载class的数量,及所占空间等信息。
10.jstat -compiler pid
显示VM实时编译的数量等信息。
11.stat -printcompilation pid
当前VM执行的信息。
jstat -gccause pid 1 每格1毫秒输出结果
jstat -gccause pid 3000 每格3秒输出结果

图中参数含义如下:
S0 — Heap上的 Survivorspace 0 区已使用空间的百分比
S1 — Heap上的 Survivorspace 1 区已使用空间的百分比
E — Heap上的 Eden space区已使用空间的百分比
O — Heap上的 Old space 区已使用空间的百分比
P — Perm space 区已使用空间的百分比
YGC — 从应用程序启动到采样时发生 YoungGC 的次数
YGCT –从应用程序启动到采样时 Young GC 所用的时间(单位秒)
FGC — 从应用程序启动到采样时发生 Full GC的次数
FGCT –从应用程序启动到采样时 Full GC 所用的时间(单位秒)
GCT — 从应用程序启动到采样时用于垃圾回收的总时间(单位秒)

S0C、S1C、S0U、S1U:Survivor 0/1区容量(Capacity)和使用量(Used)
EC、EU:Eden区容量和使用量
OC、OU:年老代容量和使用量
PC、PU:永久代容量和使用量
YGC、YGT:年轻代GC次数和GC耗时
FGC、FGCT:Full GC次数和Full GC耗时
GCT:GC总耗时

查看jvm 配置信息
docker exec -it xxx /bin/sh

java -XX:+PrintCommandLineFlags 打印jvm启动参数配置
java -XX:+PrintGCDetails 打印GC运行信息

jinfo -flags pid docker 容器内java进程pid默认为1

内存分析工具
jvisualvm jdk自带的工具
mac 终端 直接运行 jvisualvm

jconsole jdk 工具
mac 终端直接运行 jconsole

JProfiler
mat
Java 内存泄漏排查
mat

gc.log分析
gceasy


标题:dockerfile指定jvm参数 Docker java jvm OOM内存溢出问题排查 诊断监控
作者:woyehua
地址:https://blog.stormbirds.cn/articles/2022/08/25/1661406805518.html