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