I am working on a Java library that is a thin wrapper for the Windows Waveform Functions to play 24 bit audio through Java. (The JVM only supports 8bit and 16bit audio).
The paradigm in the Windows Waveform Functions is:
- Create Header struct
- call waveOutPrepareHeader on the Header.
- Send header to sound card
- Sound card plays asynchronously (which means the Header must stay in memory for the duration of the audio playing)
- When the sound card is done playing, it sets a "Done" bit in the header
- When the "done" bit is set, I have to call
waveOutUnprepareHeader
- Then I can remove the header from memory
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.
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:
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++.
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.
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.
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()
.
Is there a good way to protect myself from accidental memory leaks here?