分类 Java 相关 下的文章

jstack

jstack 用来打印当前 Java 进程的所有 stacktrace 信息, 包括每层栈的类名, 方法名, 行号, 以及线程的状态, 当前线程拥有的锁, 还能打印部分 native stracktrace.

jstack [ option ] pid
jstack [ option ] executable core
jstack [ option ] [server-id@]remote-hostname-or-IP

最常使用的还是跟 pid 去打印当钱进程的栈. 比如:

jstack -l 42596
jstack -l 42596 > /tmp/stack.txt
jstack -m 42596 // 打印包括 native 栈在内的所有栈. 
jstack -F 42956 //Solaris & Linux 上 force 打印栈

这个功能可以通过最新的 jcmd 来实现.

jcmd  42596 Thread.print

jinfo

jinfo 是 JDK 提供的一个查看 JVM 系统配置信息和启动参数的命令, 并且它可以在 JVM 运行状态下修改某些参数. 在 JDK 8 之后, jinfo 的很多功能都可以被新的工具 jcmd 所拥有, 更推荐使用 jcmd.

获取 java 线程 42596 的相关信息:

host:Downloads xiatian$ jinfo 42596
Attaching to process ID 42596, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.141-b15
Java System Properties:

java.vendor = Oracle Corporation
java.vm.info = mixed mode
line.separator = 

java.vm.name = Java HotSpot(TM) 64-Bit Server VM
org.apache.commons.logging.Log = org.apache.commons.logging.impl.NoOpLog
-XX:+UseG1GC
-XX:+UseStringDeduplication
-XstartOnFirstThread
-Dorg.eclipse.swt.internal.carbon.smallFonts
-Dosgi.requiredJavaVersion=1.8
-Xms256m
-Xmx1024m
-Declipse.p2.max.threads=10
java.specification.version = 1.8

VM Flags:
Non-default VM flags: -XX:CICompilerCount=4 -XX:ConcGCThreads=2 -XX:G1HeapRegionSize=1048576 -XX:InitialHeapSize=268435456 -XX:MarkStackSize=4194304 -XX:MaxHeapSize=1073741824 -XX:MaxNewSize=643825664 -XX:MinHeapDeltaBytes=1048576 -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseFastUnorderedTimeStamps -XX:+UseG1GC -XX:+UseStringDeduplication 
Command line:  -Dosgi.requiredJavaVersion=1.8 -Dosgi.instance.area.default=@user.home/eclipse-workspace -XX:+UseG1GC -XX:+UseStringDeduplication -XstartOnFirstThread -Dorg.eclipse.swt.internal.carbon.smallFonts -Dosgi.requiredJavaVersion=1.8 -Xms256m -Xmx1024m -Xdock:icon=../Resources/Eclipse.icns -XstartOnFirstThread -Dorg.eclipse.swt.internal.carbon.smallFonts -Declipse.p2.max.threads=10 

jinfo 还可以设置某些 JVM 参数, 比如:

jinfo -flag +PrintGCDetails  42596

那些参数变量是可以运行时通过 jinfo 更改的呢? 可以通过下面的命令获取当前 JRE 支持的运行时可更改的 flags:

java -XX:+PrintFlagsFinal -version|grep manageable

manageable.png
或者查看官方页面(https://www.oracle.com/technetwork/java/javase/tech/vmoptions-jsp-140102.html), 凡是标明 Manageable 的, 都是可以运行时可更改的.

一个例子是: 如果你发现有 JVM 可能有 OOM 的风险, 那么你可以通过 jinfo 立即设置: HeapDumpOnOutOfMemoryError & HeapDumpPath, 那么当它 OOM 时, 它自动做一个 heap dump.

与 verbose GC log 相关的 JVM flags

默认情况下, JVM 是不会输出 verbose GC log 的, 只有设置相关 flag 之后才会写到文件. 并且根据各种 flag 参数的设置不同, 输出的详细程度也不一样. 下面这些都是与 verbose GC log 相关的参数.

  1. -Xloggc:verboseGC.log
    设置 GC log 文件名, 这个必须设置, 才能看到日志文件.
  2. -XX:+PrintGC 打印 GC 日志
  3. -XX:+PrintGCDetails 输出更详细的 GC 日志, 包括每次年轻代, 总 heap size 大小 GC 前后变化, 以及消耗 CPU 的时间.

与log 文件 rotate 和文件大小, 个数相关的三个参数:

  1. -XX:+UseGCLogFileRotation log 循环写, 而不是一直写到一个文件

  2. -XX:NumberOfGCLogFiles=5 可循环写的文件个个数, 这里设置共5个

  3. -XX:GCLogFileSize=2000k 每个可循环文件的大小, 这里设置是2M. 当前正在写的文件名有一个 current, 用来区别不是正在写的. 比如可能的文件如下:

    verboseGC.log.0
    verboseGC.log.1
    verboseGC.log.2.current
    verboseGC.log.3
    verboseGC.log.4

log 文件中显示与时间相关的参数:
-XX:+PrintGCTimeStamps
-XX:+PrintGCDateStamps

一个典型的 verbose GC log 如下:

Java HotSpot(TM) 64-Bit Server VM (25.141-b15) for bsd-amd64 JRE (1.8.0_141-b15), built on Jul 12 2017 04:35:23 by "java_re" with gcc 4.2.1 (Based on Apple Inc. build 5658) (LLVM build 2336.11.00)
Memory: 4k page, physical 16777216k(317124k free)

/proc/meminfo:

CommandLine flags: -XX:GCLogFileSize=204800 -XX:InitialHeapSize=20971520 -XX:MaxHeapSize=20971520 -XX:NumberOfGCLogFiles=5 -XX:+PrintGC -XX:+PrintGCDateStamps -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseGCLogFileRotation -XX:+UseParallelGC 
2019-07-05T20:39:58.844+0700: 0.374: [GC (Allocation Failure) [PSYoungGen: 5632K->496K(6144K)] 5632K->2232K(19968K), 0.0028539 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
2019-07-05T20:39:58.935+0700: 0.466: [GC (Allocation Failure) [PSYoungGen: 6128K->480K(6144K)] 7864K->3361K(19968K), 0.0017132 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
2019-07-05T20:39:59.028+0700: 0.559: [GC (Allocation Failure) [PSYoungGen: 6112K->480K(6144K)] 8993K->4240K(19968K), 0.0014840 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]  

首先, 在每个文件的头部, 都会有当前 JVM 的一些信息, 以及一些 JVM 启动参数, 之后就是详细的 GC 日志.

如何生成 thread dump

在分析应用响应变慢, CPU 使用率比较高, 死锁等问题时, thread dump 都非常有用. 那么怎么样才能获取一个Java 应用的 thread dump 呢?

  1. 如果有 JDK, 那么 JDK 再带的小工具能非常快的做出 thread dump.
    首先, 使用 JDK bin 目录下的 jps 命令, 能非常快的确认正在运行的 java 进程, 打印进程 id.

    33205 MyApp
    93946 Jps
    

    上面的输出当中, 93946是 jps 命令本身的进程 id, 因为它也是一个 java 进程;
    得到进程 ID 之后, 有下面这2种方法, 可以获取 thread dump:

    1. 使用 jstack 命令
      jstack -l > <file-path>
      jstack -l 33205 > /tmp/threadDump.txt

    2. 使用 jcmd 命令
      jcmd Thread.print > <file-path>
      jcmd 33205 Thread.print /tmp/threadDump.txt

  2. 在某些生产环境, 可能只安装 JRE, 没有安装 JDK, 那么可以使用 kill -3 命令, 给Java 进程发信号, 让它产生 thread dump.

  3. 如果你是在一个桌面系统, 可以使用JDK 自带的 GUI 工具: Java Mission Control 或者 JVisualVM.

  4. 如果想使用编程的方式, 可以使用 ThreadMXBean, 它能 dump 出 Java 进程的所有线程.

    ThreadMXBean threadMxBean = ManagementFactory.getThreadMXBean();
        for (ThreadInfo ti : threadMxBean.dumpAllThreads(true, true)) {
            System.out.print(ti.getThreadId());
            System.out.print(ti.getThreadName());
            System.out.print(ti.getThreadState());
            System.out.print(ti.getStackTrace());
            System.out.print(ti.getLockInfo());
        }
}
  1. 在某些 Windows 桌面环境, 如果你是通过命令行启动, 可以通过 Ctrl + Break 来生成 thread dump

jstat

jstat 用来显示 JVM 的某些指标的统计信息, 可以每隔固定时间采集一次,设置共采集多少次. 主要用来采集 GC 和运行时编译相关的统计信息.

比如下面诊断为什么系统一直 full GC, 最后发现是人为的调用 System.gc().

jstat -gccause 332 3s 5 //332 是进程id, 每 3s 采集一次, 共采集 5 次z

image1.png

下面这个例子采集 GC 的统计信息, S0, S1 分别代表 survivor 0 和 survivor 1, E 代表 Eden, O 代表 Old, P 代表 Permermanent, C 代表 Capacity, U 代表 Used. YGC 和 FGC 分别代表 Young generation 和 Full generation GC, 后面的 T 代表花费的的时间 (Time)

jstat -gc 332 3s 5

image2.png

另外 -options 显示所有可以采集的项目

$ ./jstat -options
-class
-compiler
-gc
-gccapacity
-gccause
-gcnew
-gcnewcapacity
-gcold
-gcoldcapacity
-gcpermcapacity
-gcutil
-printcompilation