I write a blog post on this and related in-depth profiling topics every two weeks (you can follow me on social media https://twitter.com/parttimen3rd and https://mastodon.social/@parttimenerd to get notified).
I write a blog post on this and related in-depth profiling topics every two weeks (you can follow me on social media https://twitter.com/parttimen3rd and https://mastodon.social/@parttimenerd to get notified).
I believe the code of the constructor of Profiler is not thread safe, it has a publication problem. The method onEnd can be called by the profiler thread while the fields options and store are not fully initialized.
From another thread POV, a final field is only fully initialized after the call to the constructor not inside the call to the constructor.
I believe the issue is between the profiler thread and the shutdown hook thread. Both will run concurrently because the profiler thread is marked deamon. So the shutdown hook thread can see the Profiler fields not fully initialized. The call to addShutdownHook() should be done outside of the Profiler constructor.
public static Profiler newInstance(Options options) {
Profiler profiler = new Profiler(options);
Runtime.getRuntime().addShutdownHook(new Thread(profiler::onEnd));
return profiler;
}
private Profiler(Options options) {
this.options = options;
this.store = new Store(options.getFlamePath());
}
In principle you can also use chained constructors: public Profiler(Options options) {
this(options, null);
Runtime.getRuntime().addShutdownHook(new Thread(this::onEnd)); // okay to leak this here
}
private Profiler(Options options, Void dummy) {
this.options = options;
this.store = new Store(options.getFlamePath());
}
(cf. https://stackoverflow.com/a/35169705/623763)Actually, is there anything stopping the sampler from taking another sample while onExit is running?
Re the multi-threading issue, it should be possible to stop the profiler thread in the hook, if we keep a reference to the thread, no?
https://github.com/openjdk/jdk/blob/2fa09333ef0ac2dc1e44292f8d45d4571cb22cca/src/java.base/share/classes/java/lang/Runtime.java#L69
https://github.com/openjdk/jdk/blob/2fa09333ef0ac2dc1e44292f8d45d4571cb22cca/src/java.base/share/classes/java/lang/ApplicationShutdownHooks.java#L65
maybe I'm missing something that is java specific with the way constructors work. I'm guessing even without the synchronisation point java lets you share objects in a way that would normally be considered unsafe as long as all the fields are final.