Memory leak while converting emf

Currently, I use the Aspose.Imaging Library in java to convert image types, mainly to convert EMF images to PNG images. However, as time flows and the conversion is triggered continuously, the memory of the server will continue to grow, especially when the conversion fails or the conversion time is too long,The increase will be obvious, and the memory will not drop (return) after the conversion. Is there any solution to this problem?

here is my code:

public static void main(String[] args) throws IOException, InterruptedException {
    for (int i = 0; i < 100; i++) {
        Thread.sleep(100);
        convertSingleXlsx();
    }
    while (true) {}
}

private static void convertSingleXlsx() throws IOException {

    String filePath = "/test-181.emf";
    InputStream inputStream = new FileInputStream(filePath);

    com.aspose.imaging.fileformats.emf.EmfImage image =
        (com.aspose.imaging.fileformats.emf.EmfImage)com.aspose.imaging.Image.load(inputStream);
    try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) {
        long startTime = System.currentTimeMillis();

        image.save(byteArrayOutputStream, new com.aspose.imaging.imageoptions.PngOptions());
        byte[] bWrite = byteArrayOutputStream.toByteArray();
        OutputStream os = new FileOutputStream("/Users/wangfang/Downloads/_out1.png");
        for (int x = 0; x < bWrite.length; x++) {
            os.write(bWrite[x]);
        }
        os.close();

        //image.save("/Downloads/_out.svg", new com.aspose.imaging.imageoptions.SvgOptions());
        long wmfTime = System.currentTimeMillis();
        System.out.println("convert time:" + (wmfTime - startTime));
    } catch (Exception e) {
        System.out.println(e);
    } finally {
        image.close();
        inputStream.close();
    }

}

test-181.emf.zip (30.6 KB)

Hello, @wanghq09
Thank you for using Aspose.Imaging for Java.
Please, let me clarify the situation.

Firstly, we should pay attention to how memory is allocated in Java and how it returns to the heap.
Life stages of allocated memory (simplified)

  1. Allocation (memory is marked as in use).
  2. Using.
  3. The memory is no longer in use, and no one is referring to it, BUT the GC has not been started yet, so the memory remains unavailable (marked as in use). This situation is like memory usage growing up.
  4. GC is started, it detects a memory that no one refers to, and returns it to the heap.
  5. Memory is marked as free/available.

From this point of view, the better is to rewrite your example. We do not need to save an image in memory (ByteArrayOutputStream) and save it to a file. We can save it to a file directly (less memory needed).

try {
	com.aspose.imaging.fileformats.emf.EmfImage image =
		(com.aspose.imaging.fileformats.emf.EmfImage)com.aspose.imaging.Image.load(filePath);

	long startTime = System.currentTimeMillis();

	image.save("/Users/wangfang/Downloads/_out1.png", new com.aspose.imaging.imageoptions.PngOptions());
	long wmfTime = System.currentTimeMillis();
	System.out.println("convert time:" + (wmfTime - startTime));
} catch (Exception e) {
System.out.println(e);
} finally {
image.close();
}

The using InputStream and OutputStream is less effective from the memory using point.
The better way is to use the following methods
Image.load("filename") or Image.load(java.io.RandomAccessFile file)
and
Image.save("filename") or Image.save(java.io.RandomAccessFile file)

Or you could call gc.collect() to start the garbage collector manually (not recommended without a definite reason)

Please feel free to write me if I can help you anyhow more.

I convert the emf file in server as a service, so i can not save it to a file, is there some other way to handle this?

@wanghq09
Ok, got it. Yes, you can use ByteArrayOutputStream but better set the start capacity of it.
The second solution is to limit the memory amount for internal use inside Aspose.Imaging.
It is possible to set a limit for all Aspose.Imaging operations or for a specific image that you process.
More details here:

Using the memory strategy optimization you can limit the memory consumption by the cost of the speed of conversions.

public static void main(String[] args) throws IOException, InterruptedException {
    
	// Set 1GB as maximum for all Aspose.Imaging internal operations.
	// Or instead, you can set the local limit in convertSingleXlsx
	com.aspose.imaging.memorymanagement.Configuration.setBufferSizeHint(1024); // In MB
	
	for (int i = 0; i < 100; i++) {
        Thread.sleep(100);
        convertSingleXlsx();
    }
	gc.collect()
    while (true) {
		Thread.yield();
	}
}

private static void convertSingleXlsx() throws IOException {

    String filePath = "/test-181.emf";
    InputStream inputStream = new FileInputStream(filePath);

	com.aspose.imaging.LoadOptions loadOptions = new com.aspose.imaging.LoadOptions();
	// This line is unnecessary if global limit was set by Configuration.setBufferSizeHint(1024)
	loadOptions.setBufferSizeHint(200); // 200 MB
    com.aspose.imaging.fileformats.emf.EmfImage image =
        (com.aspose.imaging.fileformats.emf.EmfImage)com.aspose.imaging.Image.load(inputStream, loadOptions);
    try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) {
        long startTime = System.currentTimeMillis();
		// NOTE: that byteArrayOutputStream will be allocate memory by it self ignoring our BufferSizeHint
        image.save(byteArrayOutputStream, new com.aspose.imaging.imageoptions.PngOptions());
        // NOTE: Here will be allocate memory additionally (in a second time) by byteArrayOutputStream ignoring our BufferSizeHint
		byte[] bWrite = byteArrayOutputStream.toByteArray();
        OutputStream os = new FileOutputStream("/Users/wangfang/Downloads/_out1.png");
        for (int x = 0; x < bWrite.length; x++) {
            os.write(bWrite[x]);
        }
        os.close();

        //image.save("/Downloads/_out.svg", new com.aspose.imaging.imageoptions.SvgOptions());
        long wmfTime = System.currentTimeMillis();
        System.out.println("convert time:" + (wmfTime - startTime));
    } catch (Exception e) {
        System.out.println(e);
    } finally {
        image.close();
        inputStream.close();
    }
}