Ask Questions and Find Answers
Important:
Ask is now read-only. You can review any existing questions and answers, but not add anything new.
But - don't panic! While ask is no more, we've replaced it with discuss - the new Liferay Discussion Forum! Read more here here or just visit the site here:
discuss.liferay.com
RE: Primefaces JSF VeiwScoped beans cleanup in session
So I think this probably a standard issue with JSF VeiwScoped beans that they never seem to get garbage collected out of the session because its never really know when the when a page changes. I was wondering if anyone had any good ideas about this issue.
Hi Bill,A bean annotated with @ViewScoped will be made available for garbage collection with a navigation-rule or an implicit navigation takes place from one view to another. So for example, if you submit a form and then navigate to a different view that doesn't utilize any @ViewScoped beans then that's best for freeing-up memory. Also, we have a session listener in the bridge that will instruct Mojarra to cleanup managed beans and make them available for garbage collection when the portlet session expires.Neil
Yeah I'm not really using JSF navigation in this app. Each JSF portlet is just in its own liferay page. I was thinking of doing something progammatically like: FacesContext.getCurrentInstance().getViewRoot().getViewMap().remove("yourBean");
Hi Bill,
Just curious, when do you think would be a good time to try to cleanup the @ViewScoped beans programmatically? When a "Submit" button is clicked? Regardless, you might want to put a System.out.println inside of the bean's constructor to see if it gets re-created due to an EL expression getting evaluated.
Neil
Just curious, when do you think would be a good time to try to cleanup the @ViewScoped beans programmatically? When a "Submit" button is clicked? Regardless, you might want to put a System.out.println inside of the bean's constructor to see if it gets re-created due to an EL expression getting evaluated.
Neil
Trying FacesContext.getCurrentInstance().getViewRoot().getViewMap() only returns one entry in the map called headManagedBean. Is that a liferay thing? I thought I'd fine all the ViewScoped beans I currently had in my session.
Well I thought I could do it in the PostConstruct method of one of my view to remove others that I redirect to form it that were giving me some memory issues.
Maybe I should be more clear about what I'm doing. I'm going from one ViewScoped bean to another by doing the following in the bean: public void reviewDocSet() {
String packageName = selectedTask.getPackageName();
String batchGuid = selectedTask.getBatchGuid();
ExternalContext context = FacesContext.getCurrentInstance().getExternalContext();
ThemeDisplay themeDisplay = (ThemeDisplay) context.getRequestMap().get(WebKeys.THEME_DISPLAY);
Layout layout = (Layout) context.getRequestMap().get(WebKeys.LAYOUT); try {
String displayURL = layout.getGroup().getDisplayURL(themeDisplay);
context.redirect(displayURL + DOC_SET_REVIEW_PAGE + BATCHGUID_PARAMETER + batchGuid + PACKAGENAME_PARAMETER
+ packageName);
} catch (Exception e) {
LOGGER.error(e.getLocalizedMessage());
}
}
This could be what I'm doing wrong.
String packageName = selectedTask.getPackageName();
String batchGuid = selectedTask.getBatchGuid();
ExternalContext context = FacesContext.getCurrentInstance().getExternalContext();
ThemeDisplay themeDisplay = (ThemeDisplay) context.getRequestMap().get(WebKeys.THEME_DISPLAY);
Layout layout = (Layout) context.getRequestMap().get(WebKeys.LAYOUT); try {
String displayURL = layout.getGroup().getDisplayURL(themeDisplay);
context.redirect(displayURL + DOC_SET_REVIEW_PAGE + BATCHGUID_PARAMETER + batchGuid + PACKAGENAME_PARAMETER
+ packageName);
} catch (Exception e) {
LOGGER.error(e.getLocalizedMessage());
}
}
This could be what I'm doing wrong.
Also I know the PostConstruct method in the called bean is always called because I pass in parameters in my redirect to display different data each time its called
Hi Bill,
Regarding the headManagedBean, yes that's an implementation detail of Liferay Faces Bridge -- it needs to keep track of what it added to the <head>...</head> section of the portal page.
Regarding the PostConstruct approach, by removing it from the viewscope map, I'd be concerned that it would get unnecessarily re-created each time an f:ajax partial-submit occurs. Also, it seems like you're getting closer and closer to @RequestScoped with that approach.
The heart of the problem is that you are redirecting from a portal page with a JSF portlet on it to a different portal page that doesn't have a JSF portlet from the same webapp context. That's why Mojarra loses the ability to make the bean available for garbage collection. Instead, my recommendation would be, that when the Submit button is clicked, to execute a JSF action with a navigation-rule that has ?faces-redirect=true to a view that does not have any @ViewScoped beans associated with it. Then, that page could then execute some JavaScript that does document.location="..."; in order to do a client-initiated redirect to the non-JSF portal page.
Neil
Regarding the headManagedBean, yes that's an implementation detail of Liferay Faces Bridge -- it needs to keep track of what it added to the <head>...</head> section of the portal page.
Regarding the PostConstruct approach, by removing it from the viewscope map, I'd be concerned that it would get unnecessarily re-created each time an f:ajax partial-submit occurs. Also, it seems like you're getting closer and closer to @RequestScoped with that approach.
The heart of the problem is that you are redirecting from a portal page with a JSF portlet on it to a different portal page that doesn't have a JSF portlet from the same webapp context. That's why Mojarra loses the ability to make the bean available for garbage collection. Instead, my recommendation would be, that when the Submit button is clicked, to execute a JSF action with a navigation-rule that has ?faces-redirect=true to a view that does not have any @ViewScoped beans associated with it. Then, that page could then execute some JavaScript that does document.location="..."; in order to do a client-initiated redirect to the non-JSF portal page.
Neil
I am going from a JSF Veiwscope page to another JSF ViewScope page, not and non-JSF portal page.
As far as I know, Mojarra will only cause the @ViewScoped bean to be made available for garbage collection if you execute a navigation-rule to a different view that doesn't have any @ViewScoped beans. Aside from that they will get cleaned up on session expiration.
Under the hood, Liferay Faces Bridge keeps managed beans in PortletSession.PORTLET_SCOPE. And as I recall, Liferay Portal makes a distinction beyond that with "instanceable" portlets, so that each portlet instance has its own PortletSession.PORTLET_SCOPE. So even if you were to navigate from PageA to PageB, and each page had the same JSF portlet on them, they would be different portlet instances and Mojarra wouldn't be able to cleanup the @ViewScoped beans.
Under the hood, Liferay Faces Bridge keeps managed beans in PortletSession.PORTLET_SCOPE. And as I recall, Liferay Portal makes a distinction beyond that with "instanceable" portlets, so that each portlet instance has its own PortletSession.PORTLET_SCOPE. So even if you were to navigate from PageA to PageB, and each page had the same JSF portlet on them, they would be different portlet instances and Mojarra wouldn't be able to cleanup the @ViewScoped beans.
It sounds like you are saying that we can't use view scope in Liferay? Everytime I hit a page I'll get a new Viewscoped bean that can't be garbage collected? Man I've really misunderstood this whole thing. What about using something like Omnifaces? Would that help at all?
Hi Bill,I must have not communicated myself well, because I didn't mean to give you the impression that @ViewScoped can't be used in Liferay. It most certainly can be used in Liferay. We've actually done a lot of testing in this regard, making sure that @ViewScoped beans are cleaned up properly. For example, we have a test portlet that checks for regressions with an old memory leak bug we fixed many years ago.When I mentioned the "instanceable" feature, I was trying to communicate that Liferay permits you to have two instances of the same portlet on the same portal page. Each portlet will have its own PortletSession.PortletScope which makes it possible for them to function on the same page without interfering with one another.Beans annotated with @ViewScoped are *always* able to be garbage collected. They are made available for garbage collection after a navigation-rule executes, or when a session expires. And there shouldn't be a new instance of a @ViewScoped bean every time you hit the page. Even if you reload the page, Mojarra should use the same @ViewScoped bean instance as the previous page load.OmniFaces should not be necessary. Regardless, OmniFaces was not designed with portlets in mind.Neil
Thanks Neil for all this information. We were looking at things yesterday jvisualvm and it looked like garbage collecting was working fine. We are going to do further analysis today as well. Thanks again, your expertise is always appreciated.
What about session and request scope? Would they have the same issues as view scope? Also I added the following settings in my web.xml:
<context-param>
<param-name>com.sun.faces.numberOfViewsInSession</param-name>
<param-value>3</param-value>
</context-param>
<context-param>
<param-name>com.sun.faces.numberOfLogicalViews</param-name>
<param-value>3</param-value>
</context-param>
Do these help at all?
<context-param>
<param-name>com.sun.faces.numberOfViewsInSession</param-name>
<param-value>3</param-value>
</context-param>
<context-param>
<param-name>com.sun.faces.numberOfLogicalViews</param-name>
<param-value>3</param-value>
</context-param>
Do these help at all?
What about when I'm switching between pages using Liferay navigation.
Beans annotated with @SessionScoped will not be made available for garbage collection until the session expires. So @ViewScoped is generally better than @SessionScoped because of that. As a recommended practice, you should use @RequestScoped over @ViewScoped/@SessionScoped because it will generally keep more memory available on the server. However, beans annotated with @RequestScoped could cause CPU utilization to increase under heavy load because the bean instances will have to be constructed and destroyed for each request.
Yes but with session scope I shouldn't get a new bean every time I go into the page like I'm seeing with view scope?
When you wrote:
Are you seeing a new @ViewScoped bean instance every time you reload in the same browser tab? Or Is the new bean instance happening for each new browser tab?
Regarding @SessionScoped, you are correct it should only get created once for each corresponding PortletSession.PORTLET_SCOPE.
"get a new bean every time I go into the page"
Are you seeing a new @ViewScoped bean instance every time you reload in the same browser tab? Or Is the new bean instance happening for each new browser tab?
Regarding @SessionScoped, you are correct it should only get created once for each corresponding PortletSession.PORTLET_SCOPE.
In general, config params like com.sun.faces.numberOfViewsInSession and com.sun.faces.numberOfLogicalViews should work the same in webapp and portlet environments.
My pleasure Bill. Glad to know that garbage collection is working fine.
In the end I had to back off the use of ViewScoped managed beans and move to mainly using SessionScoped and in some cases RequestScoped. I was getting way to many ViewScoped beans being created during a session that were not getting garbage collected until the session expired. It seem like everytime I went into a lifery page the had ViewScoped bean on I got a new bean added to my session. Switching over to SessionScoped only gives me one bean in the session regardless of how times I go into the page that its deployed on. Also I was still able to reinitialize the new SessionScoped beans by adding the following tag to the their corresponding facelet::
<f:metadata>
<f:viewAction action="#{packagesView.onload}" />
</f:metadata>
The initialization code I had been executing in the @PostConstruct method is now executed in the onload method shown and called above in my backing bean:
@PostConstruct
public void init() {
}
public void onload() {
loadAllPackages();
initTogglerList();
}
A similar change was made to all my bean classes the I moved from ViewScoped to SessionScoped.
So it seems that using ViewScoped beans with Liferay navigation is just not viable. Would it be possible for the bridge to know that a Liferay navigation was performed which moved off a page that had a Viewscoped bean and then mark that bean for garbage collection?
<f:metadata>
<f:viewAction action="#{packagesView.onload}" />
</f:metadata>
The initialization code I had been executing in the @PostConstruct method is now executed in the onload method shown and called above in my backing bean:
@PostConstruct
public void init() {
}
public void onload() {
loadAllPackages();
initTogglerList();
}
A similar change was made to all my bean classes the I moved from ViewScoped to SessionScoped.
So it seems that using ViewScoped beans with Liferay navigation is just not viable. Would it be possible for the bridge to know that a Liferay navigation was performed which moved off a page that had a Viewscoped bean and then mark that bean for garbage collection?
Hi Bill,
Thanks for posting your solution.
The only way for the bridge to determine that the user has clicked on a link with a URL to a different Liferay Portal page would be if the link were generated by the JSF portlet, which of course relies on the the bridge's ExternalContext implementation to encode portlet URLs. You would need to display these links instead of the links generated by the Liferay Portal theme and/or nagivation portlet. That way, when the user clicks on a link, Mojarra would be made aware that the user has navigated away from the view and make any associated @ViewScoped beans available for garbage collection prior to redirecting to the desired Liferay Portal page URL.
Neil
Thanks for posting your solution.
The only way for the bridge to determine that the user has clicked on a link with a URL to a different Liferay Portal page would be if the link were generated by the JSF portlet, which of course relies on the the bridge's ExternalContext implementation to encode portlet URLs. You would need to display these links instead of the links generated by the Liferay Portal theme and/or nagivation portlet. That way, when the user clicks on a link, Mojarra would be made aware that the user has navigated away from the view and make any associated @ViewScoped beans available for garbage collection prior to redirecting to the desired Liferay Portal page URL.
Neil
But I really like using Liferay's navigation.
So we are in production now and supporting about 100 concurrent users daily performing thousands of transaction. We did need to lighten up some of our payloads that but that said even this load we are running between 2 and 3 Gb of heap at peak time. We have about 10 main portlets that provide the bulk of out data management needs to the end users. We're pretty happy with that.
Hi Bill,
Congratulations on going production! Glad to know that things are working well.
Did you end up going with any @ViewScoped beans?
Neil
Congratulations on going production! Glad to know that things are working well.
Did you end up going with any @ViewScoped beans?
Neil
No we are using session scope as described above which is working very well for us.
In a few cases we could use request scope on certain portlets that were more transient.
Copyright © 2025 Liferay, Inc
• Privacy Policy
Powered by Liferay™