[英]How to manage memory deallocation properly between C++ and Java with JNI?
我正在开发一个Java 库,它是Windows 波形函数的一个瘦包装器,可以通过 Java 播放 24 位音频。 (JVM 仅支持 8 位和 16 位音频)。
Windows 波形函数中的范例是:
waveOutUnprepareHeader
鉴于我的 Java 库将成为本机波形函数的瘦包装器,我有一个用于 Header Pointer 的类,因此我可以根据需要将其保留在范围内,根据需要传递它,并最终调用waveOutUnprepareHeader
在上面。
public class WaveHeader {
long waveHeaderPointer;
public WaveHeader(byte[] buffer) {
waveHeaderPointer = HWaveOut.createHeader(buffer, buffer.length);
}
}
上面第 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;
}
如您所见,我在 C++ 中在堆上分配了一个WAVEHDR
。
我的理解是,当我完成 WAVEHDR 时,我有责任解除它的分配——Java 垃圾收集器不会为我销毁它。
我最初考虑将取消分配代码放在 java 中的finalize()
中,这样当 java 对象在 java 中被垃圾收集时,C++ 结构总是自动取消分配,但根据这个答案,这种方法会导致内存泄漏。
然后我想用没有结束的资源编译器警告中的类象的InputStream的赶上我犯任何错误,但即使我做WaveHeader
Closable
,我不明白我已经习惯了,如果我不叫编译器警告close()
。
这里有没有什么好的方法可以保护自己免受意外内存泄漏的影响?
一种解决方案是在启动时创建这些WAVEHDR
对象的池,并且只允许 Java 代码从池中取出对象并回收它们。 未能返回对象将在启动和崩溃后立即导致一个空池。
您是对的,编译器不会警告您缺少close()
,但是lint或类似的静态代码分析工具会。 无论如何, Closeable
是推荐的方法,如果你将它与try()
,语言将站在你这边。 尽管如此,从finalize()
调用close()
是一个很好的做法(除非您知道您的 JVM 有Steven M. Cherry 描述的错误)。
顺便说一句,他并没有说
finalize()
导致了内存泄漏; 这是一个堆损坏,更糟糕的事情; 但这份报告是 2008 年的,所以你几乎没有机会在生产中遇到这个错误。
至于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);
}
现在您不再关心close()
或管理 C++ 对象的内存:它全部由 Java 管理。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.