Can't allocate large char array in JNI

C

CD1

Hi there!

I'm trying to allocate a char array using JNI, but when the length is
too big it doesn't work! My char array is a representation of an
image, and its size is the width * height * 3 characters, so it
usually goes beyond 1 MB. My code works with smaller images, but when
the array size is bigger than 718832 (don't know why this is the
maximum value), the JVM crashes and it returns 134 as the exit code.

My code is like this:

// the variables are JNIEnv *env, int length, jchar *value, jmethodID
char_array_fid

jcharArray jchar_array = (*env)->NewCharArray(env, length);
if (jchar_array == NULL) {
return;
}
(*env)->SetCharArrayRegion(env, jchar_array, 0, length, value); // !!!
(*env)->SetObjectField(env, obj, char_array_fid, jchar_array);

By using printfs, I've found out the JVM crashes when it calls the
function SetCharArrayRegion, but only if the length is greater than
718832. And if I modify the length value to something like 1000, this
exact code works fine.

Any help would be appreciated :)

See ya!
 
G

Gordon Beaton

My code is like this:

Code that's "like" yours doesn't help anyone debug your real code.
// the variables are JNIEnv *env, int length, jchar *value, jmethodID
char_array_fid

jcharArray jchar_array = (*env)->NewCharArray(env, length);
if (jchar_array == NULL) {
return;
}
(*env)->SetCharArrayRegion(env, jchar_array, 0, length, value); // !!!
(*env)->SetObjectField(env, obj, char_array_fid, jchar_array);

By using printfs, I've found out the JVM crashes when it calls the
function SetCharArrayRegion, but only if the length is greater than
718832. And if I modify the length value to something like 1000, this
exact code works fine.

Any help would be appreciated :)

Try posting a compilable example or at least a complete function. The
problem is usually in the parts you didn't post.

How many jchars are in the "jchar *value" array that you pass to
SetCharArrayRegion? Perhaps more importantly, how did you allocate and
initialize it?

What does (*env)->ExceptionOccurred(env) (and ExceptionDescribe()) say
after the call to SetCharArrayRegion()? After SetObjectField()?

/gordon

--
 
C

CD1

Hi Gordon,

Sorry for the lack of details. Here's the full code:

// start here

jobject new0(JNIEnv *env, jclass cls) {
jmethodID constructor_mid = (*env)->GetMethodID(env, cls,
"<init>", "()V");
if (constructor_mid == NULL) {
return NULL;
}
return (*env)->NewObject(env, cls, constructor_mid);
}

void set_int(JNIEnv *env, jclass cls, jobject obj,
char *field_name, jint value) {
jfieldID int_fid = (*env)->GetFieldID(env, cls, field_name, "I");
if (int_fid == NULL) {
return;
}
(*env)->SetIntField(env, obj, int_fid, value);
}

void set_char_array(JNIEnv *env, jclass cls, jobject obj,
char *field_name, jchar *value, int length) {
jfieldID char_array_fid = (*env)->GetFieldID(env, cls, field_name,
"[C");
if (char_array_fid == NULL) {
return;
}
jcharArray jchar_array = (*env)->NewCharArray(env, length);
if (jchar_array == NULL) {
return;
}
(*env)->SetCharArrayRegion(env, jchar_array, 0, length, value);
(*env)->ExceptionDescribe(env);
(*env)->SetObjectField(env, obj, char_array_fid, jchar_array);
}

JNIEXPORT jobject JNICALL Java_OpenCv_loadImage(JNIEnv *env, jclass
jclazz,
jstring jfilePath) {
const char *file_path = (*env)->GetStringUTFChars(env, jfilePath,
NULL);
IplImage *cv_image = cvLoadImage(file_path, CV_LOAD_IMAGE_COLOR);
(*env)->ReleaseStringUTFChars(env, jfilePath, file_path);
if (cv_image == NULL) {
return NULL;
}
jclass iplimage_cls = (*env)->FindClass(env, "IplImage");
if (iplimage_cls == NULL) {
return NULL;
}
jobject jiplimage = new0(env, iplimage_cls);
if (jiplimage == NULL) {
return NULL;
}
set_int(env, iplimage_cls, jiplimage,
"nChannels", (jint) cv_image->nChannels);
if ((*env)->ExceptionCheck(env)) {
return NULL;
}
set_int(env, iplimage_cls, jiplimage, "depth", (jint) cv_image-
if ((*env)->ExceptionCheck(env)) {
return NULL;
}
set_int(env, iplimage_cls, jiplimage, "width", (jint) cv_image-
if ((*env)->ExceptionCheck(env)) {
return NULL;
}
set_int(env, iplimage_cls, jiplimage, "height", (jint) cv_image-
if ((*env)->ExceptionCheck(env)) {
return NULL;
}
set_char_array(env, iplimage_cls, jiplimage, "imageData",
(jchar *) cv_image->imageData, cv_image->imageSize);
if ((*env)->ExceptionCheck(env)) {
return NULL;
}
set_int(env, iplimage_cls, jiplimage,
"dataOrder", (jint) cv_image->dataOrder);
if ((*env)->ExceptionCheck(env)) {
return NULL;
}
set_int(env, iplimage_cls, jiplimage, "origin", (jint) cv_image-
if ((*env)->ExceptionCheck(env)) {
return NULL;
}
set_int(env, iplimage_cls, jiplimage,
"widthStep", (jint) cv_image->widthStep);
if ((*env)->ExceptionCheck(env)) {
return NULL;
}
set_int(env, iplimage_cls, jiplimage,
"imageSize", (jint) cv_image->imageSize);
if ((*env)->ExceptionCheck(env)) {
return NULL;
}
set_char_array(env, iplimage_cls, jiplimage, "imageDataOrigin",
(jchar *) cv_image->imageDataOrigin,
cv_image->width * cv_image->height * cv_image->nChannels);
set_int(env, iplimage_cls, jiplimage, "align", (jint) cv_image-
if ((*env)->ExceptionCheck(env)) {
return NULL;
}
cvReleaseImage(&cv_image);
return jiplimage;
}

// end here

The jchar *value comes from the object allocated by the library
OpenCV, ant its size is the imageSize field of the IplImage struct.

When I run this function passing a small image, it always works; with
a medium image, it works sometimes (I run once and it works, I run
again and it doesn't!); but with a large image (specifically,
imageSize = 1434600), it never worked: the JVM crashes right on the
SetCharArrayRegion call, so the ExceptionDescribe in the line below is
never reached. So, based on this randomness I suppose the problem is
memory related, I don't know ;/

The header of the JVM crash log is this:

#
# An unexpected error has been detected by Java Runtime Environment:
#
# SIGSEGV (0xb) at pc=0x00002ae7f37b1cc7, pid=16200, tid=1076017488
#
# Java VM: Java HotSpot(TM) 64-Bit Server VM (10.0-b22 mixed mode
linux-amd64)
# Problematic frame:
# C [libc.so.6+0x75cc7] memcpy+0x3f7
#
# If you would like to submit a bug report, please visit:
# http://java.sun.com/webapps/bugreport/crash.jsp
#

The purpose of this function, by the way, is to convert a C structure
(IplImage) to a Java object with the same attributes so it can be
parsed on the Java side.

Thanks again!
 
G

Gordon Beaton

The jchar *value comes from the object allocated by the library
OpenCV, ant its size is the imageSize field of the IplImage struct.

What is the real declared type of the cv_image->imageData, i.e. before
the explicit typecast?

What are "sizeof(jchar)" and "sizeof(cv_image->imageData[0])"?

Here's something you can test, add this to the start of set_char_array()
before calling any of the jni functions:

volatile jchar tmp;
int i;

for (i=0; i<length; i++) {
tmp = value;
}

Does this code pass ok?

Try a small example without using your image library. With the
following code I can successfully allocate char arrays of 30MB using
the default java heap size. Beyond that, ExceptionDescribe() correctly
identifies the error as "Out of memory: Java heap space":

// public static native char[] getCharArray(int n);

JNIEXPORT jcharArray JNICALL
Java_Chars_getCharArray(JNIEnv *env, jclass unused, jint n)
{
jcharArray jca;
jchar *jc;
int i;

jca = (*env)->NewCharArray(env, n);

if ((*env)->ExceptionOccurred(env)) {
(*env)->ExceptionDescribe(env);
}

if ((jc = malloc(n * sizeof(jchar)))) {
// some nonsense initial values
for (i=0; i<n; i++) {
jc = i % 255;
}

(*env)->SetCharArrayRegion(env, jca, 0, n, jc);

if ((*env)->ExceptionOccurred(env)) {
(*env)->ExceptionDescribe(env);
}

free(jc);
}

return jca;
}

/gordon

--
 
R

Roedy Green

By using printfs, I've found out the JVM crashes when it calls the
function SetCharArrayRegion, but only if the length is greater than
718832. And if I modify the length value to something like 1000, this
exact code works fine.

Are C strings limited to 64K?
Perhaps you want a byte array. Java chars are 16 bits. C chars are
usually 8 bits.
 
G

Gordon Beaton

What is the real declared type of the cv_image->imageData, i.e. before
the explicit typecast?

What are "sizeof(jchar)" and "sizeof(cv_image->imageData[0])"?

Checked this myself: IplImage->imageSize is "image size in bytes", and
IplImage->imageData is a byte pointer (C char), so by casting to a
(jchar*) without adjusting the length, you are exceeding the bounds of
the imageData array by a factor 2.

You should be using a jbyteArray instead. Always treat explicit
typecasts with suspicion...

/gordon

--
 
C

CD1

Thank you guys, that was really the problem! In C, it's a char array,
but in Java it must be a byte array, because of the sizes.

See ya!

What is the real declared type of the cv_image->imageData, i.e. before
the explicit typecast?
What are "sizeof(jchar)" and "sizeof(cv_image->imageData[0])"?

Checked this myself: IplImage->imageSize is "image size in bytes", and
IplImage->imageData is a byte pointer (C char), so by casting to a
(jchar*) without adjusting the length, you are exceeding the bounds of
the imageData array by a factor 2.

You should be using a jbyteArray instead. Always treat explicit
typecasts with suspicion...

/gordon

--
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top