BalusC opened a new pull request, #995: URL: https://github.com/apache/myfaces/pull/995
[MYFACES-4751](https://issues.apache.org/jira/browse/MYFACES-4751) ### Problem A Facelets view rendered without writing its state — e.g. a page with **no `UIForm`**, so no view state token is emitted — can never be restored. Unlike a saved view, it is never registered in the session `SerializedViewCollection`, so the usual *evict view → destroy its view scope* cleanup never runs for it. Any `@ViewScoped` beans created while rendering it therefore linger in the session (`ViewScopeContextualStorageHolder`) until the session expires; no `@PreDestroy` is ever invoked. Requesting such a page repeatedly within one session leaks **one view-scope storage per request, unbounded**. With a moderately large view (e.g. a ~200-row table bound to a `@ViewScoped` bean) refreshed a few hundred times, this exhausts the heap (`OutOfMemoryError`). A heap dump shows ~N retained `ContextualStorage` / `@ViewScoped` bean instances (one per request) where only `NUMBER_OF_VIEWS_IN_SESSION` should be retained. ### Root cause Server-side `@ViewScoped` cleanup is driven solely by `SerializedViewCollection` eviction (`ViewScopeContext.destroyAll(viewScopeId)`). A view whose state is never written is never put into that collection (`StateCacheServerSide`'s save path isn't reached), so its lazily-created view-scope storage is orphaned. In `FaceletViewDeclarationLanguage.renderView` this is the *"GET case without any form that triggers state saving"* branch, which never destroyed the view scope. ### Fix In that branch, destroy the view scope of a non-transient view whose state was not written, at the end of the request, via `viewMap.clear()` — which publishes `PreDestroyViewMapEvent` and destroys the (CDI or non-CDI) `@ViewScoped` storage. ### Test `ViewScopeFormlessLeakTestCase` renders a formless page bound to a `@ViewScoped` bean N times in one session and asserts the bean is destroyed N times (its `@PreDestroy` runs) instead of accumulating. It fails without the fix (`expected: <5> but was: <0>`) and passes with it. ### Notes Reproduced and verified on a real deployment (TomEE WebProfile, server-side state saving): before the fix the formless GET-refresh OOM-thrashed; after, the retained `@ViewScoped` storage stays bounded and the workload completes in a 512 MB heap. The issue is not 4.1.x-specific — the 4.0.x line exhibits it too — so it may warrant forward-porting to `main`. -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
