[JNI Memory Leak] Help please
Posted: 2014-11-06T20:34:21-07:00
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
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 );
}
}
}