如何在调用本地函数时使JNI重新加载共享库?

4

背景

我在JAVA中有一个本地函数

package mypackage;

public class MyWrapper {

    private native int
    wrapFilterRawRequest(String config, String rawRequest);

    public void 
    processRequest( String configPath,String rawRequest){
        int status = 0; 
        status = new MyWrapper().wrapFilterRawRequest(configPath, rawRequest);
        status2 = new MyWrapper().wrapFilterRawRequest(configPath, rawRequest);
    }

    static{
        System.loadLibrary("mylibrary");
    }

}

同时也需要一个在C语言中的JNI封装器

int processRequest(char *conf, char *req)
{
    int status = 0;   
    // Do something here
    return status;
}

JNIEXPORT jint JNICALL Java_mypackage_MyWrapper_wrapFilterRawRequest
(JNIEnv *env, jobject obj, jstring config, jstring rawRequest)
{
    jint status = 0;

    const char *conf, *req;
    char *conf_copy, *req_copy;

    conf = (env)->GetStringUTFChars(config, NULL);
    req  = (env)->GetStringUTFChars(rawRequest, NULL);

    copy_str(&conf_copy, conf);
    copy_str(&req_copy, req);

    (env)->ReleaseStringUTFChars(config, conf);
    (env)->ReleaseStringUTFChars(rawRequest, req);

    status = processRequest(conf_copy , req_copy );   
    return status;
}

问题

在JAVA中调用本地函数两次会导致错误,因为这相当于连续调用C函数processRequest(char*, char*),而我的应用程序不支持这种操作。我希望每次都能像运行可执行文件两次一样独立地调用它。(当同一个函数在同一个应用程序中被调用两次时,程序无法正常工作)。我希望JNI在进行本地调用时重新初始化所有内容。我该如何做到这一点?


1
你不能修复你的应用程序,使其能够被多次调用吗? - Peter Lawrey
在使用gdb进行调试时,问题出现在一个动态链接库的函数调用中,而我不想对其进行任何更改。 - varrunr
2
你是否考虑过每次将应用程序作为单独的进程运行? - Peter Lawrey
我猜你是指JAVA应用程序吧?因为C应用程序作为共享库加载。 - varrunr
1个回答

5

嗯,你可以尝试使用自己的ClassLoader从JVM中卸载dll。加载所需的类并执行需要的操作,将值设置为null,然后调用System.gc()以清除实例及其dll的旧痕迹,最后通过重新初始化该实例来重新加载dll到JVM中:

package test;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

/**
 *
 * @author David
 */
public class Test {

    static {
         System.loadLibrary("mydll.dll");
    }

    @Override
    public void finalize() {
        System.out.println("A garbage collected");
    }

    public void print(String str, int value) {
        System.out.println(str);
        System.out.println(value);
    }

    public static int getNumber() {
        return 42;
    }

    public static void main(String[] args) throws Exception {
        Class<?> clazz = Test.class;//class name goes here
        // static call
        Method getNumber = clazz.getMethod("getNumber");
        int i = (Integer) getNumber.invoke(null /*
                 * static
                 */);
        // instance call
        Constructor<?> ctor = clazz.getConstructor();
        Object instance = ctor.newInstance();
        Method print = clazz.getMethod("print", String.class, Integer.TYPE);
        print.invoke(instance, "Hello, World!", i);
        print = null;
        instance = null;
        ctor = null;
        getNumber = null;
        clazz = null;
        i = 0;
        System.gc();  //reload by empting garbage   Class<?> clazz = Test.class;//class name goes here

        clazz = Test.class;//class name goes here
        // static call
        getNumber = clazz.getMethod("getNumber");
        i = (Integer) getNumber.invoke(null /*
                 * static
                 */);
        // instance call
        ctor = clazz.getConstructor();
        instance = ctor.newInstance();
        print = clazz.getMethod("print", String.class, Integer.TYPE);
        print.invoke(instance, "Hello, World!", i);
        print = null;
        instance = null;
        ctor = null;
        getNumber = null;
        clazz = null;
        i = 0;
        System.gc();  //reload by empting garbage
    }
}

参考资料:


我需要调用一个方法。根据参考文献 ,我需要使用aClass.getMethod("<method>",parameters)。我在JAVA方面的经验有限。你能帮我解决如何传递第二个参数(因为我有两个字符串作为参数)的问题吗?它期望是一个包含Class对象(参数)的数组。 - varrunr
以上代码未卸载共享库。我实现了jint JNI_OnUnload(JavaVM *vm, void *reserved)函数来检查,但它并未被调用。 - varrunr
1
我认为上面的代码片段中可能缺少自定义类加载器? - Mick

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