Can't allocate large char array in JNI

Discussion in 'Java' started by CD1, Apr 21, 2008.

  1. CD1

    CD1 Guest

    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!
     
    CD1, Apr 21, 2008
    #1
    1. Advertising

  2. On Sun, 20 Apr 2008 20:07:48 -0700 (PDT), CD1 wrote:
    > 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

    --
     
    Gordon Beaton, Apr 21, 2008
    #2
    1. Advertising

  3. CD1

    CD1 Guest

    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-
    >depth);

    if ((*env)->ExceptionCheck(env)) {
    return NULL;
    }
    set_int(env, iplimage_cls, jiplimage, "width", (jint) cv_image-
    >width);

    if ((*env)->ExceptionCheck(env)) {
    return NULL;
    }
    set_int(env, iplimage_cls, jiplimage, "height", (jint) cv_image-
    >height);

    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-
    >origin);

    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-
    >align);

    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!

    On Apr 21, 4:12 am, Gordon Beaton <> wrote:
    > On Sun, 20 Apr 2008 20:07:48 -0700 (PDT), CD1 wrote:
    > > 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
    >
    > --
     
    CD1, Apr 21, 2008
    #3
  4. On Mon, 21 Apr 2008 08:07:08 -0700 (PDT), CD1 wrote:
    > 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

    --
     
    Gordon Beaton, Apr 22, 2008
    #4
  5. CD1

    Roedy Green Guest

    On Sun, 20 Apr 2008 20:07:48 -0700 (PDT), CD1
    <> wrote, quoted or indirectly quoted someone
    who said :

    >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.
    --

    Roedy Green Canadian Mind Products
    The Java Glossary
    http://mindprod.com
     
    Roedy Green, Apr 22, 2008
    #5
  6. On 22 Apr 2008 05:46:23 GMT, Gordon Beaton wrote:
    > 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

    --
     
    Gordon Beaton, Apr 22, 2008
    #6
  7. CD1

    CD1 Guest

    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!

    On Apr 22, 5:49 am, Gordon Beaton <> wrote:
    > On 22 Apr 2008 05:46:23 GMT, Gordon Beaton wrote:
    >
    > > 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
    >
    > --
     
    CD1, Apr 24, 2008
    #7
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Bill Medland
    Replies:
    1
    Views:
    1,517
    Jean-Francois Briere
    Oct 26, 2006
  2. lovecreatesbeauty
    Replies:
    1
    Views:
    1,061
    Ian Collins
    May 9, 2006
  3. Replies:
    3
    Views:
    738
  4. davidb
    Replies:
    0
    Views:
    766
    davidb
    Sep 1, 2006
  5. Ralph Shnelvar
    Replies:
    44
    Views:
    426
    Albert Schlef
    Jan 7, 2010
Loading...

Share This Page