How to use env->ReleaseStringUTFChars(...) after env->GetStringUTFChars(...) - java

I am learning to use C++ in Android.
The following function works well, but I do not know how to use ReleaseStringUTFChars(fileName, 0) to release (or free) the memory allocated by env->GetStringUTFChars(input, 0);
Activity_stringFromJNI(
JNIEnv *env,
jobject, /* this */
jstring input) {
const char *str_C = env->GetStringUTFChars(input, 0);
char *data = funcX(str_C); // funcX allocates with malloc
// env->ReleaseStringUTFChars(input, str_C); ???
free(data); // Freeing funcX allocations
return env->NewStringUTF(someString.c_str());
}
Also, I have been trying to see the documentation, but I can't find it.
How can I check if the memory is being released or freed correctly?
Thanks!

Per the JNI documentation (which is available on Oracle's website, the owner of Java), you simply need to pass to ReleaseStringUTFChars() the original jstring and the const char * pointer that GetStringUTFChars() returned, eg:
Activity_stringFromJNI(
JNIEnv *env,
jobject, /* this */
jstring input)
{
const char *str_C = env->GetStringUTFChars(input, 0);
char *data = funcX(str_C); // funcX allocates with malloc
env->ReleaseStringUTFChars(input, str_C);
// ... data is being used ...
// ... data is being used ...
free(data); // Freeing funcX allocations
return env->NewStringUTF(someString.c_str());
}
On a side note, note that JNI deals with Modified UTF-8, not standard UTF-8. But most C++ code that deals with UTF-8 is likely to deal with standard UTF-8, not Modified UTF-8. So, it is generally better to deal with Java strings in their native UTF-16 encoding rather than UTF-8, if you can help it (GetStringChars(), ReleaseStringChars(), NewString(), etc).

Related

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. (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?
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. 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. At any rate, Closeable is the recommended way to go, and if you use it with try(), the language will be on your side. 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).
By the way, he did not say that finalize() caused a memory leak; 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.
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:
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.

Exception 0xC0000005 in jvm.dll when creating an instance via JNI NewObject

I am writing a plug-in for an existing application. Implementation language is C. However, the actual functionality is implemented in Java. For this reason, I am using Java Native Interface (JNI) to create a JVM instance from within C. I can find the appropriate Java class and create an instance. This is what the code looks like:
login(uintptr_t connection, const char* username, …) {
…
jmethodID constructor = (*env)->GetMethodID(env, ps->class, "<init>", "(JLjava/lang/String;)V");
jstring jusername = (*env)->NewStringUTF(env, username);
jobject instance = (*env)->NewObject(env, ps->class, constructor, connection, jusername);
Everything works just fine.
On Linux.
On Windows, it is a complete mess. As soon as I try to create an instance of the Java class, it throws a
EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x0813751f, pid=8, tid=0x00000009
. More details are written to a log file, but the stack trace is not helpful other than pointing to somewhere in the jvm.dll. Stepping through with a debugger has not been insightful. Note this is not the same as this question.
After days, I figured it out.
The constructor I am invoking expects a parameter. The type is long (Java) aka J (JNI Type Signature) aka jlong (corresponing C type). A C uintptr_t is compatible with a jlong.
On Linux, my uintptr_t is 8 bytes long, since I am in a amd64 environment with 64 bit applications. For Windows, the application was build in 32 bit. As a result uintptr_t is only 4 bytes long, but the JVM still expect a 8 byte jlong. However, NewObject is a variadic function, automatic promotion does not happen and type safety is not guaranteed.
login(uintptr_t connection, const char* username, …) {
…
jmethodID constructor = (*env)->GetMethodID(env, ps->class, "<init>", "(JLjava/lang/String;)V");
jstring jusername = (*env)->NewStringUTF(env, username);
jlong jconnection = connection;
jobject instance = (*env)->NewObject(env, ps->class, constructor, jconnection, jusername);
A simple cast to the correct type was the solution. I expect this pitfall to exist with CallVoidMethod or any of the Call*Method mentioned in the documentation, too.

Send C++ string to Java via JNI

I am working on the C++ side of a project that is building an Android application. There is some information (via strings and string arrays) that I need to pass to the Java application (via JNI). I have never done this before, and the people working in the reverse direction have no experience with C++ and admit that they cannot really help.
I did find the following code (from here)
#include <jni.h>
#include "ArrayHandler.h"
JNIEXPORT jobjectArray JNICALL Java_ArrayHandler_returnArray (JNIEnv *env, jobject jobj){
jobjectArray ret;
int i;
char *message[5]= {"first","second","third","fourth","fifth"};
ret= (jobjectArray)env->NewObjectArray(5,env->FindClass("java/lang/String"),env->NewStringUTF(""));
for(i=0;i<5;i++) {
env->SetObjectArrayElement(ret,i,env->NewStringUTF(message[i]));
}
return(ret);
}
But this makes no sense to me. Mostly, I am not sure how I am supposed to incorporate this into the C++ side of the program and I am failing to understand exactly how this works. Is the code sending out the message upon execution of the return(ret); line? Or during the execution of the line within for loop?
Ideally, I would like the string/string array to be sent out "live" in line and not at the end of a function so that I do not have to incorporate a new function.
Will the code I found work for what I want (with some adaptation)? Is what I am looking for even possible? If so, how can I do it?
EDIT/UPDATE:
Having spent the day looking into JNI and the terminology, I think I have failed to correctly communicate what I am looking to achieve both here and as a comment to #jogabonito's answer/reply.
That being said. The code I am working on is for an IM client that will need to push message and presence updates to the Android java application (via JNI) so that the Android application does not poll for updates. I have managed to learn how to setup the functions for the java code to call to requrest information. However, I do not have any idea how to push new message or presence information (jabber stanza strings) to the java code when it comes in. All the code that I have seen on how to do this (see below for example) seems to require getting information from the java code (env, class, methodid, etc).
It does not make sense to me how this is supposed to be possible when it is not the java code calling the function, but my c++ code. Any explanation/help would be very much appreciated.
#include <string.h>
#include <stdio.h>
#include <jni.h>
jstring Java_the_package_MainActivity_getJniString( JNIEnv* env, jobject obj){
jstring jstr = (*env)->NewStringUTF(env, "This comes from jni.");
jclass clazz = (*env)->FindClass(env, "com/inceptix/android/t3d/MainActivity");
jmethodID messageMe = (*env)->GetMethodID(env, clazz, "messageMe", "(Ljava/lang/String;)Ljava/lang/String;");
jobject result = (*env)->CallObjectMethod(env, obj, messageMe, jstr);
const char* str = (*env)->GetStringUTFChars(env,(jstring) result, NULL); // should be released but what a heck, it's a tutorial :)
printf("%s\n", str);
return (*env)->NewStringUTF(env, str);
}
At the request of #Sam, here is a method that avoids using modified UTF-8 because we don't know that it is safe to do so.
NewStringUTF creates a string from its modified UTF-8 encoding.
It is not correct to use it with user data--it's unlikely to be encoded with modified UTF-8. We could just hope that the characters in the data are restricted to keep it compatible. Instead, we can convert it properly.
JNI uses modified UTF-8 strings throughout its API. We can use strings we know are compatible, particularly literals for Java identifiers (except not all currency symbols).
Below are two native method implementations. The second is better in most ways.
For this native method:
private static native String getJniString();
Here is an implementation:
JNIEXPORT jstring JNICALL
Java_the_Package_MainActivity_getJniString(JNIEnv *env, jclass)
{
std::string message = "Would you prefer €20 once "
"or ₹10 every day for a year?";
int byteCount = message.length();
jbyte* pNativeMessage = reinterpret_cast<const jbyte*>(message.c_str());
jbyteArray bytes = env->NewByteArray(byteCount);
env->SetByteArrayRegion(bytes, 0, byteCount, pNativeMessage);
// find the Charset.forName method:
// javap -s java.nio.charset.Charset | egrep -A2 "forName"
jclass charsetClass = env->FindClass("java/nio/charset/Charset");
jmethodID forName = env->GetStaticMethodID(
charsetClass, "forName", "(Ljava/lang/String;)Ljava/nio/charset/Charset;");
jstring utf8 = env->NewStringUTF("UTF-8");
jobject charset = env->CallStaticObjectMethod(charsetClass, forName, utf8);
// find a String constructor that takes a Charset:
// javap -s java.lang.String | egrep -A2 "String\(.*charset"
jclass stringClass = env->FindClass("java/lang/String");
jmethodID ctor = env->GetMethodID(
stringClass, "<init>", "([BLjava/nio/charset/Charset;)V");
jstring jMessage = reinterpret_cast<jstring>(
env->NewObject(stringClass, ctor, bytes, charset));
return jMessage;
}
JNI is awkward. so, if we can move the knowledge that the native string is "UTF-8" to the Java side, we can do this:
private static String getJniString2()
{
return new String(getJniStringBytes(), Charset.forName("UTF-8"));
}
private static native byte[] getJniStringBytes();
And the much simpler implementation:
JNIEXPORT jbyteArray JNICALL Java_the_Package_MainActivity_getJniStringBytes(JNIEnv *env, jclass)
{
std::string message = "Would you prefer €20 once "
"or ₹10 every day for a year?";
int byteCount = message.length();
jbyte* pNativeMessage = reinterpret_cast<const jbyte*>(message.c_str());
jbyteArray bytes = env->NewByteArray(byteCount);
env->SetByteArrayRegion(bytes, 0, byteCount, pNativeMessage);
return bytes;
}
In the function you shared, in your c++ code you are creating an object array with NewObjectArray. Then in your for-loop you are creating a string with NewStringUTF and storing it at an index in your array using SetObjectArrayElement. Until now, your object array is only known to your c++ code and not to your java code. Only when you return it will your java app get access to it.
I can think of a couple of ways to send the string to java from c++, though it may not be exactly what you intended.
Pass a String array to your native function. In you native code you can access each element using GetObjectArrayElement and update it using SetObjectArrayElement. This will probably be pointless since you end up having to call a function which I persume you do not want.
If you already have a string defined as a field in your java code, from your native get access to it using GetFieldID and GetObjectField, and you can update it using SetObjectField. I dont know how you will signal your java code that the field has been updated though ( if you need it)
EDIT
The updated function which you have written is meant to be called from the java layer. The clue for this is name of the function Java_the_package_MainActivity_getJniString. To call java code from a native context, you will need references to the env and obj from java. Have a look at How do I load my own Java class in C on Android? for an approach to get this. You will also probably have to look up how to use global references in JNI
You can convert a c-string into a jstring and return it. An example would look something along the lines of:
JNIEXPORT jstring JNICALL Java_Class_Method(jstring data)
{
// jstring to char *
const char *cStr = (*env)->GetStringUTFChars(env, data, NULL);
// convert char * to jstring and return it
return ((*env)->NewStringUTF(env, cStr));
}
Typically with JNI the calls go from the JVM into the C code. The normal paradigm would be:
Java programmers make a Java class with several methods declared as native (no implementation)
Java programmers compile the class with javac
Java programmers run javah against the compiled .class file, this produces a .h header file
C programmer #include the new header file and implement the interface
The only examples I have seen of doing this in the reverse direction (C code initiating contact with Java) involves having the C code actually create a JVM.
To answer your question about the code sample, the Java Strings being created are returned with the return statement at the end of code execution, logically, this is when program flow for that thread of execution is returned back to the JVM.

Should you call ReleaseStringUTFChars if GetStringUTFChars returned a copy?

The book "Essential JNI: Java Native Interface" by Rob Gordon contains the following code example to convert a jstring to a C string:
const char* utf_string;
jboolean isCopy;
utf_string = env->GetStringUTFChars(str, &isCopy);
/* ... use string ... */
if (isCopy == JNI_TRUE) {
env->ReleaseStringUTFChars(str, utf_string);
}
Note that it only calls ReleaseStringUTFChars if isCopy is true.
But the book Java Native Interface: Programmer's Guide and Specification (alternate link: http://192.9.162.55/docs/books/jni/html/objtypes.html#5161) says:
The ReleaseString-Chars call is
necessary whether GetStringChars has
set *isCopy to JNI_TRUE or JNI_FALSE.
ReleaseStringChars either frees the
copy or unpins the instance, depending
upon whether GetStringChars has
returned a copy or not.
I am correct in assuming this is a bug in Gordon's book?
Yes, your assumption is correct (you should always call ReleaseStringUTFChars).

How to pass an image from C++ to Java

I'm trying to pass an image (PNG file that it is only in memory and not on the hard-disk) from an old C++ application to my Java 8 application, but I'm not finding solutions. From my Java app, I call native functions in the C++ DLL.
In my Java app I suppose I should write something that works with an
array of byte.
public static native Byte[] getByteArrayImage()
In the C++ app I wrote this code
JNIEXPORT jbyteArray JNICALL Java_it_getByteArrayImage(JNIEnv * env, jclass obj, jint imagekind)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
unsigned long bufferSize;
BYTE* buffer = PaintToBufferImage(imagekind, &bufferSize);
jbyteArray jb= env->NewByteArray(bufferSize);
env->SetByteArrayRegion(jb, 0, bufferSize, (jbyte *)buffer);
return jb;
}
Could you help me? Thank you.

Categories