6

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.

Arjan Tijms
  • 37,782
  • 12
  • 108
  • 140
Rubin Simons
  • 1,556
  • 1
  • 13
  • 20
  • 1
    Via `Session Startup -> [Edit] Profile Settings -> Profiling Settings -> Customize Profiling Settings -> Miscellaneous -> Keep VM alive` you can make sure JProfiler keeps the VM alive, without having to use a `System.in.read()`. – bcd Jul 29 '15 at 09:05
  • That is true, but that would end the main scope, thus ending the lifetime of the com.example.Example object (at least, it would become a candidate for garbage collection after main exits). System.in.read() within main avoids that. – Rubin Simons Jul 29 '15 at 10:26

2 Answers2

18

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.

Rubin Simons
  • 1,556
  • 1
  • 13
  • 20
1

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

Ingo Kegel
  • 46,523
  • 10
  • 71
  • 102
  • I'm running Jprofiler 9.0.2, with Oracle JDK 8 r33; I do *exactly* as you say, and I do not find the Example object in the All objects view. I have no inclusion filters defined, only the default exclusion filter that came default with the Full Instrumentation profile. – Rubin Simons Jul 29 '15 at 09:56
  • I've found 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. – Rubin Simons Jul 29 '15 at 10:04
  • I've updated the question. object now shows up in All objects view, but right-click and show object in Heap Walker fails. – Rubin Simons Jul 29 '15 at 10:11
  • I did the above once and found the example class. Then I closed Jprofiler, do exactly the same thing, and now for the life of me, I cannot get it to find the object anymore in the All objects view. – Rubin Simons Jul 29 '15 at 10:22
  • Ok, I just restarted a profiling session from scratch. I quickly did a search in the All objects view, found it, and saw it disappear. I did not exit the application, the main loop is still open. This should not be possible right? – Rubin Simons Jul 29 '15 at 10:36