Details
Description
An OutOfMemoryError within GzipFilter. It happens randomly on a production system, but I can't reproduce it in my development environment.
java.lang.OutOfMemoryError at java.util.zip.Deflater.init(Native Method) at java.util.zip.Deflater.<init>(Deflater.java:121) at java.util.zip.GZIPOutputStream.<init>(GZIPOutputStream.java:46) at org.mortbay.servlet.GzipFilter$GzipStream.doGzip(GzipFilter.java:525) at org.mortbay.servlet.GzipFilter$GzipStream.<init>(GzipFilter.java:417) at org.mortbay.servlet.GzipFilter$GZIPResponseWrapper.newGzipStream(GzipFilter.java:392) at org.mortbay.servlet.GzipFilter$GZIPResponseWrapper.getOutputStream(GzipFilter.java:342)
My diagnosis:
This is actually caused by the C heap running out of memory.
The gzip filter creates GZipOutputStream objects, but doesn't call close() as that would close the underlying socket stream. GZipOutputStream calls out to C code which uses the C heap. If GZipOutputStream objects aren't cleaned up by GC fast enough (so finalized can be called), it's possible that the C heap can run out of memory.
This is a known issue in the JVM:
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4797189
http://markmail.org/message/zflsen5q2uchpas3
A possible solution:
- Extend GZipOutputStream to free up C memory when finish() is called.
- Modify doGZip() to instantiate the extended class instead of GZipOutputStream
class GZOutput extends GZipOutputStream { GZOutput(OutputStream out, int size) throws IOException { super(out, size); } public void finish() { super.finish(); def.end(); //protected java.util.zip.Deflater } }
We did something similar to the above, but we did it in a more complicated way to avoid having to modify Jetty source.
This doesn't entirely fix the issue; finish() is never called when an exception is caught in doFilter() and isCommitted() is false. Perhaps wrappedResponse.finish() should be called in the catch, but I'm not sure.
This is a very old and well known issue. Please fix it.