不同用户执行jstack导致Full GC

Execute jstack with different user cause Full GC

我用tomcat用户启动tomcat,用root用户执行jstack。我查看了gc日志,发现Full GC发生了。

我搜索并发现 sudo -u tomcat jstack -l <pid> 将解决问题。但是为什么root用户的jstack会造成full gc呢?

环境:

[root@bogon ~]# cat /etc/redhat-release 
CentOS release 6.8 (Final)
[root@bogon ~]# java -version
java version "1.8.0_65"
Java(TM) SE Runtime Environment (build 1.8.0_65-b17)
Java HotSpot(TM) 64-Bit Server VM (build 25.65-b01, mixed mode)
[root@bogon ~]# service tomcat version
vagrant: Display Tomcat Version
Using CATALINA_BASE:   /apps/tomcat
Using CATALINA_HOME:   /usr/local/tomcat
Using CATALINA_TMPDIR: /apps/tomcat/temp
Using JRE_HOME:        /usr/java/jdk1.8.0_65/
Using CLASSPATH:       /usr/local/tomcat/bin/bootstrap.jar:/usr/local/tomcat/bin/tomcat-juli.jar
Server version: Apache Tomcat/8.5.11
Server built:   Jan 10 2017 21:02:52 UTC
Server number:  8.5.11.0
OS Name:        Linux
OS Version:     2.6.32-642.15.1.el6.x86_64
Architecture:   amd64
JVM Version:    1.8.0_65-b17
JVM Vendor:     Oracle Corporation



[root@bogon tomcat]# jstack -l 2644
2644: Unable to open socket file: target process not responding or HotSpot VM not loaded
The -F option can be used when the target process is not responding

GC 日志

CommandLine flags: -XX:+DisableExplicitGC -XX:GCLogFileSize=2097152 -XX:+HeapDumpOnOutOfMemoryError -XX:InitialHeapSize=1149239296 -XX:InitiatingHeapOccupancyPercent=45 -XX:+ManagementServer -XX:MaxGCPauseMillis=200 -XX:MaxHeapSize=1149239296 -XX:MaxMetaspaceFreeRatio=80 -XX:MetaspaceSize=100663296 -XX:MinMetaspaceFreeRatio=50 -XX:NumberOfGCLogFiles=10 -XX:+PrintClassHistogram -XX:+PrintGC -XX:+PrintGCApplicationStoppedTime -XX:+PrintGCDateStamps -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintTenuringDistribution -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseG1GC -XX:+UseGCLogFileRotation -XX:+UseStringDeduplication

2017-07-05T20:13:10.591+0800: 38.999: [Full GC (Heap Inspection Initiated GC)  47M->13M(1096M), 0.0710197 secs]
   [Eden: 48.0M(54.0M)->0.0B(54.0M) Survivors: 0.0B->0.0B Heap: 47.5M(1096.0M)->13.2M(1096.0M)], [Metaspace: 15845K->15845K(1062912K)]
 [Times: user=0.04 sys=0.02, real=0.07 secs]

 num     #instances         #bytes  class name
----------------------------------------------
   1:         27256        5409512  [C
   2:          2889        3734776  [B
   3:            28         935336  [J
   4:         26366         632784  java.lang.String
   5:          4464         392832  java.lang.reflect.Method
   6:          2964         338128  java.lang.Class
   7:          9495         303840  java.util.HashMap$Node
   8:          1566         203296  [I
   9:          2576         167120  [Ljava.lang.Object;
  10:           971         120680  [Ljava.util.HashMap$Node;
  11:          3610         115520  java.util.concurrent.ConcurrentHashMap$Node
  12:          3335          69056  [Ljava.lang.Class;
  13:           924          67912  [Ljava.lang.String;
  14:          1697          67880  java.lang.ref.Finalizer
  15:          1608          64320  java.util.LinkedHashMap$Entry
  16:          1232          59136  java.util.HashMap
  17:           652          52160  java.lang.reflect.Constructor
  18:          3243          51888  java.lang.Object
  19:          1453          46496  java.util.Hashtable$Entry
  20:          1027          41080  java.util.TreeMap$Entry
  21:           711          39816  java.util.zip.ZipFile$ZipFileInputStream
  22:           708          39648  java.util.zip.ZipFile$ZipFileInflaterInputStream
  23:           308          37176  [Ljava.lang.reflect.Method;
  24:           764          36672  org.apache.tomcat.util.modeler.AttributeInfo
  25:            56          31968  [Ljava.util.concurrent.ConcurrentHashMap$Node;
  26:           442          31824  java.lang.reflect.Field
  27:            30          31200  [[C

在您的情况下,GC 是由 -XX:+PrintClassHistogram 选项引起的。此标志强制堆检查伴随 Full GC 以响应 SIGQUIT 信号。

正如您从 this answer 中发现的那样,jstack 使用 SIGQUIT 启动与目标 JVM 的通信。同样的答案解释了为什么 jstack 在 tomcat 用户下工作,但在 root 下不工作。

简而言之,SIGQUIT(也称为 kill -3)有两个作用:启动动态附加和转储 JVM 状态。如果 Dynamic Attach 失败,则 JVM 继续进行堆检查,这反过来会导致 Full GC,因为 -XX:+PrintClassHistogram 选项已打开。