JConsole 里的 GC 按钮。原创
是这样的,前几天有个小伙伴给我发了段代码:
public class Main {
static class OOMObject{
public byte[] placeholder=new byte[64*1024];
}
public static void fillHeap(int num) throws InterruptedException {
List<OOMObject> list = new ArrayList<>();
for (int i = 0; i < num; i++) {
//稍作延迟,令监视曲线的变化更加明显
Thread.sleep(50);
list.add(new OOMObject());
}
System.gc();
}
public static void main(String[] args) throws InterruptedException {
fillHeap(1000);
}
}
我看了一眼:问发生肾么事儿了?
他给我说:
歪歪,这个代码是我看书上演示 JConsole 检测内存的测试案例。但是我观察的时候看到了这里有个[执行GC]的按钮,手贱点了一下。我就想问一下这里的[执行GC]和代码里面的 System.gc() 是一回事吗?
我一看:嚯,好家伙,这问题有点意思啊。这我还真不知道呢。
于是我给他说:
这不,很快啊,我就研究完了。
先说结论:
JConsole 里面点击按钮执行 GC 和在程序里面调用 System.gc() 是一回事。
研究过程
从哪里开始研究呢?
来,一起说一遍:源码之下无秘密。
你知道的,JConsole 这画风一看就是 Java 用 swing 编程写出来的玩意。
好巧不巧,我大学的时候学 swing 学的风生水起,因为我太喜欢那种运行起来就能直接看到画面的感觉了。
对于初学者是一种鼓励的感觉。
那么就很简单了,我只要找到 JConsole 对应的源码,看看点击 GC 按钮的时候它执行的源码是什么不就完事了吗?
那么问题来了,JConsole 的源码去哪里找呢?
OpenJDK 里面就有:
http://hg.openjdk.java.net/jdk8/jdk8/jdk/file/758db1c4c65c/
把下载下来的源码导入到 Idea 里面,然后全局查找 JConsole 类:
可以看到在源码中的路径为:
sun.tools.jconsole.JConsole
这里还有我们熟悉的 javac、jcmd、jinfo、jmap、jps、jstack、jstat 这些小工具。
找到 JConsole 的源码之后怎么办呢?
读呗。
但是我大概读了 5 分钟的样子,感觉不对劲。
swing 编程我基本上已经全部还给老师了,看着有点懵逼的样子,只能连蒙带猜的看个大概,效率太低了。
但是我看的时候看到这行代码的时候,我突然冒出了另外一个思路:
你说这行是在干啥?
用脚指头猜也知道是设置 title。
title 是什么?
诶,不就是 JConsole 页面的这个玩意吗?
这一点我还是没还给老师的。
在源码里面 title 值是从这里取出来的:
title = Messages.JAVA_MONITORING___MANAGEMENT_CONSOLE;
这是个啥东西?
我也不知道,反正我二话不说上去就是一个全局搜索,路子就是这么野:
这一看,嚯,这不是多个配置文件嘛,整的还是国际化。
那一串看着像是乱码的玩意,很明显,是 Unicode 嘛,来转义一波:
你说这事整的,还真被我猜对了。
验证了这个点,接下来的思路就很简单了。
这不是还有[执行]两个字的中文嘛?
执行对应的 Unicode 是 \u6267\u884c 。
你懂我意思吧?
上来就是一个反向操作,回手掏:
PERFORM_GC=\u6267\u884c &GC
仅有的那点 swing 编程知识告诉我,接下来只要找 PERFORM_GC 对应的文案是在哪里用的,不就能拿到这个 button 了吗?
屁话少说,直接上来又是一个全局搜索:
看到这行代码的时候我觉得我真特么的是一个及里日。
及里儿斯就是 genius,天才的意思。
来,看一眼这行代码:
这个按钮的变量的名称就是 gcButton。
而当我看到这个 gcButton 位于的 Java 类的时候,我才发现我大意了:
sun.tools.jconsole.MemoryTab
MemoryTab,内存选项卡,多贴切啊,简直就是见名知意了。
而这两个类隔的并不远,我要是把类名看一眼,也不至于这样反向去查:
这波看起来是走远了一点,但是不亏,反正就是找到了 gcButton。
最终看一眼 gcButton 被调用的地方:
真相就是一步之遥了。
这代码对应的 gc() 方法是啥呢?
proxyClient.getMemoryMXBean().gc();
首先它要获取一个 MemoryMXBean 出来,然后调用它的 gc 方法:
而 MemoryMXBean 是一个接口,它对应的实现类是:
sun.management.MemoryImpl
到这里了,就真相大白了。
这里的 gc() 方法和 System.gc() 一模一样:
所以,再次说一下结论:
JConsole 里面点击按钮执行 GC 和在程序里面调用 System.gc() 是一回事。
我承认,整个查找的过程中除了我的“及里儿斯”之外,还有一点运气的成分在里面。
诶,但是最终我就是找到了,你说这事搞的简直没地儿说理去。