OutOfMemoryError when exporting (semi-)large unit test run report?
Let me preface by saying that I imagine the underlying issue here is my own, but I'm struggling with how to track it down. After unit tests of a reasonable quantity (~700+) are run in my plugin, if you try to export an HTML test run report it pretty quickly brings up the "You're running out of memory" dialog. If you tell it to continue, the following stack trace is written into the log:
2018-02-18 14:59:30,226 [1091630] ERROR - intellij.openapi.progress.Task - Java heap space
java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOfRange(Arrays.java:3664)
at java.lang.String.<init>(String.java:207)
at java.lang.String.substring(String.java:1933)
at com.sun.org.apache.xalan.internal.xsltc.runtime.BasisLibrary.substring_afterF(BasisLibrary.java:335)
at GregorSamsa.string$dash$replace$dash$all()
at GregorSamsa.string$dash$replace$dash$all()
... A TON of these!
Reports for smaller test runs export just fine. Any idea why this is happening? I've been trying to use YourKit to profile memory utilization starting with the export request, but everything is weighted so heavily toward character and byte arrays (as you'd expect with a textual report export) that it's difficult to see the root cause.
Any thoughts on things to check here?
Thanks in advance!
Scott
请先登录再写评论。
Hi Scott, if you can reproduce this reliably (sounds like you can), then set the -XX:+HeapDumpOnOutOfMemoryError flag on your process. Then when you get that exception, you'll get a heap dump of the memory state at that time. Then grab yourself a copy of the Eclipse Memory Analyzer (there's a standalone build) and point it at the heap dump. There's a brief tutorial on how to use it here: http://2min2code.com/articles/eclipse_memory_analyzer/, and there are many more out there. I believe YourKit contains some similar functionality but I don't know if it's as comprehensive as MAT, which is really an excellent tool.
Thanks, Colin. I do have access to both hprof and YourKit format heap dumps from when the problem occurs and have been analyzing both. YourKit has very nice tools for analyzing memory snapshots/heap dumps so that's what I've been using.
The largest object is of type com.intellij.execution.testframework.sm.runner.SMTestProxy$SMRootTestProxy and has a retained size of ~38MB. That's not a huge size, but given that the problem is related to test report generation and that the largest object is from the testframework package, there's probably something there. Nothing else on the dominators list looks like it would be related to test report generation.
I'll keep digging in...perhaps just add some logging at all of the entry points of the objects that my unit test EPs produce that might be queried as the report is being generated to see if that sheds any light on the issue.
Thanks again!
Scott
I have some additional insights. I'm able to export the report in XML format (~18MB XML file) with no problem. When I try to export the same report in HTML format, memory jumps from ~220MB to the maximum heap size (currently 2GB) very quickly, and the memory profiler is showing virtually all of that increase in the form of char[] data. It looks like the OOME is a result of the standard XSLT to produce the HTML file.
Each of my test method executions does write the full "console" output into the test console view, but that information is all included in the XML file above so it's no more than 18MB total (likely way less given the verbose nature of the surrounding XML document).
I might play with a custom XSL template for the HTML transform and see if I can determine why it's taking ~2GB of memory to render an 18MB XML file as HTML.
I've confirmed that it's an issue as the default XSLT is applied. I grabbed the default intellij-export.xsl and created a slightly modified version of it that only includes the output for tests that didn't pass (status of failed, error, ignored, or skipped) and the report exports just fine (assuming that in general you only have a small number of tests that don't pass).
Basically I modified the first line of the following:
to be:
I then restored the original condition and removed the string-replace-all so that the raw output is rendered into the test report (albeit without changing newlines to line breaks), and it also completed very quickly and without issues.
So the OOME is definitely happening as a result of the XSLT performing string-replace-all on every output in the test run. It looks like that was sourced from here:
http://geekswithblogs.net/Erik/archive/2008/04/01/120915.aspx
as an XSLT 1.0 stand-in for 2.0's fn:replace(). However, given its inefficiency on a data set of any real size, that seems like an IDE issue to me. Should I log a bug?
Regards,
Scott
FYI, I just logged this bug for the underlying issue:
https://youtrack.jetbrains.com/issue/IDEA-187039