[ 
https://issues.apache.org/jira/browse/TRINIDAD-1669?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Andy Schwartz updated TRINIDAD-1669:
------------------------------------

    Status: Patch Available  (was: Open)

> Improve transient memory consumption of UIXComponentBase.getClientId()
> ----------------------------------------------------------------------
>
>                 Key: TRINIDAD-1669
>                 URL: https://issues.apache.org/jira/browse/TRINIDAD-1669
>             Project: MyFaces Trinidad
>          Issue Type: Improvement
>         Environment: All
>            Reporter: Blake Sullivan
>            Assignee: Blake Sullivan
>         Attachments: trinidad-1669.patch
>
>   Original Estimate: 72h
>  Remaining Estimate: 72h
>
> Calling UIXComponentBase.getClientId consumes a great deal of transient 
> memory.  Under light loads, this doesn't matter--the objects are extremely 
> short-lived and are allocated out of the first-generation heap.  However, 
> when large numbers of users are accessing the server simultaneously these 
> allocations contribute to first-generation heap exhaustion and 
> first-generation heap GC's when deeply nested NamingContainers are used.
> There are two reasons that large amounts of transient memory is consumed in 
> these cases:
> 1) UIXComponentBase doesn't cache clientIds because the clientIds are partly 
> determined by the component's ancestors and there are cases (such as 
> stamping), where multiple clientIds may map to a single component instance
> 2) clientIds are generated recursively by:
> a) calling getContainerClientId() and appending the NamingContainer separator 
> and the component's id to the result
> b) getContainerClientId() is implemented by calling getContainerClientId() 
> and doing likewise
> So, each NamingContainer in the hierarchy is going to:
> 1) Get it's ancestor's container clientId and if one exists
> 2) Get it's id attribute
> 3) Allocate a StringBuilder to contain these two Strings, append them together
> 4) Convert the StringBuilder to a String and return the result
> An earlier JIRA used a ThreadLocal StringBuilder to remove the StringBuilder 
> allocation in step 3) in the common case, halving the transient memory usage, 
> however we still have the String allocations made necessary by the use of   
> String getContainerClientId(FacesContext context, UIComponent child).
> For a 20 row table containing 10 columns nested four NamingContainers deep 
> (counting the table as one of these), we end up with 1000 String allocations, 
> which wouldn't necessarily be that bad if the size of the Strings wasn't 
> increasing and if the Rendering code was the only code calling getClientId() 
> (InvokeOnComponent is the primary culprit here, though replacing 
> invokeOnComponent calls with visitTree calls improves things).
> The proposed solution is to replace generating new Strings at each 
> NamingContainer level with appending the NamingContainer ids into a 
> StringBuilder (in fact, the shared StringBuilder) passed to the appending 
> code--a String is only generated when the returning the value from 
> getClientId().  In scalability testing, this change has been worth about 8%.
> The advantages of this approach are:
> 1) If the component code compiles, the code will almost certainly work 
> correctly
> 2) It clientId caching is also used, this approach speeds up generation of 
> the cached result
> The disadvatanges of this approach is:
> 1) Any overrides of getClientId() or getContainerClientId() must be changed 
> to overrides of appendClientId() or appendContainerClientId().  To enforce 
> this, getClientId() and getContainerClientId() are made final on 
> UIXComponentBase.  This, is of course, an incompatible api change
> The new/changed apis on UIXComponentBase:
>   /**
>    * Appends the container's clientId for the requesting child to the 
> StringBuilder, returning the passed in StringBuilder.
>    * Component implementations are only allowed to mutate the StringBuilder 
> other than to append.
>    * Subclasses that wish to modify the clientIds returned for their children 
> should override this method rather than
>    * <code>getContainerClientId</code>.
>    * @param context FacesContext
>    * @param child Optional child component that is requesting the container's
>    *              clientId
>    * @param clientIdAppendable StringBuilder to append the container's 
> clientId to
>    * @see #getContainerClientId(FacesContext, UIComponent)
>    */
>   public StringBuilder appendContainerClientId(
>     FacesContext context,
>     UIComponent child,
>     StringBuilder clientIdAppendable)
>   /**
>    * Appends the clientId of this component to the StringBuilder, returning 
> the passed in StringBuilder.
>    * Component implementations typically only mutate the StringBuilder to 
> append.
>    * Subclasses that wish to modify the clientIds that they return should 
> override this method rather than
>    * <code>getClientId</code>.
>    * @param context FacesContext
>    * @param clientIdAppendable StringBuilder to append the component's 
> clientId to
>    * @return the clientIdAppendable StringBuilder passed in as the 
> clientIdAppendable parameter
>    * @see #getClientId
>    */
>   public StringBuilder appendClientId(FacesContext context, StringBuilder 
> clientIdAppendable)
>    /**
>    * Final override of getContainerClientId to make 
> <code>appendContainerClientId</code>
>    * the supported hook for modifying the clientIds of a component's children.
>    * @see #appendContainerClientId
>    */
>   @Override
>   public String getContainerClientId(FacesContext context)
>   /**
>    * Final override of getContainerClientId to make 
> <code>appendContainerClientId</code>
>    * the supported hook for modifying the clientIds of a component's children.
>    * The implementation uses <code>appendContainerClientId</code> to 
> calculate the
>    * the container's clientId prefix with far fewer temporary Strings than
>    * the class JSF implementation.
>    * @param context FacesContext
>    * @param child Optional child component that is requesting the container's
>    *              clientId
>    * @return the clientId prefix to add to the child's id
>    * @see #appendContainerClientId
>    */
>   @Override
>   public final String getContainerClientId(FacesContext context, UIComponent 
> child)
>   /**
>    * Final override of getClientId to make <code>appendClientId</code>
>    * the supported hook for modifying the clientIds of a component.
>    * The implementation uses <code>appendClientId</code> to calculate the
>    * the component's clientId with far fewer temporary Strings than
>    * the class JSF implementation.
>    * @param context FacesContext
>    * @return the clientId
>    * @see #appendClientId
>    */
>   @Override
>   public final String getClientId(FacesContext context)

-- 
This message is automatically generated by JIRA.
-
You can reply to this email to add a comment to the issue online.

Reply via email to