1. Overview
1.概述
In addition to typical development utilities such as compiler and runtime, each JDK release is shipped with a myriad of other tools. Some of these tools can help us to gain valuable insights into our running applications.
除了典型的开发工具,如编译器和运行时,每个JDK版本都有无数的其他工具。这些工具中的一些可以帮助我们对正在运行的应用程序获得有价值的洞察力。
In this article, we’re going to see how we can use such tools to find out more about the GC algorithm used by a particular JVM instance.
在本文中,我们将看到如何使用此类工具来了解特定JVM实例所使用的GC算法的情况。
2. Sample Application
2.申请书样本
Throughout this article, we’re going to use a very simple application:
在这篇文章中,我们将使用一个非常简单的应用程序。
public class App {
public static void main(String[] args) throws IOException {
System.out.println("Waiting for stdin");
int read = System.in.read();
System.out.println("I'm done: " + read);
}
}
Obviously, this app waits and keeps running until it receives something from the standard input. This suspension helps us to mimic the behavior of long-running JVM applications.
显然,这个应用程序会等待并继续运行,直到它从标准输入中收到一些东西。这种暂停有助于我们模仿长期运行的JVM应用程序的行为。
In order to use this app, we have to compile the App.java file with javac and then run it using the java tool.
为了使用这个应用程序,我们必须用javac编译App.java文件,然后用java工具运行它。
3. Finding the JVM Process
3.寻找JVM进程
To find the GC used by a JVM process, first, we should identify the process id of that particular JVM instance. Let’s say that we ran our app with the following command:
要找到一个JVM进程所使用的GC,首先,我们应该确定该特定JVM实例的进程ID。假设我们用以下命令运行我们的应用程序。
>> java App
Waiting for stdin
If we have JDK installed, the best way to find the process id of JVM instances is to use the jps tool. For instance:
如果我们安装了JDK,查找JVM实例的进程ID的最好方法是使用jps工具。比如说。
>> jps -l
69569
48347 App
48351 jdk.jcmd/sun.tools.jps.Jps
As shown above, there are three JVM instances running on the system. Obviously, the description of the second JVM instance (“App”) matches our application name. Therefore, the process id we’re looking for is 48347.
如上图所示,系统上有三个JVM实例在运行。显然,第二个JVM实例(”App”)的描述与我们的应用程序名称相符。因此,我们要找的进程ID是48347。
In addition to jps, we can always use other general utilities to filter out running processes. For instance, the famous ps tool from the procps package will work as well:
除了jps之外,我们还可以使用其他一般的实用程序来过滤出正在运行的进程。例如,著名的ps工具,来自procps包的工具也可以工作。
>> ps -ef | grep java
502 48347 36213 0 1:28AM ttys037 0:00.28 java App
However, the jps is way simpler to use and requires less filtering.
然而,jps的使用方式更简单,需要的过滤也更少。
4. Used GC
4.二手GC
Now that we know how to find the process id, let’s find the GC algorithm used by JVM applications that are already running.
现在我们知道了如何找到进程ID,让我们找到已经运行的JVM应用程序所使用的GC算法。
4.1. Java 8 and Earlier
4.1.Java 8和更早的版本
If we’re on Java 8, we can use the jmap utility to print the heap summary, heap histogram, or even generate a heap dump. In order to find the GC algorithm, we can use the -heap option as:
如果我们在Java 8上,我们可以使用jmap工具来打印堆摘要、堆直方图,甚至是生成堆转储。为了找到GC算法,我们可以使用-heap选项作为。
>> jmap -heap <pid>
So in our particular case, we’re using the CMS GC:
因此,在我们的特殊情况下,我们正在使用CMS GC。
>> jmap -heap 48347 | grep GC
Concurrent Mark-Sweep GC
For other GC algorithms, the output is almost the same:
对于其他GC算法,输出结果几乎相同。
>> jmap -heap 48347 | grep GC
Parallel GC with 8 thread(s)
4.2. Java 9+: jhsdb jmap
4.2 Java 9+: jhsdb jmap
As of Java 9, we can use the jhsdb jmap combination to print some information about the JVM heap. More specifically, this particular command would be equivalent to the previous one:
从Java 9开始,我们可以使用jhsdb jmap组合来打印有关JVM堆的一些信息。更具体地说,这个特定的命令将等同于前面的命令。
>> jhsdb jmap --heap --pid <pid>
For instance, our app is running with G1GC now:
例如,我们的应用程序现在正在使用G1GC运行。
>> jhsdb jmap --heap --pid 48347 | grep GC
Garbage-First (G1) GC with 8 thread(s)
4.3. Java 9+: jcmd
4.3. Java 9+: jcmd
In modern JVMs, the jcmd command is pretty versatile. For instance, we can use it to get some general info about the heap:
在现代JVM中,jcmd命令的用途相当广泛。例如,我们可以用它来获取一些关于堆的一般信息。
>> jcmd <pid> VM.info
So if we pass our app’s process id, we can see that this JVM instance is using Serial GC:
因此,如果我们传递我们的应用程序的进程ID,我们可以看到这个JVM实例正在使用串行GC。
>> jcmd 48347 VM.info | grep gc
# Java VM: OpenJDK 64-Bit Server VM (15+36-1562, mixed mode, sharing, tiered, compressed oops, serial gc, bsd-amd64)
// omitted
The output is similar for G1 or ZGC:
G1或ZGC的输出是类似的。
// ZGC
# Java VM: OpenJDK 64-Bit Server VM (15+36-1562, mixed mode, sharing, tiered, z gc, bsd-amd64)
// G1GC
# Java VM: OpenJDK 64-Bit Server VM (15+36-1562, mixed mode, sharing, tiered, compressed oops, g1 gc, bsd-amd64)
With a little bit of grep magic, we can also remove all those noises and just get the GC name:
用一点点grep的魔法,我们也可以去除所有这些噪音,只得到GC的名字。
>> jcmd 48347 VM.info | grep -ohE "[^\s^,]+\sgc"
g1 gc
4.4. Command Line Arguments
4.4.命令行参数
Sometimes, we (or someone else) explicitly specify the GC algorithm while launching the JVM application. For instance, we’re opting to use ZGC here:
有时,我们(或其他人)会在启动JVM应用程序时明确指定GC算法。例如,我们在这里选择使用ZGC。
>> java -XX:+UseZGC App
In such cases, there are much simpler ways to find the used GC. Basically, all we have to do is to somehow find the command that the application has been executed with.
在这种情况下,有更简单的方法来找到使用的GC。基本上,我们所要做的就是以某种方式找到应用程序被执行的命令。
For example, on UNIX-based platforms, we can use the ps command again:
例如,在基于UNIX的平台上,我们可以再次使用ps命令。
>> ps -p 48347 -o command=
java -XX:+UseZGC App
From the above output, it’s obvious that the JVM is using ZGC. Similarly, the jcmd command also can print the command line arguments:
从上面的输出中可以看出,JVM使用的是ZGC。同样地,jcmd命令也可以打印命令行参数。
>> jcmd 48347 VM.flags
84020:
-XX:CICompilerCount=4 -XX:-UseCompressedOops -XX:-UseNUMA -XX:-UseNUMAInterleaving -XX:+UseZGC // omitted
Surprisingly, as shown above, this command will print both implicit and explicit arguments and tunables. So even if we don’t specify the GC algorithm explicitly, it’ll show the selected and default one:
令人惊讶的是,如上图所示,这个命令会同时打印隐式和显式参数和可调整项。因此,即使我们没有明确指定GC算法,它也会显示选定的和默认的算法。
>> jcmd 48347 VM.flags | grep -ohE '\S*GC\s'
-XX:+UseG1GC
And even more surprising, this will work on Java 8 as well:
而更令人惊讶的是,这也将在Java 8上工作。
>> jcmd 48347 VM.flags | grep -ohE '\S*GC\s'
-XX:+UseParallelGC
5. Conclusion
5.总结
In this article, we saw different approaches to find the GC algorithm used by a particular JVM instance. Some of the mentioned approaches were tied to specific Java versions, and some were portable.
在这篇文章中,我们看到了不同的方法来寻找一个特定的JVM实例所使用的GC算法。所提到的一些方法是与特定的Java版本相联系的,而有些是可移植的。
Moreover, we saw a couple of ways to find the process id, which is always needed.
此外,我们还看到了一些寻找进程ID的方法,这也是一直需要的。