validation,方法执行和JIT编译过程中类加载的原因和跟踪

我试图在非常详细的基础上理解哪些事件导致类加载,并且在我的测试期间遇到了一个我在这个非常基本的示例中无法理解的行为:

public class ClinitTest { public static Integer num; public static Long NUMTEST; static { NUMTEST = new Long(15);; num = (int) (NUMTEST * 5); System.out.println(num); } public static void main(String[] args) { System.out.println( "The number is " + num); } } 

在执行时运行java.lang.Long时。 好吧,它先前由bootstrap类加载器加载,但此时调用了AppClassloader,因为它尚未注册为启动类加载器。 因此,LauncherHelper将获取应用程序类,在它调用main方法之前,JVM将确保该类已初始化。 在执行期间,会发生此类加载。

在另一个运行中,我使用Java代理将重命名为其他内容并添加一个空代理。 我的期望是 – 由于原来的代码没有被执行,我也不会得到类加载事件。

奇怪的是,此时java.lang.Long的负载似乎发生在更早的时间。 在我的跟踪中,我看到当LauncherHelper尝试validation主类时会触发它。 在这里,它尝试通过reflection获取main方法,并且在引擎盖下调用java.lang.Class.getDeclaredMethods0()会导致调用AppClassLoader请求java.lang.Long

所以问题是:

  1. 在正常执行时,如果稍后加载类(即代码实际执行时),但是如果代码实际上永远不会被执行,那么它是如何被加载的,因为重命名的clinit永远不会被调用?

  2. JVM中是否有一种方法可以跟踪哪些事件导致此类类加载? 不仅当它发生时,而且实际上是哪个指令或事件导致它,因为它可能是由首次使用的类,另一个正在validation的类,JIT编译等引起的。

在订阅JVMTI ClassLoad事件的代理的帮助下,我已经validation了在运行ClinitTest并且静态初始化已删除时加载java.lang.Long

由于您使用Java代理运行测试,我想也是如此

  • java.lang.Long在代码转换过程中由代理本身加载;
  • 或者代理在签名中添加/修改Long类的公共方法。

LauncherHelpervalidation主类时,它遍历公共方法,寻找public static void main() 。 作为副作用,解决了这些方法的签名中提到的所有类。

我不知道现有的工具允许跟踪JVM内部事件的类加载,但是这样的工具可以很容易地用几行代码编写。 这里是。

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

编译:

 g++ -shared -fPIC -olibclassload.so classload.c -lunwind -lunwind-x86_64 

跑:

 java -agentpath:/path/to/libclassload.so ClinitTest 

每当类加载事件发生时,它将显示混合堆栈跟踪(C + Java),例如

 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