Skip to content

The free_callback function has a JNI weak global reference leak problem #1586

@xiezhaokun

Description

@xiezhaokun

Version of JNA and related jars

all version

Version and vendor of the java virtual machine

all

Operating system

all

System architecture (CPU type, bitness of the JVM)

Complete description of the problem

The number of arg_classes created by create_callback and the number of arg_classes released by create_callback are inconsistent.

for (i=0;i < argc;i++) {
    int jtype;
    jclass cls = (*env)->GetObjectArrayElement(env, arg_classes, i);
    if ((cb->conversion_flags[i] = get_conversion_flag(env, cls)) != CVT_DEFAULT) {
      cb->arg_classes[i] = (*env)->NewWeakGlobalRef(env, cls);
      cvt = 1;
    }
    else {
      cb->arg_classes[i] = NULL;
    }
...
}

When direct is false , free the cb->arg_classes , but not free JNI weak reference.
https://github.com/java-native-access/jna/blob/master/native/callback.c#L219C3-L224

  if (!direct || !cvt) {
    free(cb->conversion_flags);
    cb->conversion_flags = NULL;
    free(cb->arg_classes);
    cb->arg_classes = NULL;
  }
void 
free_callback(JNIEnv* env, callback *cb) {
  (*env)->DeleteWeakGlobalRef(env, cb->object);
  ffi_closure_free(cb->closure);
  free(cb->arg_types);
  if (cb->arg_classes) {
    unsigned i;
    for (i=0;i < cb->cif.nargs;i++) {
      if (cb->arg_classes[i]) {
        (*env)->DeleteWeakGlobalRef(env, cb->arg_classes[i]);
      }
    }
    free(cb->arg_classes);
  }
...
}

Steps to reproduce

native code
#include <stdio.h>

typedef void(*callback_function)(int, void *);

void set_callback(callback_function callback) {
    // Call Java callback function with parameter 42
    callback(42, NULL);
}
gcc -shared -fPIC -o libjnatest.so jna_test.c
java code
  • MyCallback
package com.sun.jna;

public interface MyCallback extends Callback {
    void callback(int id, Pointer pointer);
}
  • MyCallbackImpl
package com.sun.jna;

public class MyCallbackImpl implements MyCallback {
    @Override
    public void callback(int id, Pointer pointer) {
//        System.out.println(pointer);
    }
}
  • Example
package com.sun.jna;

public class Example {

   // static MyCallback myCallback = new MyCallbackImpl();
    public interface MyLibrary extends Library {
        MyLibrary INSTANCE = Native.loadLibrary(System.getProperty("user.dir") + "/libjnatest.so", MyLibrary.class);

        void set_callback(Callback myCallback);
    }

    public static void main(String[] args) throws InterruptedException {
        System.out.println();
        System.out.println("--------------------start--------------------");
        test(1000000);
        System.out.println("--------------------end--------------------");
        Thread.sleep(1000000L);
    }

    static void test(int loop) {

        for (int i = 0; i < loop; i++) {
            MyCallback myCallback = new MyCallbackImpl();
            MyLibrary.INSTANCE.set_callback(myCallback);
            CallbackReference callbackReference = CallbackReference.callbackMap.get(myCallback);
            // free reference
            callbackReference.dispose();
        }
    }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions