如何在java中(使用jna)从cgo返回的C字符串中释放内存?
戈兰如何在java中(使用jna)从cgo返回的C字符串中释放内存?,java,go,jna,cgo,Java,Go,Jna,Cgo,戈兰 输入“C” //导出MyFunction func MyFunction(str*C.char)*C.char{ goStr:=C.GoString(str) msg:=goStr+“世界” 返回C.CString(msg) } func main(){} 爪哇 公共接口MyLib扩展库{ MyLib INSTANCE=Native.load(“MyLib.so”,MyLib.class); 字符串MyFunction(stringstr); } 公共类CGoExample{ 公共静
输入“C”
//导出MyFunction
func MyFunction(str*C.char)*C.char{
goStr:=C.GoString(str)
msg:=goStr+“世界”
返回C.CString(msg)
}
func main(){}
爪哇
公共接口MyLib扩展库{
MyLib INSTANCE=Native.load(“MyLib.so”,MyLib.class);
字符串MyFunction(stringstr);
}
公共类CGoExample{
公共静态void main(字符串[]args){
String str=“Hello”;
字符串msg=MyLib.INSTANCE.MyFunction(str);
System.out.println(msg);
}
}
这段代码有效,Java字符串被传递给Go,Go返回一个新字符串。我遇到的问题是,Go返回的CString不会被Java或Go垃圾收集器释放。CGO文档声明“调用者有责任安排释放它,例如通过调用C.free”,当然在我的情况下,调用C.free将不起作用,因为我正在返回CString,如果我在返回它之前释放CString,我不会从Go函数得到正确的响应。我还尝试调用了defer C.free(unsafe.Pointer(msg))
,但我发现在返回语句之前调用defer语句是很困难的
当我需要返回CString时,我假设它的内存需要从java中释放出来,我该怎么做呢
编辑:
根据DanielWiddis的评论,我尝试了以下内容,但java进程的代码为-1073741819
/#包括
输入“C”
进口“不安全”
//导出MyFunction
func MyFunction(str*C.char)*C.char{
goStr:=C.GoString(str)
msg:=goStr+“世界”
返回C.CString(msg)
}
//免出口
无函数(str*C.char){
C.free(不安全的指针(str))
}
func main(){}
公共接口MyLib扩展库{
MyLib INSTANCE=Native.load(“MyLib.so”,MyLib.class);
字符串MyFunction(stringstr);
无空洞(字符串str);
}
公共类CGoExample{
公共静态void main(字符串[]args){
String str=“Hello”;
字符串msg=MyLib.INSTANCE.MyFunction(str);
System.out.println(msg);
MyLib.INSTANCE.Free(msg);
}
}
C.CString()的返回类型是指针:*C.char
通过将其直接映射到Java端的字符串
,您将失去对该本机指针的跟踪,使得以后无法跟踪和释放它
最简单的解决方案就是将其映射到Java端的指针。在界面中:
指针MyFunction(字符串str);
无空洞(指针str);
然后使用它:
指针pMsg=MyLib.INSTANCE.MyFunction(str);
System.out.println(pMsg.getString(0));
MyLib.INSTANCE.Free(pMsg);
在这种情况下,(8位)字符的字节[]
数组也可能工作。就我个人而言,我可能会将类型安全指针与一些面向对象的帮助器方法一起使用,例如:
CStringPtr类扩展指针类型{
公共CStringPtr(字符串str){
super(MyLib.INSTANCE.MyFunction(str));
}
公共字符串getString(){
返回此.getPointer().getString(0);
}
公众免费{
MyLib.INSTANCE.Free(this.getPointer());
}
}
这将使您的代码:
CStringPtr pMsg=新CStringPtr(str);
System.out.println(pMsg.getString());
pMsg.free();
根据DanielWiddis的评论,我提出了以下解决方案,成功地为所有相关对象释放内存
主程序包
//#包括
输入“C”
进口(
“不安全”
)
//导出MyFunction
func MyFunction(cStr*C.char)*C.char{
str:=C.GoString(cStr)
msg:=str+“世界”
返回C.CString(msg)
}
//免出口
无函数(str*C.char){
C.free(不安全的指针(str))
}
func main(){}
公共接口MyLib扩展库{
MyLib INSTANCE=Native.load(“MyLib.so”,MyLib.class);
指针MyFunction(指针ptr);
无空隙(指针ptr);
}
公共类CGoExample{
公共静态void main(字符串[]args){
//使用字符串值创建指针
String str=“Hello”;
指针ptr=新内存(str.length()+1);
ptr.clear();
ptr.setString(0,str);
//传递返回消息指针的pointer to Go函数
指针resultPtr=MyLib.INSTANCE.MyFunction(ptr);
//从指针获取消息字符串
String msg=resultPtr.getString(0);
//取消分配邮件的内存
MyLib.INSTANCE.Free(resultPtr);
System.out.println(msg);
}
}
在旧版本的Java中,我可能会为此使用finalize()
方法。不过,我认为现在不推荐使用finalize()
,因此您需要做一些额外的工作。或者,如果你还不想做这项工作,至少要为finalize()
被完全删除的那一天做好准备。是的,我们来了:你能不能把C.free
映射为接口中的另一个函数/方法,并从Java调用它?几乎所有其他API都是这样做的。理论上,CString
确实需要libc
提供的malloc()
的内存,后者cgo
安排与生成的可执行映像链接C.free
是与libc
相同的free()
的(几乎)1:1映射。因此,从技术上讲,您可以在该内存上调用free()
,但这必须是相同的free()
——也就是说,它必须是由相同的libc
提供的相同符号。我不知道你是否能用JNA保证这一点。如果不是,我同意Daniel Widdis的建议。另一种选择是注意C.CString
没有什么特别的(调用malloc()