OutOfMemoryError- Aspose.Cells holds onto millions of cells forever

I have been investigating an OutOfMemoryError in a particular unit test,
where the error appears to occur long after we have released our reference
to the document.
What I have found is that Aspose.Cells has a static field in
CommentCollection which holds a reference to the first CommentCollection
you create, and thus indirectly a reference to the Worksheet and ultimately
the entire document. If the first document you process is therefore very
large, subsequent documents can't be processed either, because the memory
has not been released.
This is in version 8.1.2.
Reference path to root:
--> sun.misc.Launcher$AppClassLoader@0x75e0179e0 (146 bytes) (field classes:)
--> java.util.Vector@0x75e152bc8 (36 bytes) (field elementData:)
--> [Ljava.lang.Object;@0x75e152be8 (20496 bytes) (Element 1200 of [Ljava.lang.Object;@0x75e152be8:)
--> class com.aspose.cells.CommentCollection (108 bytes) (static field a:)
--> com.aspose.cells.CommentCollection$a@0x75e6b4d68 (24 bytes) (field a:)
--> com.aspose.cells.CommentCollection@0x75e6b4d78 (40 bytes) (field c:)
--> com.aspose.cells.Worksheet@0x75e6b4dc8 (348 bytes) (field r:)
--> com.aspose.cells.WorksheetCollection@0x75e4bbc90 (365 bytes) (field a:)
--> java.util.ArrayList@0x75e4bbd70 (32 bytes) (field elementData:)
--> [Ljava.lang.Object;@0x75e4bbd88 (96 bytes) (Element 5 of [Ljava.lang.Object;@0x75e4bbd88:)
--> com.aspose.cells.Worksheet@0x75e1b2040 (348 bytes) (field o:)
--> com.aspose.cells.Cells@0x75e1b2138 (175 bytes) (field j:)
--> com.aspose.cells.aoz@0x75e1b2220 (44 bytes) (field d:)
--> com.aspose.cells.aoz$c@0x75e1b2240 (36 bytes) (field b:)
--> [Lcom.aspose.cells.aoz$a;@0x75e9ea398 (2496 bytes) (Element 233 of [Lcom.aspose.cells.aoz$a;@0x75e9ea398:)
--> com.aspose.cells.aoz$a@0x75eb2be08 (44 bytes) (field c:)
--> [Lcom.aspose.cells.ek;@0x75eb2be40 (2016 bytes) (Element 132 of [Lcom.aspose.cells.ek;@0x75eb2be40:)
--> com.aspose.cells.ek@0x75eb316f0 (35 bytes) 

Why a reference is being kept:

You have a static field:

    private static CommentCollection.a a;
This field is initialised from the constructor
(it is usually not a good idea to initialise statics from non-static methods!):
	CommentCollection(Worksheet sheet) {
this.c = sheet;
this.b = new Comment();
if(a == null) {
a = new CommentCollection.a();
}
}
Plus the comparator class itself lacks the static keyword:
	private class a implements Comparator {
private a() {
}

public int compare(Object x, Object y) {
Comment var3 = (Comment)x;
Comment var4 = (Comment)y;
return var3.getRow() < var4.getRow()?-1:(var3.getRow() > var4.getRow()?1:var3.getColumn() - var4.getColumn());
}
}

This comparator keeps a reference to the CommentCollection forever,
and therefore the entire workbook and the millions of cells which are
now eating all our memory.

The workaround on our end seems to be to ensure that the first
spreadsheet processed is a very small one.

There seem to be even more, even after I worked around that problem. Now I see large quantities of other objects being kept in memory. Reference chains:

1)
–> sun.misc.Launcher$AppClassLoader@0x75dea5888 (146 bytes) (field classes:)
–> java.util.Vector@0x75e12f8f8 (36 bytes) (field elementData:)
–> [Ljava.lang.Object;@0x75e12f918 (20496 bytes) (Element 1063 of [Ljava.lang.Object;@0x75e12f918:)
–> class com.aspose.cells.a.d.fr (108 bytes) (static field v:)
–> com.aspose.cells.a.e.b@0x75e3ccb28 (64 bytes) (field table:)
–> [Ljava.util.HashMap$Node;@0x75e3ccb58 (144 bytes) (Element 14 of [Ljava.util.HashMap$Node;@0x75e3ccb58:)
–> java.util.HashMap$Node@0x75e81ab10 (44 bytes) (field value:)
–> com.aspose.cells.a.d.fp@0x75e3b77a0 (200 bytes) (field h:)
–> com.aspose.cells.a.d.fu@0x75e3b7aa8 (72 bytes) (field table:)
–> [Ljava.util.HashMap$Node;@0x75e3f8098 (32784 bytes) (Element 746 of [Ljava.util.HashMap$Node;@0x75e3f8098:)
–> java.util.HashMap$Node@0x75e4d7ed0 (44 bytes) (field value:)
–> com.aspose.cells.a.d.ft@0x75e4d7f00 (30 bytes)

2)
–> sun.misc.Launcher$AppClassLoader@0x75dea5888 (146 bytes) (field classes:)
–> java.util.Vector@0x75e12f8f8 (36 bytes) (field elementData:)
–> [Ljava.lang.Object;@0x75e12f918 (20496 bytes) (Element 1063 of [Ljava.lang.Object;@0x75e12f918:)
–> class com.aspose.cells.a.d.fr (108 bytes) (static field v:)
–> com.aspose.cells.a.e.b@0x75e3ccb28 (64 bytes) (field table:)
–> [Ljava.util.HashMap$Node;@0x75e3ccb58 (144 bytes) (Element 14 of [Ljava.util.HashMap$Node;@0x75e3ccb58:)
–> java.util.HashMap$Node@0x75e81ab10 (44 bytes) (field value:)
–> com.aspose.cells.a.d.fp@0x75e3b77a0 (200 bytes) (field F:)
–> com.aspose.cells.a.d.be@0x75e5f3710 (32 bytes) (field a:)
–> [Lcom.aspose.cells.a.d.bd;@0x75e5f3728 (27064 bytes)

I’m not sure if these are the ones holding the bulk of the data, because the heap histogram shows 1.5GB of byte[] but each byte[] is very small, so it’s time consuming to track each one of those down.


Hi Nicholas,


First of all, I request you to try the presented scenario against Aspose.Cells for Java 8.5.0.4. that is currently the latest version whereas your current revision v8.4.1.0 is almost one year old (published on 07-24-2014). The API has improved a lot during this period so I hope you will not face OutOfMemoryError with recent releases. Moreover, the API now provides the Memory Preferences that further improves the memory utilization.

In case the problem persists with latest version then we require an executable standalone demo application along with the supporting samples to properly & efficiently investigate the matter on our side. Please note, it is important for us to replicate the problem with most recent release before escalating it for detailed analysis so I also request you to provide the details of the tool used for monitoring.

8.5.0.4 has the same bug if you just look at the code. CommentCollection: Still static: private static CommentCollection.za b;
Still initialised from the constructor: CommentCollection(Worksheet sheet) { this.d = sheet; this.c = new Comment(); if(b == null) { b = new CommentCollection.za(); } }
Still not static: private class za implements Comparator { private za() { } public int compare(Object x, Object y) { Comment var3 = (Comment)x; Comment var4 = (Comment)y; return var3.getRow() < var4.getRow()?-1:(var3.getRow() > var4.getRow()?1:var3.getColumn() - var4.getColumn()); } }
To reproduce this, you can load literally any spreadsheet file. In fact, you don't even have to start with a file:
@Test public void testAsposeMemoryBug() throws Exception { License cellsLicence = new License(); cellsLicence.setLicense("--- put licence here ---"); //applyWorkaround(); WeakReference comments = processFile(); // Attempting to clean up objects which aren't really referenced.
// Usually you wouldn't do this. System.gc(); System.gc(); System.gc(); // The reference was weak, so we expect it to be collected.
// (The GC might not do it though, so this can't be written
// as a reproducible test.) System.out.println(comments.get()); } private void applyWorkaround() throws Exception { // Force Aspose to keep a very small spreadsheet in memory forever,
// to avoid it doing so with a much larger one: Workbook workbook = new Workbook(); workbook.getWorksheets().get(0).getComments(); } Run this test and it will print something like:
com.aspose.cells.CommentCollection@39f0678b
This shows that a reference is still held by something other than our code. (It's held by that static in CommentCollection.) Uncomment the call to applyWorkaround() and it will now print: null The workaround forces the library to store a small spreadsheet in memory forever by triggering the bug itself. The second spreadsheet, then, doesn't trigger the bug, so the reference is properly released.

You can of course use jmap/jhat to verify exactly where the reference is held,
which is how I found this in the first place.

Hi Nicholas,


Thank you for testing the latest release. We have logged an investigative ticket with Id CELLSNET-43785 in our database. Please spare us some time to properly investigate the matter, and get back to you with updates in this regard.

Just wanted to mention that we see this leak as well (using 8.3.0), using the same reference path.

Seems like someone forgot to make the “CommentCollection.za” Comparator implementation static (nested class instead of inner class) - it retains the implicit reference to its outer class.

Hi,

Thanks for your posting and considering Aspose APIs.

We have also logged your comment in our database against this issue for product team consideration. Hopefully, it will help the team to investigate and fix this issue. Please spare us some time. Once we will have some fix or other update for you, we will let you know asap.

Hi,

Thanks for your using Aspose.Cells.

Please download and try the latest fix: Aspose.Cells for .NET v8.5.1.1 and let us know your feedback.

What about the Java version?

Hi Taras,


As you may be aware that all Aspose for Java APIs are auto-ported from their equivalent revision of .NET API therefore in case an issue is found, we first fix it in the .NET API and auto-port the changes for Java API. In reference to the problem discussed in this thread, we have currently addressed it for .NET API and hope to publish the auto-ported Java API in couple of days.

The ticket Id for tracking the issue status for Java API is CELLSJAVA-41419. If you wish to get notifications about this fix, you may subscribe to this thread or create a new one so we could attach this ticket your thread.

Hi Babar,

Thanks for clarifying.
I have observed the issue indeed in Aspose Cells for Java.
Kind regards,
Taras

Hi Taras,

Thanks for your clarification and using Aspose.Cells.

Since the issue is fixed in .NET version therefore we are hopeful your issue will be fixed soon and you will get a fixed java version before the end of next week. In case, the issue is difficult to be resolved, then it might take longer like couple of more weeks.

Hi,

Thanks for using Aspose.Cells for Java.

Please download and try this fix: Aspose.Cells for Java v8.5.1.2 and let us know your feedback.

The issues you have found earlier (filed as CELLSJAVA-41419) have been fixed in this update.


This message was posted using Notification2Forum from Downloads module by Aspose Notifier.

I can confirm that Aspose Cells for Java 8.5.2 does indeed fix this memory leak.

Hi Taras,


Thank you for the confirmation. Please feel free to contact us back in case you require our further assistance with Aspose APIs.