myfaces-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Matthias We├čendorf (JIRA) <...@myfaces.apache.org>
Subject [jira] Updated: (TRINIDAD-1669) Improve transient memory consumption of UIXComponentBase.getClientId()
Date Tue, 17 Aug 2010 05:54:16 GMT

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

Matthias We├čendorf updated TRINIDAD-1669:
-----------------------------------------

           Status: Resolved  (was: Patch Available)
    Fix Version/s: 2.0.0.3-core
       Resolution: Fixed

> 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
>             Fix For: 2.0.0.3-core
>
>         Attachments: trinidad-1669-take2.patch, 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.


Mime
View raw message