You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by bu...@apache.org on 2003/06/13 20:34:10 UTC
DO NOT REPLY [Bug 20758] New: -
Memory Leak in Classloader/Manager deploy/undeploy
DO NOT REPLY TO THIS EMAIL, BUT PLEASE POST YOUR BUG
RELATED COMMENTS THROUGH THE WEB INTERFACE AVAILABLE AT
<http://nagoya.apache.org/bugzilla/show_bug.cgi?id=20758>.
ANY REPLY MADE TO THIS MESSAGE WILL NOT BE COLLECTED AND
INSERTED IN THE BUG DATABASE.
http://nagoya.apache.org/bugzilla/show_bug.cgi?id=20758
Memory Leak in Classloader/Manager deploy/undeploy
Summary: Memory Leak in Classloader/Manager deploy/undeploy
Product: Tomcat 4
Version: 4.1.24
Platform: Other
OS/Version: Other
Status: NEW
Severity: Normal
Priority: Other
Component: Webapps:Manager
AssignedTo: tomcat-dev@jakarta.apache.org
ReportedBy: sys@yahoo.com
I have found that after deploying and removing my application using tomcat
manager a few times, the JVM throws an out of memory exception.
I found the following posts on tomcat-dev/tomcat-user mail archives but did
not see any bugzilla entries for it.
POST 1
http://www.mail-archive.com/tomcat-dev@jakarta.apache.org/msg42351.html
--------------------------------------------------------------------------
I believe I am seeing a memory leak that occurs when deploying or
more precisely undeploying a web application through the Tomcat manager.
I've done some analysis using a stripped down web application, JProbe,
and code inspection. I would not presume to know the Tomcat source nor
have done a complete and thorough analysis, but I would like to share
my observations and more importantly, solicit feedback from the Tomcat
user/development community.
Environment:
RedHat 8.0, JDK 1.4.1, Tomcat 4.1.21 Beta
Synopsis of problem:
We are deploying and undeploying our web applications through
the Tomcat Manager. In the case of one of our web applications,
redeploying 3-4 times resulted in an OutOfMemoryError in
Tomcat's JVM. Initially, we thought this was due to several daemon
Threads that were not Servlet lifecycle aware. But even after
fixing these, we were still running out of memory.
Suspecting that our classes were not being garbage collected
(note the distinction between object garbage collection and
class garbage collection) and might be pinned by classes that
exist higher in the ClassLoader hierarchy (in common, shared,
or possibly even server), I decided to try profiling using JProbe
(http://java.quest.com/jprobe/jprobe.shtml) and a VERY simple
web application. This web application is composed of a single
Servlet that does nothing but allocate a 1,000,000 element
byte array in its init() method and nulls it in its destroy() method.
I deployed and undeployed several times running under JProbe's
memory debugger and did observe a small memory leak of org.apache.*
classes/instances.
Analysis:
These are the org.apache instances that do not appear to be garbage
collected after a deploy/undeploy cycle:
Class Count
---------------------------------------------------------------------
org.apache.catalina.LifecycleListener[] 4
org.apache.catalina.Valve[] 1
org.apache.catalina.core.ApplicationContext 1
org.apache.catalina.core.ApplicationContextFacade 1
org.apache.catalina.core.NamingContextListener 1
org.apache.catalina.core.StandardContext 1
org.apache.catalina.core.StandardContextMapper 1
org.apache.catalina.core.StandardContextValve 1
org.apache.catalina.core.StandardPipeline 1
org.apache.catalina.deploy.ApplicationParameter[] 1
org.apache.catalina.deploy.NamingResources 1
org.apache.catalina.deploy.SecurityConstraint[] 1
org.apache.catalina.deploy.FilterMap[] 1
org.apache.catalina.loader.WebappClassLoader 1
org.apache.catalina.loader.WebappLoader 1
org.apache.catalina.session.StandardManager 1
org.apache.catalina.startup.ContextConfig 1
org.apache.catalina.util.LifecycleSupport 4
org.apache.commons.collections.LRUMap 1
org.apache.commons.collections.SequencedHashMap$Entry 6
org.apache.naming.NameParserImpl 2
org.apache.naming.NamingContext 3
org.apache.naming.NamingEntry 4
org.apache.naming.TransactionRef 1
org.apache.naming.resources.ImmutableNameNotFoundException 1
org.apache.naming.resources.ProxyDirContext 1
org.apache.naming.resources.ProxyDirContext$CacheEntry 5
org.apache.naming.resources.ResourceAttributes 3
org.apache.naming.resources.WARDirContext 2
org.apache.naming.resources.WARDirContext$WARResource 2
org.apache.naming.resources.WARDirContext$Entry 2
org.apache.naming.resources.WARDirContext$Entry[] 2
Initially, I focused on the org.apache.catalina.core.StandardContext
class. It seemed like a nice entry point that scopes the Catalina
classes supporting a web application deployment.
It appears that an instance is pinned in several locations:
1. org.apache.naming.ContextBindings.bindContext() is called (in
org.apache.catalina.core.NamingContextListener.lifecycleEvent()
given a org.apache.catalina.LifeCycleEvent whose getType() is
org.apache.catalina.Lifecycle.START_EVENT). This puts
StandardContext into a static Hashtable within ContextBindings.
This Hashtable entry is removed by a call to
ContextBindings.unbindContext(). unbindContext() appears to never
be called.
2. An org.apache.jasper.logging.DefaultLogger instance (which
implements org.apache.jasper.logging.Logger) is created in
org.apache.jasper.servlet.JspServlet.init(). DefaultLogger's
setName() method is called resulting in the Logger being placed
into a static Hashtable. Entries in this Hashtable are removed
via the Logger.close() and Logger.removeLogger(...) methods,
neither of which appear to be called.
DefaultLogger refers to a StandardContext via the following chain
of references:
DefaultLogger -> org.apache.catalina.core.ApplicationContextFacade
-> org.apache.catalina.core.ApplicationContext -> StandardContext
3. The DefaultLogger created in JspServlet is also referenced
by a static field in org.apache.jasper.Constants. This field
does not appear to be cleared.
4. org.apache.catalina.core.StandardHostDeployer has a
static org.apache.catalina.Context field that is set to the
suspect StandardContext after a call to its addChild() method
(called reflectively by org.apache.commons.digester.Digester).
This field is not unset unless the install(URL, URL) method
is called (but not if the other install(String, URL) or either
of the remove() methods is called).
This context can be replaced if addChild() is called again,
but would this happen only if another deployment occured?
If so, StandardContext is still "pinned" if the web application
is left undeployed and a subsequent deployment does not occur.
5. One of the Digester instances can also periodically hold onto
a StandardContext (as its root), but this reference can and
appears to be replaced (via Digester.push() when its stack
is empty). I have not analyzed when this may be the case,
so similar to (4), it seems possible that StandardContext
will be pinned if the Digester instance is not "reset."
At this point in time, I have not analyzed memory leaks beyond
references to StandardContext. Many other "leaky" instances
can be traced back to StandardContext (ApplicationContext,
ApplicationContextFacade, StandardManager, etc...).
The number of instances that appear to be leaking and the size
of these instances is fairly small. I would guess less than 2K.
However, I suspect that these instances are pinnning classes.
By instrumenting our code, I have been able to determine that
instances are indeed being garbage collected. On the other hand,
I have been able to instrument WebappClassLoader and have not seen
it finalize. In our production environment, we are deploying a
Jetspeed portal which contains hundreds of classes which I believe
can explain the limited number of times we are able to re-deploy
before running out of memory.
If you've managed to read through to here, any ideas or pointers
would be greatly appreciated.
Thanks,
Ted Chen
POST 2:
http://www.mail-archive.com/tomcat-user@jakarta.apache.org/msg90811.html
----------------------------------------------------------------------
I believe I'm seeing a memory leak as a result of a Manager
deploy/undeploy. I have a very simple test case: a Servlet that has a
static field that refers to an object (Foo) that allocates a large chunk
of memory. I've instrumented both the Servlet (init(), destroy(), and
finalize()) and Foo (ctor and finalize()). The Servlet has been
configured to load on startup.
On a deploy, I see:
Foo.ctor (during class initialization of the Servlet)
Servlet.init():
On an undeploy, I see:
Servlet.destroy()
Servlet.finalize()
I never see Foo.finalize(). If I continue to deploy/undeploy
repeatedly, eventually the VM reports an OutOfMemoryError when I try to
deploy. Running the VM with -verbose:gc and "encouraging" GC whenever
possible, I see that after each undeploy, memory usage goes up roughly
by what I've allocated in Foo.
Any ideas?
---------------------------------------------------------------------
To unsubscribe, e-mail: tomcat-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: tomcat-dev-help@jakarta.apache.org