I'm using the ExecutionSample event from HotSpot in OpenJDK11.
It has a field for thread state, but I only ever see one value for the field: STATE_RUNNABLE
How does HotSpot choose which threads to sample? Why are they always runnable?
Simple reproduction code in Kotlin:
import jdk.jfr.Recording
import jdk.jfr.consumer.RecordingFile
import java.nio.file.Path
object ExecutionSampleTest {
private const val EXECUTION_SAMPLE = "jdk.ExecutionSample"
private val RECORDING_PATH = Path.of("/tmp/recording.jfr")
@JvmStatic
fun main(args: Array<String>) {
Recording().use { recording ->
recording.enable(EXECUTION_SAMPLE)
recording.settings = recording.settings.plus("$EXECUTION_SAMPLE#period" to "1 ms")
recording.start()
repeat(100) {
// start some sleeping threads, just so we've got something to sample
Thread { Thread.sleep(20_000) }.start()
}
Thread.sleep(20_000)
recording.stop()
recording.dump(RECORDING_PATH)
RecordingFile.readAllEvents(RECORDING_PATH).forEach {
println("Thread state: ${it.getString("state")}")
}
}
}
}
Only ever prints: Thread state: STATE_RUNNABLE
Why does the HotSpot ExecutionSample event always return STATE_RUNNABLE?
By design.
JFR method profiler periodically samples Java threads and produces two types of events:
ExecutionSample
, when a thread state is in_java
NativeMethodSample
, when a thread state is in_native
See jfrThreadSampler.cpp source code:
if (JAVA_SAMPLE == type) {
if (thread_state_in_java(thread)) {
ret = sample_thread_in_java(thread, frames, max_frames);
}
} else {
assert(NATIVE_SAMPLE == type, "invariant");
if (thread_state_in_native(thread)) {
ret = sample_thread_in_native(thread, frames, max_frames);
}
}
Both in_java
and in_native
correspond to STATE_RUNNABLE
in JFR format.
When a thread is not runnable for either reason (STATE_SLEEPING
, STATE_PARKED
etc.), its JVM state is thread_blocked
. However, JFR method profiler does not sample blocked threads at all.
If you are interested in wall clock profiling (i.e. sampling all threads, whether they run on CPU or sleep), you may want to look at async-profiler. Async-profiler can also produce output in JFR compatible format, but unlike JFR, it generates ExecutionSample
events both for RUNNABLE and IDLE threads.