我是JProfiler的新手.我创建了一个非常简单的测试应用程序.这是一个带有main方法的Main.java:
package com.example;
import java.io.IOException;
public class Main {
public static void main(String[] args) throws IOException {
Example e = new Example(); //Gets gc'ed?
System.out.println(e.getMessage());
System.in.read();
System.exit(0);
}
}
Run Code Online (Sandbox Code Playgroud)
请注意,我暂停直到按键.这样我确定主要范围不会结束,直到我按下一个键,所以我希望e存在而不是垃圾收集(如果这个假设不正确,请纠正我).Example类:
package com.example;
public class Example {
public String getMessage() {
String testString = "This is the test string. Press a key to exit.";
return testString;
}
}
Run Code Online (Sandbox Code Playgroud)
我使用JProfiler Eclipse插件启动上述应用程序.我创建了一个基于Full Instrumentation配置文件的会话; 我删除了Java EE和JDBC特定的探测器,剩下的就是默认值.
现在,当探查器启动时,我转到所有对象视图,我希望找到com.example.*类,但我找不到; 为什么会这样?
好吧,也许我只能在使用其他视图时找到这些对象,比如Allocation调用树,所以我使用视图中的按钮启用Allocation Recording(默认情况下禁用).它要求我点击之后计算分配,弹出一个对话框.我接受了默认值,并且我看到了一个空的视图,可以自动更新永恒的空虚.
那么我试试Heap Walker.它要求我先进行转储.我得到一个对话框,为我提供"选择记录的对象"的选项,默认情况下未选中.我将它保留为默认值,并显示实例计数视图.但是,在我提供的这个Classes视图中找不到我的对象.
所以我想我做的事情根本就是错误的; 我该怎么做才能看到我的对象,特别是我的对象的精确实例数?
更新1: 我发现了问题的一部分.当探查器窗口出现时,它会显示"会话启动"对话框,您可以在其中选择配置文件并设置各种设置.在名为"Startup"的第一个选项卡上有一个小部分,其中有一个名为"Initial recording profile"的设置,默认设置为[no recordings].当我将其保留为默认值时,我找不到Example对象.当我将其设置为"CPU录制"时,我可以在"所有对象"视图中找到我的示例对象.
更新2: 我在Heap Walker中找不到对象.当我在All Objects视图中选择com.example.Example时,我可以右键单击该对象并选择(在Heap Walker中显示对象).当我这样做时,Heap Walker告诉我堆上没有这样的对象!是什么赋予了?
更新3: com.example.Example对象似乎有时出现,有时不出现.我无法弄清楚为什么.此外,当它出现时,它将从All对象视图中消失,即使主循环尚未退出,即使com.example.Example对象仍应存活...
更新4: 事实证明e 是垃圾收集,无论IBM J9 JVM的范围如何.请参阅我对此的回答,修改main以在按键等待后调用第二个方法,这会强制对象保持活动状态.
我终于真正解开了这个谜团.事实证明我正在运行IBM的J9 VM.显然,J9垃圾收集更具攻击性:如果e不再在该范围内使用,它将清除主范围内的 e.我已经验证了Oracle的JVM 不会发生这种特定行为.
长话短说:在IBM J9上,你不能假设对象在一个块的范围内保持活跃.在Oracle的JVM上,至少在默认情况下,e 块在块结束之前不会被垃圾收集,无论e的进一步使用如何.
在IBM J9上,当您想强制对象保持存在时,必须将来使用它.为了证明这一点,我修改了Example.java以包含以下内容:
package com.example;
public class Example {
public String getFirstMessage() {
String firstTestString = "This is the first message: Hello!";
return firstTestString;
}
public String getSecondMessage() {
String secondTestString = "This is the second message: Goodbye!";
return secondTestString;
}
}
Run Code Online (Sandbox Code Playgroud)
然后,在main中我确保在wait-on-key-press(System.in.read())之后调用getSecondMessage().这样,我们确定GC在main的作用域结束之前无法清除对象,因为将来会有一个调用等待,就在用户按下一个键之后发生.所以Main.java看起来像:
package com.example;
import java.io.IOException;
public class Main {
public static void main(String[] args) throws IOException {
Example e = new Example();
System.out.println(e.getFirstMessage());
System.in.read();
System.out.println(e.getSecondMessage());
System.exit(0);
}
}
Run Code Online (Sandbox Code Playgroud)
分析上述代码,无论之前认为是其中的因素的CPU记录设置都将按预期工作:对象保持活动状态,因为在按下该键之前无法对其进行垃圾回收.