Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

jprofiler

I'm new to JProfiler. I've created a very simple test application. Here's a Main.java with a main method:

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

Note that I pause until key-press. 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). The Example class:

package com.example;

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

I start the above application using the JProfiler Eclipse plugin. I've created a session that's based on the Full Instrumentation profile; I've removed the Java EE and JDBC specific probes and left the rest at defaults.

Now when the profiler starts, I go to the all objects view and I'd expect to find com.example.* classes, but I find none; why is that happening?

Ok, so maybe I can only find these objects when I use another view, like the Allocation call tree, so I enable Allocation Recording using the button in the view (it's disabled by default). It asks me to click on calculate allocation after that, which pops up a dialog. I accept the defaults and I'm presented with an empty view that auto-updates in eternal emptiness.

So then I try Heap Walker. It asks me to make a dump first. I get a dialog that provides me the option to "select recorded objects" which is default unselected. I leave it at defaults and am presented with an instance count view. However, my object is not findable in this Classes view I'm presented with.

So I suppose I'm doing something fundamentally wrong; What should I do to see my objects, and specifically the precise instance count of my objects?

UPDATE 1: I've found a part of the problem. When the profiler window comes up, it presents you with the Session Startup dialog, where you can choose the profile and set various settings. There is a little section on the first tab called "Startup" which has a setting called "Initial recording profile", which by default is set to [no recordings]. When I leave this at its default, I cannot find the Example object. When I set it to "CPU recording" I can find my Example object in the All Objects view.

UPDATE 2: I cannot find the object in Heap Walker. When I select com.example.Example in the All Objects view, I can right-click the object and choose (show object in Heap Walker). When I do so, Heap Walker tells me there's no such object on the heap! What gives?

UPDATE 3: The com.example.Example object seems to show up sometimes, sometimes not. I cannot figure out why. Additionally, when it shows up, it will disappear from the All objects view, even though the main loop has not exited yet, even though the com.example.Example object should still be alive...

UPDATE 4: It turns out e is garbage collected, regardless of scope ending on IBM's J9 JVM. See my answer to this which modifies main to invoke a second method after the key-press wait, which forces the object to remain alive.

like image 835
Rubin Simons Avatar asked Jul 29 '15 08:07

Rubin Simons


2 Answers

I finally truly solved this mystery. It turns out I'm running IBM's J9 VM. Apparently, J9 garbage collection is quite a bit more aggressive: It will clean up e within the main scope if e is not going to be used within that scope anymore. I have verified that this specific behaviour does not happen with Oracle's JVM.

So long story short: on IBM J9, you cannot assume that objects stay alive within the scope of a block. On Oracle's JVM, at least by default, e is not garbage collected until after the block ends, regardless of further usage of e.

On IBM J9, when you want to force the object to stay in existence, there has to be a future usage of it. To prove this I modified Example.java to contain the following:

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;
    }
}

Then, in main I made sure to have an invocation of getSecondMessage() AFTER the wait-on-key-press (System.in.read()). This way, we know for sure that the GC cannot cleanup the object before main's scope ends because there's an invocation waiting in the future, happening right after the user presses a key. So Main.java looks like:

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

Profiling the above code regardless of CPU recording settings previously thought to be a factor in this will work as expected: the object stays alive because it cannot be garbage collected before the key is pressed.

like image 63
Rubin Simons Avatar answered Jan 01 '23 22:01

Rubin Simons


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).

That is correct. The "Example" object will be held by a stack reference at that point and cannot be garbage collected. Both the "All objects" view and the heap walker will show this object.

I just tested your example and it works for me with both JProfiler 8.1.4 and JProfiler 9.0.2.

Use the "View->Find" action to search for "Example".

like image 37
Ingo Kegel Avatar answered Jan 01 '23 21:01

Ingo Kegel