With the help of an agent that subscribes to JVMTI ClassLoad event I've verified that java.lang.Long
is not loaded when running ClinitTest
with static initialized removed.
Since you are running a test with a Java agent, I suppose that either
java.lang.Long
is loaded by the agent itself during the transformation of your class;
- or the agent adds/modifies a public method with
Long
class in the signature.
When LauncherHelper
validates the main class, it traverses public methods looking for public static void main()
. As a side effect, all classes mentioned in the signatures of these methods are resolved.
I'm not aware of an existing tool that allows to trace class loading with respect to JVM internal events, but such a tool can be easily written in a few lines of code. Here it is.
#include <jvmti.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <libunwind.h>
#include <cxxabi.h>
static char* fix_class_name(char* class_name) {
class_name[strlen(class_name) - 1] = 0;
return class_name + 1;
}
static void print_native_backtrace() {
unw_context_t context;
unw_cursor_t cursor;
unw_getcontext(&context);
unw_init_local(&cursor, &context);
char func[256];
unw_word_t offs;
while (unw_step(&cursor) > 0 && unw_get_proc_name(&cursor, func, sizeof(func), &offs) == 0) {
if (func[0] == '_' && func[1] == 'Z') {
int status;
char* demangled = abi::__cxa_demangle(func, NULL, NULL, &status);
if (demangled != NULL) {
strncpy(func, demangled, sizeof(func));
free(demangled);
}
}
printf(" - %s + 0x%x\n", func, offs);
}
}
static void print_java_backtrace(jvmtiEnv *jvmti) {
jvmtiFrameInfo framebuf[256];
int num_frames;
if (jvmti->GetStackTrace(NULL, 0, 256, framebuf, &num_frames) == 0 && num_frames > 0) {
for (int i = 0; i < num_frames; i++) {
char* method_name = NULL;
char* class_name = NULL;
jclass method_class;
jvmtiError err;
if ((err = jvmti->GetMethodName(framebuf[i].method, &method_name, NULL, NULL)) == 0 &&
(err = jvmti->GetMethodDeclaringClass(framebuf[i].method, &method_class)) == 0 &&
(err = jvmti->GetClassSignature(method_class, &class_name, NULL)) == 0) {
printf(" * %s.%s + %ld\n", fix_class_name(class_name), method_name, framebuf[i].location);
} else {
printf(" [jvmtiError %d]\n", err);
}
jvmti->Deallocate((unsigned char*)class_name);
jvmti->Deallocate((unsigned char*)method_name);
}
}
}
void JNICALL ClassLoad(jvmtiEnv *jvmti, JNIEnv* jni, jthread thread, jclass klass) {
char* class_name;
jvmti->GetClassSignature(klass, &class_name, NULL);
printf("Class loaded: %s\n", fix_class_name(class_name));
jvmti->Deallocate((unsigned char*)class_name);
print_native_backtrace();
print_java_backtrace(jvmti);
}
JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *vm, char *options, void *reserved) {
jvmtiEnv *jvmti;
vm->GetEnv((void**)&jvmti, JVMTI_VERSION_1_0);
jvmtiEventCallbacks callbacks = {0};
callbacks.ClassLoad = ClassLoad;
jvmti->SetEventCallbacks(&callbacks, sizeof(callbacks));
jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_CLASS_LOAD, NULL);
return 0;
}
Compile:
g++ -shared -fPIC -olibclassload.so classload.c -lunwind -lunwind-x86_64
Run:
java -agentpath:/path/to/libclassload.so ClinitTest
It will show a mixed stack trace (C + Java) whenever a class load event happens, e.g.
Class loaded: java/lang/Long
- ClassLoad(_jvmtiEnv*, JNIEnv_*, _jobject*, _jclass*) + 0x69
- JvmtiExport::post_class_load(JavaThread*, Klass*) + 0x15b
- SystemDictionary::resolve_instance_class_or_null(Symbol*, Handle, Handle, Thread*) + 0x87c
- SystemDictionary::resolve_or_fail(Symbol*, Handle, Handle, bool, Thread*) + 0x33
- get_mirror_from_signature(methodHandle, SignatureStream*, Thread*) + 0xc6
- Reflection::get_parameter_types(methodHandle, int, oopDesc**, Thread*) + 0x5df
- Reflection::new_method(methodHandle, bool, bool, Thread*) + 0xfc
- get_class_declared_methods_helper(JNIEnv_*, _jclass*, unsigned char, bool, Klass*, Thread*) + 0x479
- JVM_GetClassDeclaredMethods + 0xcb
* java/lang/Class.getDeclaredMethods0 @ -1
* java/lang/Class.privateGetDeclaredMethods @ 37
* java/lang/Class.privateGetMethodRecursive @ 2
* java/lang/Class.getMethod0 @ 16
* java/lang/Class.getMethod @ 13
* sun/launcher/LauncherHelper.validateMainClass @ 12
* sun/launcher/LauncherHelper.checkAndLoadMain @ 214