ICEfaces
  1. ICEfaces
  2. ICE-3980

Improve ICEfaces state saving memory footprint

    Details

    • Type: Improvement Improvement
    • Status: Closed
    • Priority: Major Major
    • Resolution: Fixed
    • Affects Version/s: 1.8DR#2
    • Fix Version/s: 1.8RC1, 1.8
    • Component/s: Framework
    • Labels:
      None
    • Environment:
      ICEfaces+ state saving

      Description

      QA has reported that a server with 3 instances of component-showcase runs out of heap space (with 64M configured) with 3 clients running with state saving turned on. This doesn't happen with state saving turned off.

      There are a couple of implementation details that can account for this.

      One, JSF stores state in a two stage map structure with one Map (referred to as the actualMap in JSF source) containing the tree structure and state for a View, one entry per render, default capacity of 15. This is used in JSF's support of the back/forward key with 15 steps allowed before any given capture is released from the map. The other map (the logicalMap) holds the actual Maps for all of the Views supported for a client.

      ICEfaces doesn't really need either of these concepts. ICEfaces doesn't support the back button so essentially all the the duplicate copies in the actual map are wastage. Also the notion of having a Map that holds all the actual Maps for a view is also wastage and an unnecessary limitation on the number of views available to ICEfaces. On a page reload, a fresh entry is created in this outer logical map despite the viewId still being the same.

      Theoretically, it's easy to create a new StateManager implementation that better reflects the needs of ICEfaces. In practice, the StateManager is one of the underlying-technology-aware components available from the RenderKit API. We could:

      - Copy the StateManagerImpl code and create our own RenderKit implementations for ResponseStateManager, RequestStateManager, etc., and fully flesh out the RenderKit API. This is the 'right' thing to do, but has some time and testing risks.

      - Copy the StateManagerImpl code and replace any use of the RenderKit API. I think our implementation of the DOMResponseWriter is something like this, since it's supposed to be another technology aware part of the RenderKit idea. We can probably do this, with more work to be redone later.

      - We can't use the JSF implementations since they're in the RI, and including them in the compilation unit would break Myfaces, and would require rejigging the build environment.

      - Just turn it off. Use the existing state preservation in ICEfaces.

      - Clients who have special restrictions can minimize the impact of the JSF implementation using configuration parameters. You can dial down the number of logical maps if your application supports a restricted number of physical Views. Also, you can configure down the size of the ActualMap to 2 or 3. The client should be aware of a race condition if the size of the actualMap is set to 1. In this case, it's easy to have server push generate a new render (with subsequent entry in the state map) with UI coming from the client referring to the map entry that had generated the view. The key coming from the client will then correspond to no entry in the actual map, and a ViewExpired exception will be thrown. This minimizes the impact of the memory consumption.

      The other part of the problem is the inefficiency in the state saving itself. We create Object[] to accomodate the largest number of Effects, when most of the values are null. The API doesn't specify what the components are to put in the Object[], so it's possible to easily create a Map containing Effects objects only if they exist, keyed by a simple string identifying which is which. The amount of memory savings in this case is still to be determined.



        Activity

        Hide
        Deryk Sinotte added a comment -

        Mircea commented:

        So, this is my opinion:

        • I agree that we should keep around only the state saved after the last
          render since we don't support the back-button anyway (the way the
          default StateManager implementation is expecting to be supported)
        • Implementing our own StateManager makes more sense to me since that is
          the plug point that we are expected to touch and possibly improve. I see
          more risk introducing if statements all over the place to bend
          framework's behavior into what we want.
        • I believe we can have a StateManager implementation which saves the
          component tree state into a single List or even a ObjectStream. Each
          component state will have a header into the list (or stream) that will
          describe what entries are null, how many there are, the component id
          ...and so on. The headers will be used as markers, delimiting the state
          corresponding to each component. This will save a lot heap space in the
          end.
        Show
        Deryk Sinotte added a comment - Mircea commented: So, this is my opinion: I agree that we should keep around only the state saved after the last render since we don't support the back-button anyway (the way the default StateManager implementation is expecting to be supported) Implementing our own StateManager makes more sense to me since that is the plug point that we are expected to touch and possibly improve. I see more risk introducing if statements all over the place to bend framework's behavior into what we want. I believe we can have a StateManager implementation which saves the component tree state into a single List or even a ObjectStream. Each component state will have a header into the list (or stream) that will describe what entries are null, how many there are, the component id ...and so on. The headers will be used as markers, delimiting the state corresponding to each component. This will save a lot heap space in the end.
        Hide
        Greg Dick added a comment -

        Course of action is going to be our own StateManager implementation, but without the trappings of the rest of the RenderKit implementation for now.

        Using our own state manager, the notion of the old style state saving (leaving the viewRoot in the FacesContext) or keeping only one copy the existing JSF state object[] in the session or serializing the object[] to a compressed Gzip stream and putting that in the session are only different strategies.

        We're going to do more work to understand why turning on the transient property in the UIXHtml component increases the apparent memory footprint.

        Show
        Greg Dick added a comment - Course of action is going to be our own StateManager implementation, but without the trappings of the rest of the RenderKit implementation for now. Using our own state manager, the notion of the old style state saving (leaving the viewRoot in the FacesContext) or keeping only one copy the existing JSF state object[] in the session or serializing the object[] to a compressed Gzip stream and putting that in the session are only different strategies. We're going to do more work to understand why turning on the transient property in the UIXHtml component increases the apparent memory footprint.
        Hide
        Greg Dick added a comment -

        Object counts taken by YourKit after 3 users making a number of navigation requests in JSP component-showcase.

        Show
        Greg Dick added a comment - Object counts taken by YourKit after 3 users making a number of navigation requests in JSP component-showcase.
        Hide
        Greg Dick added a comment -

        I've implemented two ICEfaces state saving implementations.

        SingleCopyStateManagerImpl.java does the same algorithm that JSF uses, except it only saves one copy per View. This implementation should sidestep the incompatibilities between JSF state saving and server push and the problem with redirection causing items being pushed out of the logical state array. This operates identical to JSF state saving with the 'com.sun.faces.numberOfLogicalViews' parameter set to 1

        ViewRootStateManagerImpl.java preserves the UIViewRoot between requests. This was the approach taken in ICEfaces prior to the implementation of StateSaving and offers the fastest and most memory efficient way to save state, the downside is the fact that the UIComponent structure is not serializable and therefore, failover cannot be supported with this approach.

        Show
        Greg Dick added a comment - I've implemented two ICEfaces state saving implementations. SingleCopyStateManagerImpl.java does the same algorithm that JSF uses, except it only saves one copy per View. This implementation should sidestep the incompatibilities between JSF state saving and server push and the problem with redirection causing items being pushed out of the logical state array. This operates identical to JSF state saving with the 'com.sun.faces.numberOfLogicalViews' parameter set to 1 ViewRootStateManagerImpl.java preserves the UIViewRoot between requests. This was the approach taken in ICEfaces prior to the implementation of StateSaving and offers the fastest and most memory efficient way to save state, the downside is the fact that the UIComponent structure is not serializable and therefore, failover cannot be supported with this approach.
        Hide
        Greg Dick added a comment -

        The JSF state saving with 15 objects image shows object sizes for JSF state saving with an actual map size of 15 entries. This is ideal for supporting back button operation for up to 15 pages, but since ICEfaces doesn't support the back button, this is redundant. Object[] size is 18.42M. Object[] sizes are important because that is the way that state saving represents the state and structure of the component tree.

        Reducing the actual map size to 1 gives an Object[] size of 7.6M. This should be identical to the SingleCopyStateManager implementation where only 1 copy of state and structure are maintained between requests. This setting in applications with server push is problematic because of race conditions with client requests which is the reason for the custom single copy implementation.

        The ViewRootStateManager implementation doesn't collect state and structure into an object[] at all, but rather simply sticks a reference to the UIViewRoot into the Session, which was a long term solution to state saving in ICEfaces. This is clearly the fastest and most memory efficient implementation, but not suitable for failover installations as instances of UIComponent are not serializable. Object[] size of 6.4M represents other consumption.

        These numbers generated in JSP component-showcase with 3 clients navigating between 20 or so pages of the application 3 times.

        Show
        Greg Dick added a comment - The JSF state saving with 15 objects image shows object sizes for JSF state saving with an actual map size of 15 entries. This is ideal for supporting back button operation for up to 15 pages, but since ICEfaces doesn't support the back button, this is redundant. Object[] size is 18.42M. Object[] sizes are important because that is the way that state saving represents the state and structure of the component tree. Reducing the actual map size to 1 gives an Object[] size of 7.6M. This should be identical to the SingleCopyStateManager implementation where only 1 copy of state and structure are maintained between requests. This setting in applications with server push is problematic because of race conditions with client requests which is the reason for the custom single copy implementation. The ViewRootStateManager implementation doesn't collect state and structure into an object[] at all, but rather simply sticks a reference to the UIViewRoot into the Session, which was a long term solution to state saving in ICEfaces. This is clearly the fastest and most memory efficient implementation, but not suitable for failover installations as instances of UIComponent are not serializable. Object[] size of 6.4M represents other consumption. These numbers generated in JSP component-showcase with 3 clients navigating between 20 or so pages of the application 3 times.
        Hide
        Greg Dick added a comment -

        Checked in last changes and set ViewRootStateSavingImpl as the icefaces default as per ICE-4031

        Show
        Greg Dick added a comment - Checked in last changes and set ViewRootStateSavingImpl as the icefaces default as per ICE-4031

          People

          • Assignee:
            Unassigned
            Reporter:
            Greg Dick
          • Votes:
            1 Vote for this issue
            Watchers:
            1 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved: