[英]How to manage memory deallocation properly between C++ and Java with JNI?
I am working on a Java library that is a thin wrapper for the Windows Waveform Functions to play 24 bit audio through Java.我正在开发一个Java 库,它是Windows 波形函数的一个瘦包装器,可以通过 Java 播放 24 位音频。 (The JVM only supports 8bit and 16bit audio).
(JVM 仅支持 8 位和 16 位音频)。
The paradigm in the Windows Waveform Functions is: Windows 波形函数中的范例是:
waveOutUnprepareHeader
waveOutUnprepareHeader
Given that my Java library is going to be a thin wrapper for the native Waveform Functions, I have a class for the Header Pointer, so I can keep it in scope for as long as needed, pass it around as needed, and eventually call waveOutUnprepareHeader
on it.鉴于我的 Java 库将成为本机波形函数的瘦包装器,我有一个用于 Header Pointer 的类,因此我可以根据需要将其保留在范围内,根据需要传递它,并最终调用
waveOutUnprepareHeader
在上面。
public class WaveHeader {
long waveHeaderPointer;
public WaveHeader(byte[] buffer) {
waveHeaderPointer = HWaveOut.createHeader(buffer, buffer.length);
}
}
The native code being called above on line 5 ( HWaveOut.createHeader()
) is:上面第 5 行调用的本机代码(
HWaveOut.createHeader()
)是:
JNIEXPORT jlong JNICALL Java_net_joshuad_waveformjni_HWaveOut_createHeader
(JNIEnv * env, jclass jclass, jbyteArray jbuffer, jint jBufferSize) {
char* buffer = new char[jBufferSize];
asCharArray(env, jbuffer, buffer);
WAVEHDR* headerOut = new WAVEHDR{ buffer, (DWORD)jBufferSize, 0, 0, 0, 0, 0, 0 };
std::cout << "[C++] Header out location: " << headerOut << std::endl;
return (jlong)headerOut;
}
As you can see, I allocate a WAVEHDR
on the heap in C++.如您所见,我在 C++ 中在堆上分配了一个
WAVEHDR
。
It is my understanding that I am responsible for de-allocating the WAVEHDR when I am done with it -- that the Java Garbage Collector won't destroy it for me.我的理解是,当我完成 WAVEHDR 时,我有责任解除它的分配——Java 垃圾收集器不会为我销毁它。
I initially considered putting the de-allocation code in finalize()
in java, so that the C++ struct is always automatically de-allocated when the java object is garbage-collected in java, but according to this answer this method will cause memory leaks.我最初考虑将取消分配代码放在 java 中的
finalize()
中,这样当 java 对象在 java 中被垃圾收集时,C++ 结构总是自动取消分配,但根据这个答案,这种方法会导致内存泄漏。
I then thought of using the compiler warnings for unclosed resources in the classes like InputStream to catch any mistakes I make, but even if I make WaveHeader
Closable
, I don't get the compiler warnings I'm used to if I don't call close()
.然后我想用没有结束的资源编译器警告中的类象的InputStream的赶上我犯任何错误,但即使我做
WaveHeader
Closable
,我不明白我已经习惯了,如果我不叫编译器警告close()
。
Is there a good way to protect myself from accidental memory leaks here?这里有没有什么好的方法可以保护自己免受意外内存泄漏的影响?
One solution is to create a pool of these WAVEHDR
objects at startup and only allow the Java code to take objects from the pool and recycle them.一种解决方案是在启动时创建这些
WAVEHDR
对象的池,并且只允许 Java 代码从池中取出对象并回收它们。 Failure to return objects will result in an empty pool right after startup and a crash.未能返回对象将在启动和崩溃后立即导致一个空池。
You are right, the compiler won't warn you about missing close()
, but lint or similar static code analysis tool, will.您是对的,编译器不会警告您缺少
close()
,但是lint或类似的静态代码分析工具会。 At any rate, Closeable
is the recommended way to go, and if you use it with try()
, the language will be on your side.无论如何,
Closeable
是推荐的方法,如果你将它与try()
,语言将站在你这边。 Still, it's a good practice to call close()
from finalize()
(unless you know that your JVM has the bug described by Steven M. Cherry ).尽管如此,从
finalize()
调用close()
是一个很好的做法(除非您知道您的 JVM 有Steven M. Cherry 描述的错误)。
By the way, he did not say that
finalize()
caused a memory leak;顺便说一句,他并没有说
finalize()
导致了内存泄漏; this was a heap corruption , something much worse;这是一个堆损坏,更糟糕的事情; but this report is of 2008, so you have little chance to encounter this bug in production.
但这份报告是 2008 年的,所以你几乎没有机会在生产中遇到这个错误。
As for the specific case of WAVEHDR
, I would suggest not to allocate it in C++ on heap, but rather to keep it all (with the buffer) allocated in Java as a direct ByteBuffer:至于
WAVEHDR
的具体情况,我建议不要在 C++ 中在堆上分配它,而是将其全部(连同缓冲区)在 Java 中作为直接 ByteBuffer 分配:
public class WaveHeader {
private ByteBuffer waveHeader;
private final static int PTR_LENGTH = 8; // 64-bit Windows
private final static int DWORD_LENGTH = 4;
private final static int WAVEHDR_LENGTH = 4*PTR_LENGTH + 4*DWORD_LENGTH;
private native static void init(ByteBuffer waveHeader);
public WaveHeader(int bufferLength) {
waveHeader = allocateDirect(bufferLength + WAVEHDR_LENGTH);
init(waveHeader);
}
}
JNIEXPORT void JNICALL Java_net_joshuad_waveformjni_WaveHeader_init(JNIEnv* env, jclass clazz, jobject byteBuffer) {
auto waveHeader = reinterpret_cast<WAVEHDR*>(env->GetDirectBufferAddress(byteBuffer));
jlong capacity = env->GetDirectBufferCapacity(byteBuffer);
waveHeader->lpData = reinterpret_cast<LPSTR>(waveHeader+1);
waveHeader->dwBufferLength = capacity-sizeof(WAVEHDR);
}
Now you don't care about close()
or managing the memory of your C++ object: it is all managed by Java.现在您不再关心
close()
或管理 C++ 对象的内存:它全部由 Java 管理。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.