Page 1 of 1

[JNI Memory Leak] Help please

Posted: 2014-11-06T20:34:21-07:00
by ring8595
I use MagickWand to build a JNI to transcoding images from JAVA.
our version is ImageMagick-devel-6.5.4.7-6.el6_2.x86_64

I find that the program is continuing consuming memory. I think that there is memory leak in the JNI part.
I debug it for a week. I got nothing.
can anyone help me. thanks

Code: Select all

#include <algorithm>
#include <vector>
#include <string.h>
#include <stdint.h>
#include <arpa/inet.h>

#include <jni.h>
#include <magick/ImageMagick.h>

void encodeAlpha(unsigned char *rawAlphaBytes,
       std::vector<unsigned char> &encodedAlphaBytes, size_t rawAlphaLength);

extern "C" JNIEXPORT void JNICALL ImageTranscoderJNIImpl_init(
        JNIEnv *env, jclass javaClass) {
    MagickCoreGenesis(NULL, (MagickBooleanType) false);
}


extern "C" JNIEXPORT jbyteArray JNICALL ImageTranscoderJNIImpl_transcode(
        JNIEnv *env, jclass javaClass, jint width, jint height,
        jboolean cropToFit, jboolean keepRatio, jint quality,
        jfloat blur, jbyteArray input) {
    ExceptionInfo exception;
    size_t inputSize = env->GetArrayLength(input);
    jbyte *inputBytes = env->GetByteArrayElements(input, 0);

    // Decode the input byte array to a Magick image
    GetExceptionInfo(&exception);
    ImageInfo *inputImageInfo = AcquireImageInfo();
    Image *inputImage = BlobToImage(inputImageInfo, inputBytes, inputSize,
        &exception);
    DestroyImageInfo(inputImageInfo);
    env->ReleaseByteArrayElements(input, inputBytes, 0);

    if (inputImage == NULL) {
        DestroyExceptionInfo(&exception);
        return NULL;
    }
    DestroyExceptionInfo(&exception);

    // Calculate image scale
    int origWidth = inputImage->columns;
    int origHeight = inputImage->rows;
    int scaleWidth = width;
    int scaleHeight = height;

    bool widthRelativelyShort =
      scaleWidth * origHeight / origWidth > scaleHeight;

    if( keepRatio ) {
        if( widthRelativelyShort == cropToFit ) {
            scaleHeight = scaleWidth * origHeight / origWidth;
        } else {
            scaleWidth = scaleHeight * origWidth / origHeight;
        }
    }

    // Rescale image
    GetExceptionInfo(&exception);
    Image *scaledImage = ResizeImage(inputImage, scaleWidth, scaleHeight,
        LanczosFilter, blur, &exception);
    DestroyImages(inputImage);

    if (scaledImage == NULL) {
        DestroyExceptionInfo(&exception);
        return NULL;
    }
    DestroyExceptionInfo(&exception);

    // Calculate final dimensions
    int finalWidth = std::min(width, scaleWidth);
    int finalHeight = std::min(height, scaleHeight);

    RectangleInfo ri = {
        finalWidth,
        finalHeight,
        (scaleWidth - finalWidth) / 2,
        (scaleHeight - finalHeight) / 2,
    };

    // Crop image
    GetExceptionInfo(&exception);
    Image *croppedAndScaledImage = CropImage(scaledImage, &ri, &exception);
    DestroyImages(scaledImage);

    if (croppedAndScaledImage == NULL) {
      DestroyExceptionInfo(&exception);
      return NULL;
    }
    DestroyExceptionInfo(&exception);

    // Handle image transparency if needed
    std::vector<unsigned char> encodedAlphaBytes;
    if (croppedAndScaledImage->matte) {
      // Allocate space for raw alpha
      unsigned char *rawAlphaBytes =
          new unsigned char[finalWidth * finalHeight];

      GetExceptionInfo(&exception);
      DispatchImage(croppedAndScaledImage, 0, 0, finalWidth, finalHeight, "A",
          CharPixel, rawAlphaBytes, &exception);

      encodeAlpha(rawAlphaBytes, encodedAlphaBytes, finalWidth * finalHeight);

      DestroyExceptionInfo(&exception);
      delete [] rawAlphaBytes;
    }
    int encodedAlphaSize = encodedAlphaBytes.size();

    // Encode transformed image as a JPEG
    ImageInfo *outputImageInfo = AcquireImageInfo();
    strcpy(outputImageInfo->magick, "JPEG");
    outputImageInfo->magick[4] = '\0';
    if (croppedAndScaledImage->colorspace != RGBColorspace) {
      TransformRGBImage(croppedAndScaledImage, RGBColorspace);
    }
    outputImageInfo->type = TrueColorType;

    // This is stupid - when writing an image to a blob we need to set the
    // quality parameter on the input image, not on the ImageInfo :(
    croppedAndScaledImage->quality = quality;

    // Remove color profile info, comments, etc.
    StripImage(croppedAndScaledImage);

    GetExceptionInfo(&exception);
    size_t outputSize = 0;
    unsigned char *outputBytes = ImageToBlob(outputImageInfo,
        croppedAndScaledImage, &outputSize, &exception);
    DestroyImages(croppedAndScaledImage);
    DestroyImageInfo(outputImageInfo);

    if (outputBytes == NULL) {
      DestroyExceptionInfo(&exception);
      return NULL;
    }
    DestroyExceptionInfo(&exception);

    // Note: Allocating 18 extra bytes for transparency + position info
    jbyteArray output = env->NewByteArray(outputSize + encodedAlphaSize + 18);
    if (output == NULL) {
        RelinquishMagickMemory(outputBytes);
        return NULL;
    }

    // Write encoded image
    env->SetByteArrayRegion(output, 0, outputSize, (jbyte *) outputBytes);

    // Write transparency
    if (!encodedAlphaBytes.empty()) {
        env->SetByteArrayRegion(output, outputSize, encodedAlphaSize,
            (const jbyte *) &encodedAlphaBytes[0]);
    }
    encodedAlphaBytes.clear();
    // Write transparency and position info
    uint16_t outAlphaLength = htons(encodedAlphaSize);
    uint32_t outWidth = htonl(finalWidth);
    uint32_t outHeight = htonl(finalHeight);
    uint32_t imageXPos = htonl(-1);
    uint32_t imageYPos = htonl(-1);
    // Transparency size
    env->SetByteArrayRegion(output, outputSize + encodedAlphaSize, 2,
        (jbyte *) &outAlphaLength);
    // Dimensions
    env->SetByteArrayRegion(output, outputSize + encodedAlphaSize + 2, 4,
        (jbyte *) &outWidth);
    env->SetByteArrayRegion(output, outputSize + encodedAlphaSize + 6, 4,
        (jbyte *) &outHeight);
    // Position
    env->SetByteArrayRegion(output, outputSize + encodedAlphaSize + 10, 4,
        (jbyte *) &imageXPos);
    env->SetByteArrayRegion(output, outputSize + encodedAlphaSize + 14, 4,
        (jbyte *) &imageYPos);

    RelinquishMagickMemory(outputBytes);

    return output;
}


void encodeAlpha(unsigned char *rawAlphaBytes,
        std::vector<unsigned char> &encodedAlphaBytes, size_t rawAlphaLength) {
    if( rawAlphaLength > 1 ) {
        int currentCount = 1;
        // Note: we use alpha between 0-63 and use the extra bits for
        // encoding
        int prevAlpha = ( rawAlphaBytes[ 0 ] >> 2 ) & 0x3f;

        for( int i = 1; i < rawAlphaLength; i++ ) {
            int alpha = ( rawAlphaBytes[ i ] >> 2 ) & 0x3f;

            if( prevAlpha == alpha && currentCount < 255 ) {
                currentCount++;
            } else {
                if( currentCount == 1 ) {
                    // we use 0-63 to code one alpha value
                    encodedAlphaBytes.push_back( prevAlpha );
                } else {
                    // and 64-127 to code a series of an alpha value
                    encodedAlphaBytes.push_back( prevAlpha | 0x40 );
                    encodedAlphaBytes.push_back( currentCount );
                    currentCount = 1;
                }

                prevAlpha = alpha;
            }
        }

        if( currentCount == 1 ) {
            // we use 0-63 to code one alpha value
            encodedAlphaBytes.push_back( prevAlpha );
        } else {
            // and 64-127 to code a series of an alpha value
            encodedAlphaBytes.push_back( prevAlpha | 0x40 );
            encodedAlphaBytes.push_back( currentCount );
        }
    }
}


Re: [JNI Memory Leak] Help please

Posted: 2014-11-06T21:36:50-07:00
by fmw42
I cannot help, but I would point out that your version of IM at 6.5.4.7 is ancient (about 350 versions old). Perhaps an upgrade might help?