Skip to content

WW-5537 fix(core): resolve classloader/memory leaks during Tomcat hot deployment#1631

Open
lukaszlenart wants to merge 3 commits intorelease/struts-6-8-xfrom
fix/WW-5537-memory-leak-cleanup
Open

WW-5537 fix(core): resolve classloader/memory leaks during Tomcat hot deployment#1631
lukaszlenart wants to merge 3 commits intorelease/struts-6-8-xfrom
fix/WW-5537-memory-leak-cleanup

Conversation

@lukaszlenart
Copy link
Member

@lukaszlenart lukaszlenart commented Mar 23, 2026

Summary

  • Fixes classloader/memory leaks that cause OutOfMemoryError (Metaspace) during Tomcat hot redeployment (WW-5537)
  • Introduces InternalDestroyable interface with container-based discovery so static caches, daemon threads, and shared references are cleaned up when Dispatcher.cleanup() runs
  • Clears OGNL, Component, ScopeInterceptor, DefaultFileManager, FreeMarker, and JSON plugin caches; stops the FinalizableReferenceQueue daemon thread; replaces ContainerHolder ThreadLocal with volatile

Changes

Area What
Core InternalDestroyable / ContextAwareDestroyable interfaces
Core ComponentCacheDestroyable, OgnlCacheDestroyable, ScopeInterceptorCacheDestroyable, FinalizableReferenceQueueDestroyable, FreemarkerCacheDestroyable
Core ContainerHolder ThreadLocal → volatile
Core Dispatcher.cleanup() refactored into focused destroy methods
JSON plugin JSONCacheDestroyable registered in struts-plugin.xml
Tests DispatcherCleanupLeakTest (8 tests), FinalizableReferenceQueueTest

Test plan

  • DispatcherCleanupLeakTest — verifies each static cache is cleared after dispatcher.cleanup()
  • FinalizableReferenceQueueTest — verifies daemon thread stops and singleton is nulled
  • Manual verification: deploy HelloWorld WAR on Tomcat 9, redeploy 5+ times, confirm no Metaspace growth via jmap/MAT
  • Run full CI suite

🤖 Generated with Claude Code

lukaszlenart and others added 3 commits March 23, 2026 07:25
… deployment

Introduce InternalDestroyable interface with container-based discovery to
clean up static caches, daemon threads, and shared references that pin the
webapp classloader after undeploy. This prevents OutOfMemoryError (Metaspace)
on repeated hot deployments.

Changes:
- Add InternalDestroyable/ContextAwareDestroyable interfaces for cleanup hooks
- Clear OGNL, Component, ScopeInterceptor, DefaultFileManager static caches
- Stop FinalizableReferenceQueue daemon thread and null its classloader
- Clear FreeMarker template/introspection caches from ServletContext
- Replace ContainerHolder ThreadLocal with volatile to prevent thread-pool leaks
- Clear static dispatcherListeners list on Dispatcher cleanup
- Add JSONCacheDestroyable for json plugin cache cleanup
- Register all destroyables via struts-beans.xml / struts-plugin.xml

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… Servlet container

Without log4j-web, Log4j2 SoftReferences delay classloader GC after undeploy.
The log4j-web module provides Log4jServletContextListener which ensures proper
Log4j2 shutdown during ServletContext destruction.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…erHolder

Replace the volatile shared reference with a ThreadLocal backed by a volatile
generation counter. Per-request clear() only affects the current thread (safe
for concurrent requests and tests). On undeploy, invalidateAll() advances the
generation counter so idle pool threads detect staleness on next access and
self-clear, preventing classloader leaks without breaking test isolation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@sonarqubecloud
Copy link

Quality Gate Failed Quality Gate failed

Failed conditions
25 Security Hotspots
43.3% Coverage on New Code (required ≥ 80%)
3.3% Duplication on New Code (required ≤ 3%)
E Reliability Rating on New Code (required ≥ A)
E Security Rating on New Code (required ≥ A)

See analysis details on SonarQube Cloud

Catch issues before they fail your Quality Gate with our IDE extension SonarQube for IDE

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant