[
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.