在 JProfiler 中,为什么我的对象没有显示在“所有对象”视图中?

In JProfiler, why does my object not show in the All Objects view?

我是 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);
    }
}

请注意,我暂停直到按键。这样我就可以确定在我按下一个键之前主范围不会结束,所以我希望 e 存在并且不会被垃圾收集(如果这个假设不正确请纠正我)。示例 class:

package com.example;

public class Example {
    public String getMessage() {
        String testString = "This is the test string. Press a key to exit.";
        return testString;
    }
}

我使用 JProfiler Eclipse 插件启动上述应用程序。我创建了一个基于 Full Instrumentation 配置文件的会话;我已经删除了 Java EE 和 JDBC 特定探测器,并将其余部分保留为默认值。

现在,当探查器启动时,我转到所有对象视图,我希望找到 com.example。* classes,但我找到 none;为什么会这样?

好的,也许我只能在使用另一个视图(如分配调用树)时才能找到这些对象,因此我使用视图中的按钮启用分配记录(默认情况下禁用)。之后它要求我单击计算分配,弹出一个对话框。我接受默认值,然后我看到一个在永恒的空虚中自动更新的空视图。

所以我尝试了 Heap Walker。它要求我先转储。我得到一个对话框,为我提供 "select recorded objects" 的选项,默认情况下未 selected。我将其保留为默认值,并显示一个实例计数视图。但是,在我看到的 类 视图中找不到我的对象。

所以我想我做的事情从根本上是错误的;我应该怎么做才能看到我的对象,特别是我的对象的精确实例数?

更新 1: 我发现了问题的一部分。当分析器 window 出现时,它会向您显示“会话启动”对话框,您可以在其中选择配置文件并设置各种设置。第一个选项卡上有一个名为 "Startup" 的小部分,其中有一个名为 "Initial recording profile" 的设置,默认情况下设置为 [无记录]。当我将它保留为默认值时,我找不到 Example 对象。当我将其设置为 "CPU recording" 时,我可以在“所有对象”视图中找到我的示例对象。

更新 2: 我在 Heap Walker 中找不到该对象。当我 select com.example.Example 在 All Objects 视图中时,我可以右键单击该对象并选择 (show object in Heap Walker)。当我这样做时,Heap Walker 告诉我堆上没有这样的对象!给出了什么?

更新 3: com.example.Example 对象似乎有时会出现,有时不会。我不知道为什么。此外,当它出现时,它将从所有对象视图中消失,即使主循环尚未退出,即使 com.example.Example 对象应该仍然存在...

更新 4: 事实证明,e 垃圾收集器,与 IBM 的 J9 JVM 上的范围结束 无关。请参阅我对此的回答,它修改了 main 以在 按键等待 之后调用第二个方法,这会强制对象保持活动状态。

This way I'm sure the main scope does not end until I press a key, so I >expect e to exist and not be garbage collected (please correct me if this >assumption is incorrect).

没错。 "Example" 对象将在此时由堆栈引用保存,不能被垃圾回收。 "All objects" 视图和堆遍历器都将显示此对象。

我刚刚测试了您的示例,它适用于 JProfiler 8.1.4 和 JProfiler 9.0.2。

使用 "View->Find" 操作搜索 "Example"。

我终于真正解开了这个谜团。原来我是 运行 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;
    }
}

然后,在 main 中,我确保调用 getSecondMessage() AFTER 等待按键 (System.in.read())。这样,我们就可以确定 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);
    }
}

分析上面的代码 不管 CPU 以前认为是影响此的一个因素的记录设置 将按预期工作:对象保持活动状态,因为它不可能是垃圾在按下键之前收集。