简体   繁体   中英

How do you deallocate memory in java (using jna) from a C String returned from cgo?

Golang


import "C"

//export MyFunction
func MyFunction(str *C.char) *C.char {
    goStr := C.GoString(str)
    msg := goStr + " world"
    return C.CString(msg)
}

func main() {}

Java


public interface MyLib extends Library {

    MyLib INSTANCE = Native.load("mylib.so", MyLib.class);

    String MyFunction(String str);

}

public class CGoExample {

    public static void main(String[] args) {
        String str = "Hello";
        String msg = MyLib.INSTANCE.MyFunction(str);
        System.out.println(msg);
    }

}

This code works, the Java String is passed to Go, and Go returns a new String. The issue I'm having is that the CString returned by Go will not be deallocated by either the Java or Go garbage collector. The CGO documentation states "it is the caller's responsibility to arrange for it to be freed, such as by calling C.free", of course in my case calling C.free will not work as I'm returning the CString and if I free the CString before returning it I don't get a proper response from the Go function. I also tried calling defer C.free(unsafe.Pointer(msg)) but as I found out the hard way defer statements will be called before the return statement.

As I need to return the CString I'm assuming its memory needs to be deallocated from java, how would I go about doing this?

Edit:

Based on DanielWiddis' comment I have tried the following but the java process exists with code -1073741819.

// #include <stdlib.h>
import "C"

import "unsafe"

//export MyFunction
func MyFunction(str *C.char) *C.char {
    goStr := C.GoString(str)
    msg := goStr + " world"
    return C.CString(msg)
}

//export Free
func Free(str *C.char){
    C.free(unsafe.Pointer(str))
}

func main() {}
public interface MyLib extends Library {

    MyLib INSTANCE = Native.load("mylib.so", MyLib.class);

    String MyFunction(String str);

    void Free(String str);

}

public class CGoExample {

    public static void main(String[] args) {
        String str = "Hello";
        String msg = MyLib.INSTANCE.MyFunction(str);
        System.out.println(msg);
        MyLib.INSTANCE.Free(msg);
    }

}

The return type of C.CString() is a pointer: *C.char .

By mapping this directly to a String on the Java side, you are losing track of that native Pointer, making it impossible to track and free it later.

The most straightforward solution is just to map that to a Pointer on the Java side. In the interface:

Pointer MyFunction(String str);

void Free(Pointer str);

Then to use it:

Pointer pMsg = MyLib.INSTANCE.MyFunction(str);
System.out.println(pMsg.getString(0));
MyLib.INSTANCE.Free(pMsg);

It's possible a byte[] array of (8 bit) characters would also work in this situation. Personally, I'd probably use a type safe pointer with some object oriented helper methods such as:

class CStringPtr extends PointerType {
    public CStringPtr(String str) {
        super(MyLib.INSTANCE.MyFunction(str));
    }

    public String getString() {
        return this.getPointer().getString(0);
    }

    public void free() {
        MyLib.INSTANCE.Free(this.getPointer());
    }
}

which would make your code:

CStringPtr pMsg = new CStringPtr(str);
System.out.println(pMsg.getString());
pMsg.free();

Based on DanielWiddis' comments I came up with the following solution which successfully deallocates memory for all involved objects.

package main

// #include <stdlib.h>
import "C"

import (
    "unsafe"
)

//export MyFunction
func MyFunction(cStr *C.char) *C.char {
    str := C.GoString(cStr)
    msg := str + " world"
    return C.CString(msg)
}

//export Free
func Free(str *C.char){
    C.free(unsafe.Pointer(str))
}

func main() {}
public interface MyLib extends Library {

    MyLib INSTANCE = Native.load("mylib.so", MyLib.class);

    Pointer MyFunction(Pointer ptr);

    void Free(Pointer ptr);

}
public class CGoExample {

    public static void main(String[] args) {

        // Create pointer with a String value
        String str = "Hello";
        Pointer ptr = new Memory(str.length() + 1);
        ptr.clear();
        ptr.setString(0, str);

        //Pass the pointer to Go function which returns the pointer of the message
        Pointer resultPtr = MyLib.INSTANCE.MyFunction(ptr);

        //Get the message String from the pointer
        String msg = resultPtr.getString(0);

        //Deallocate the memory for the message
        MyLib.INSTANCE.Free(resultPtr);

        System.out.println(msg);
    }

}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM