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]

Reply via email to