13 December 2008
One of the Java based reporting applications I currently work on has an Excel export feature. For this application I chose the JExcelApi library because I had read that it performs slightly better than the other open source alternatives and that its API was less verbose and generally easier to use. In my dev environment I had no issues with performance but in the staging and production environments, the Excel export was painfully slow. It would sometimes take up to 30 seconds to generate a single page Excel report. After running the code through a profiler I determined there were no bottlenecks in the code nor in the JExcelAPI library itself. The only significant difference between my dev environment and the staging and production environments was that the application in the later two environments would load the full dataset whereas my dev environment loaded a significantly smaller dataset. This meant a difference in heap sizes of 100 MB in dev vs ~12GB in staging/production (don’t ask).
I theorized that garbage collection was the culprit here. However, I was reluctant to believe this since the Excel reports being generated were fairly small and it didn’t seem to me that a single request would be enough to trigger a full garbage collection given the maximum amount of memory the application was allowed to use (16 GB).
Using VisualVM I monitored my local dev tomcat instance in attempt to prove my theory.
As you can see above, the two green arrows show two peaks in CPU utilization and Garbage Collection Activity. These two peaks appeared when I used the Excel export option in the application. At this point it seemed my theory was correct but to be sure I used the JVM option “-verbose:gc” to log GC activity in the staging environment. Sure enough, each time I used Excel export, the JVM would perform a full garbage collection.
After much research I finally discovered what was going on. According to the JavaDoc for the WorkbookSettings class:
* Flag to indicate whether the system hint garbage collection
* is enabled or not.
* As a rule of thumb, it is desirable to enable garbage collection
* when reading large spreadsheets from a batch process or from the
* command line, but better to deactivate the feature when reading
* large spreadsheets within a WAS, as the calls to System.gc() not
* only garbage collect the junk in JExcelApi, but also in the
* webservers JVM and can cause significant slowdown
* GC deactivated using -Djxl.nogc=true on the JVM command line
* Activated by default or by using -Djxl.nogc=false on the JVM command line
private boolean gcDisabled;”
So, JExcelApi by default will ask the JVM to perform a full garbage collection after certain operations. As the JavaDoc recommends, I added “-Djxl.nogc=true” to my JVM options and the delays went away. I have not seen this mentioned anywhere on JExcelApi’s website so If you face a similar problem with performance, try enabling this flag.
No related posts.