This is by design. It will only be destoyed immediately when a POST action results in a navigation which isn't a postback to the same view (i.e. the action method didn't return null
or void
, but a fullworthy String
, even when just empty).
The <h:link>
generates a GET link which doesn't invoke any POST action. It's with currently available technology not reliably possible to notify the server side when the view is unloaded, so JSF can't be notified to destroy the view scoped bean associated with the view. In such case, the view scoped bean will then only be destroyed when the session expires or when the max logical views in session has exceeded (the default is 15) and the associated view is the first in order.
If you really want to destoy the view scoped bean by a navigaiton action, then your best bet is to make it a POST request by <h:commandLink>
instead and issue a redirect by returning the navigation outcome with ?faces-redirect=true
parameter. But this is after all not SEO friendly as bots won't index POST links.
In theory it would be possible by HTML DOM onbeforeunload
event, but this is currently still a non-standard event and the browser behaviour is unspecified as to what happens when you send an ajax request during that event. It will sometimes arrive, but sometimes also not. JSF as a spec can't depend on non-standard specifications and hence it's never implemented from JSF side on. But it should be possible with a 3rd party JSF library which doesn't care about non-standard specifications.
Update: in practice, this has been implemented in OmniFaces @ViewScoped
since OmniFaces 2.2. Initially with help of synchronous XHR and since OmniFaces 2.6 with help of beacon (with synchronous XHR as fallback). It's working quite well in major browsers. Since OmniFaces 2.3 it even immediately destroys the associated JSF server side view state, and since OmniFaces 2.6 it also immediately destroys the physical beans, hereby further reducing unnecessary memory usage. See also among others JSF: Mojarra vs. OmniFaces @ViewScoped: @PreDestroy called but bean can't be garbage collected