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,
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,
Just wanted to mention that we see this leak as well (using 8.3.0), using the same reference path.
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,
Hi Babar,
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,