Daniel Fagerstrom schrieb:
First The BlockServlets are configured to be singletons and created by the Spring container. A BlockServlet in turn creates a SitemapServlet (or whatever servlet type it is configured to create) in its init method. The Sitemap servlet creates a TreeProcessor in its init method. The DispatcherServlet connects to the BlockServlets in its init method. All components mentioned this far should AFAICS be singletons. Does this hold in practice in your tests?
Yes. The exact numbers are (we have n=7 blocks), after 2 reloads of a static page with a number of resources (images, css):
- 1 DispatcherServlet - 7 BlockServlet - 7 SitemapServlet - 8 (n+1) TreeProcessor - 8 (n+1) EnvironmentHelper - 24 BlockCallHttpServletRequest - 27 ReadNode - 67 ResourceReader - 69 HttpEnvironment - 69 HttpRequest - 69 HttpResponse - 69 NonCachingProcessingPipeline
The TreeProcessor is a singleton but it gets and populates a new pipeline at each request, so new ResourceReaders are supposed to be created. Sources are factory based so a new BlockSource is created and it creates in turn a new BlockConnection. All this is expected, so the question is if they are destroyed correctly.
It very much looks like they are not destroyed.
Looking at the ResourceReader it is Recyclable and has a default max-pool-size of 32. I don't know enough about the Avalon life style implementation to know exactly what happens. As long as there are no more than 32 instances of ResourceReaders they are, AFAIU supposed to be kept in a pool in the memory. But we would get a problem if the recycle method of the ResourceReader is called to late or not at all in that case the other object would be kept in memory when they should have been garbage collected.
There is no config, so the default value of 32 is used, but as listed above, there are actually more than 32 instances.
The BlockCallStack contains stacks in thread local memory that contain BlockContexts. AFAICS they don't have any references to things that should be garbage collected, but if they do we would have a problem.
I will look into that now.
Looking in the RequestProcessor that is called by the standard SitemapServlet, it calls ProcessingUtil.cleanup(), while leaving the processing of a request. I don't call that in the corresponding code in the o.a.c.sitemap.SitemapServlet, that might be a problem.
I have copied that, because I tried to figure out what is different between the sitemap.SitemapServlet and the RequestProcessor, but it did not change anything with the memory leak.
Also it would be worthwhile to scrutinize that all streams are closed as they should.
I also implemented the flush() and close() methods in BlockCallHttpServletResponse.getOutputStream() (in that anonymous inner class), passing on the stuff to the internal stream, where flush() quite often gets called, but close() not. Then I saw that there "should" be a close() call in BlockConnection.getInputStream() and I added a "os.close();" right before "return new ByteArrayInputStream(out);".
Alex -- Alexander Klimetschek http://www.mindquarry.com