如何知道一个类是否已经被初始化?

5

使用自定义类加载器或Java代理+Instrumentation API,可以很容易地获取JVM加载的所有类。但是,已初始化的类列表似乎不那么容易获得。(实际上我想知道是否有任何方法可以获得它)

那么,有没有办法知道一个类是否已经被初始化?

-- 提前致谢。


你为什么想要这样做? - Eric
我有一款软件,已经对类进行了仪器化,并向每个类添加了一个特定的静态字段(我希望在执行程序后收集它)。由于我确切地知道哪些类已加载,因此可以通过反射轻松检索该字段。但是,如果加载了类X但未初始化,则反射将触发类的静态构造函数,该函数将初始化所有静态变量等。因此,如果有一种方法可以知道某个特定的类X尚未初始化,我甚至不会尝试从该类获取我的静态字段。 - josecampos
如果您已经有了仪器类,您可以添加静态初始化程序到类中,使用代码来收集它们,例如 MyClassesCollector.list.add(MethodHandles.lookup().lookupClass());此外,服务性代理也可以这样做,但会冻结目标进程。 - commit-man
感谢您的评论@commit-man。是的,我可以为被检测的类添加静态初始化程序。但是,被检测的类将直接依赖于我的运行时/agent.jar。当使用自定义类加载器时,不能百分之百保证该依赖项可从被检测的类中访问。 - josecampos
代理将被添加到系统类加载器中,您还可以通过仪表API或代理的清单修改引导类路径,这不是问题。 - commit-man
1个回答

4

我不确定仪器API,但一种可能的方法是使用JVMTI的GetClassStatus函数。

通过工具接口,您可以获取JVM加载的所有类,并找出那些没有JVMTI_CLASS_STATUS_INITIALIZED状态标志的类。

JavaVM *jvm;
jvmtiEnv *jvmti;
jvmtiError err;

env->GetJavaVM(&jvm);
jvm->GetEnv((void **) &jvmti, JVMTI_VERSION_1_2);

jint classCount = 0;
jclass * classes;

jvmti->GetLoadedClasses(&classCount, &classes);
for (int i = 0; i < classCount; i++) {
    jint classStatus = 0;
    jvmti->GetClassStatus(classes[i], &classStatus);

    if (classStatus != JVMTI_CLASS_STATUS_PRIMITIVE
        && classStatus != JVMTI_CLASS_STATUS_ARRAY
        && classStatus != JVMTI_CLASS_STATUS_ERROR
        && !(classStatus & JVMTI_CLASS_STATUS_INITIALIZED)) {
        // static initializer is not finished yet
    }
}

非常感谢您的回答。虽然它似乎正好符合我的需求,但我更倾向于使用基于Java的解决方案。因为支持不同的操作系统、不同的架构(32位、64位)等将会很痛苦。 - josecampos
@josecampos 我觉得这个功能非常底层。因此,基于Java的解决方案也将使用本地库来检查类状态。无论如何,如果你找到任何解决方案,我也很乐意看到它 :) - vsminkov
请查看 https://github.com/odnoklassniki/jvmti-tools#vmtrace已经有现成的代理源代码来跟踪类的加载/准备阶段。 - jreznot

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接