解决JNI DefineClass中的依赖关系

时间:2017-07-26 03:15:31

标签: java jvm java-native-interface jvmti

我正在使用JVMTI编写应用程序。我正在尝试检测字节码:通过在每个方法条目上注入方法调用。

我知道如何做到这一点,但问题出在仪器类中,比如称为Proxy,我使用JNI函数DefineClass加载。我的Proxy在Java类库中有一些依赖项,目前只有java.lang.ThreadLocal<Boolean>

现在,说我有这个,inInstrumentMethod是普通的boolean

public static void onEntry(int methodID)
{
    if (inInstrumentMethod) {
        return;
    } else {
        inInstrumentMethod = true;
    }

    System.out.println("Method ID: " + methodID);

    inInstrumentMethod = false;
}

代码编译和工作。但是,如果我inInstrumentMethod java.lang.ThreadLocal<Boolean>,我会得到一个NoClassDefFoundError。代码:

private static ThreadLocal<Boolean> inInstrumentMethod = new ThreadLocal<Boolean>() {
        @Override protected Boolean initialValue() {
            return Boolean.FALSE;
        }
    };

public static void onEntry(int methodID)
{
    if (inInstrumentMethod.get()) {
        return;
    } else {
        inInstrumentMethod.set(true);
    }

    System.out.println("Method ID: " + methodID);

    inInstrumentMethod.set(false);
}

我的猜测是依赖关系未正确解析,并且java.lang.ThreadLocal未加载(因此无法找到)。那么问题是,如何强制Java加载java.lang.ThreadLocal?在这种情况下,我不认为我可以使用DefineClass;还有其他选择吗?

1 个答案:

答案 0 :(得分:1)

我认为解析标准类java.lang.ThreadLocal时没有问题,而是由内部类扩展它,由

生成
new ThreadLocal<Boolean>() {
    @Override protected Boolean initialValue() {
        return Boolean.FALSE;
    }
};

由于内部类和外部类之间存在循环依赖关系,因此通过DefineClass解决此问题可能确实是不可能的,因此没有允许定义它们的顺序,除非你有一个完整的ClassLoader按需返回类。

最简单的解决方案是避免生成内部类,这在Java 8中是可能的:

private static ThreadLocal<Boolean> inInstrumentMethod
                                  = ThreadLocal.withInitial(() -> Boolean.FALSE);

如果您使用的是Java 8之前的版本,则不能以这种方式使用它,因此在这种情况下最好的解决方案是重写代码以接受默认值null作为初始值,无需指定不同的初始值:

private static ThreadLocal<Boolean> inInstrumentMethod = new ThreadLocal<>();

public static void onEntry(int methodID)
{
    if (inInstrumentMethod.get()!=null) {
        return;
    } else {
        inInstrumentMethod.set(true);
    }

    System.out.println("Method ID: " + methodID);

    inInstrumentMethod.set(null);
}

您还可以将该匿名内部类转换为顶级类。从那以后,该类与以前的外部类没有依赖关系,在使用它定义类之前首先定义ThreadLocal的子类型,应该可以解决问题。