jsfjakarta-eerichfacesajax4jsfstate-saving

How to force JSF clean its State Manager?


More specifically, this is AjaxStateManager in Richfaces 3.1.HomeMadePatch/JSF1.1. I am not responsible for these technological choices, and I already read related questions like 1, 2, 3 which basically advice to upgrade versions or switch for a stateless JSF, which I would be very glad to do if I could, but due to the big hugeness of the application I am afraid I can't do this.

Back to the topic, I am currently trying to optimize memory heap consumption. Since my predecessors set a session scope for almost every bean, and set a server timeout of 6 hours (with some javascript refreshing before it expires in the browser), users have very long sessions and keep accumulating memory. I made some optimization on my own (well it is kind of simulating conversation scope: when hitting some pages, I clean everything in the main beans). Now when the users come back to main page their session wheights around 300kb, broken down as follow:

Those results were obtained by making heapdump and analyzing memory with Eclipse MAT. As you can see, 93% of the memory is Ajax maintening useless states. I want to force it to release those useless states. Any Idea on how to do this?

I thought about limiting the number of views it can save but in some pages there are some very weird form imbrications that I am unsure it would not break if I did.

Thanks in advance..


Solution

  • Ok, so just in case, here is a relevant link about the problem, saying basically that there is no fix because it is inherent to the mechanisms of JSF. I resorted to using the parameter step on the number of views, which seems the more clean.

    But I also experimented some dirty fixing. I am posting it there for future reference. Use at your own risk, because there is no guarantee.

    package com.mycompany.ajaxutils;
    
    import javax.faces.application.StateManager;
    import javax.faces.context.FacesContext;
    import javax.servlet.http.HttpSession;
    
    import org.ajax4jsf.application.AjaxStateManager;
    import org.ajax4jsf.util.LRUMap;
    
    public class AjaxStateHelper {
        private static final AjaxSateManagerUncover asmu = new AjaxSateManagerUncover(null);
    
        public static void cleanAjaxStateManager() {
            HttpSession currentSession = (HttpSession) FacesContext.getCurrentInstance().getExternalContext()
                    .getSession(false);
            LRUMap lrumap = asmu.getLRUMap(currentSession.getAttribute(AjaxStateManager.class.getName()
                    + ".VIEW_STATES_MAP"));
            lrumap.clear();
        }
    
        /**
         * This inner class is needed because we need to make a cast to a SynchronizedStateHolder
         * which is an inner protected class inside AjaxStateManager
         */
        protected static class AjaxSateManagerUncover extends AjaxStateManager {
    
            public AjaxSateManagerUncover(StateManager parent) {
                super(parent);
            }
    
            public LRUMap getLRUMap(Object stateMap) {
                SynchronizedStateHolder s = (SynchronizedStateHolder) stateMap;
                return s.getStateMap();
            }
        }
    }
    

    Just call AjaxStateHelper.cleanAjaxStateManager(); from wherever you need it in your application and it will free the memory for you. But beware the potential side effects, this is experimental code