DOING ENTERPRISE JAVA RIGHT FROM THE START!
Tuning JSF applicationsSession size matters
Eelco Klaver ([email protected])
LinkedIn: http://www.linkedin.com/in/eklaverMember of Know IT knowledge network
DOING ENTERPRISE JAVA RIGHT FROM THE START!
And finally just one tiny little thin Javabean, Mr. Creosote?
• First JSF production experience– Pilot with 30 users went great– After 3 months a big media event was
organized to train first 80 future users– Dedicated hardware consisted of 2
production like machines (just to besure…) each serving 40 users
– Memory usage grew rapidly
• Until it finally exploded…– …after 30 minutes already
DOING ENTERPRISE JAVA RIGHT FROM THE START!
Goal of this presentation
• Demystify the JSF performance pitfalls and discuss waysto measure and improve the scalability of JSFapplications
• After the session you will understand the performanceimplications of configuration options and designdecisions in JSF
• Prerequisites– Basic JSF knowledge is required to fully understand this session
• Scope– Focus on JSF specific issues– Application logic optimization and database tuning is out of
scope
DOING ENTERPRISE JAVA RIGHT FROM THE START!
Contents
• Introduction• The essentials of JSF
– JSF life cycle– Component tree and view state saving
• JSF performance pitfalls– Session size, CPU, bandwidth
• Profiling JSF applications– Memory profiling– Performance measuring of different phases
• Improving performance– Performance tweaks– Application guidelines
• Conclusion
DOING ENTERPRISE JAVA RIGHT FROM THE START!
Introduction
DOING ENTERPRISE JAVA RIGHT FROM THE START!
Introduction
• JSF is a relatively new technology, although…– JSF 1.0 released in Feb 2004– JSF 1.1 released in May 2004– JSF 1.2 released in Nov 2006– JSF currently defacto standard for web applications
• Advantages over other frameworks like ability to createreusable components maintaining own state…
• …but little is known about performance implications– Memory usage– Bandwidth– CPU usage
• Why bother?
DOING ENTERPRISE JAVA RIGHT FROM THE START!
The essentials of JSF
DOING ENTERPRISE JAVA RIGHT FROM THE START!
JSF: Component-based architecture
• Reusable user interface components– Each UI Component maintains own state– It represents attributes, behaviors and events– Separation between component behavior and rendering– Converters and validators can be registered with components– Examples include Label, Text-Field, Form, Checkbox, etc.
• Component treeUIViewRoot
HTMLFormHTMLPanelGrid
HTMLOutputLabelHTMLInputTextHTMLOutputLabelHTMLInputSecretHTMLCommandButton
DOING ENTERPRISE JAVA RIGHT FROM THE START!
ServerClient
JSF: Lifecycle
RestoreView
Apply Request
Values
Process Events
Conversionand
Validation
Process
Events U
pdate M
odel
ProcessEvents
InvokeApplication
ProcessEvents
RenderResponse
Send Request
Render Page
Render Response
Response complete
Response complete
Response complete
Response complete
Render ResponseRender Response
DOING ENTERPRISE JAVA RIGHT FROM THE START!
JSF: State saving (1)
• All view related state in JSF is saved and restored– Component tree structure– Component attributes
• Components save their own state– saveState– restoreState
• Examplepublic Object saveState(FacesContextcontext) { Object values[] = new Object[3]; values[0] = super.saveState(context); values[1] = this.title; values[2] = this.text; return ((Object) (values)); }
DOING ENTERPRISE JAVA RIGHT FROM THE START!
JSF: State saving (2)
• Methods of state saving is configurable– Server side - state is stored in HTTP session– Client side - state is serialized and stored in hidden input param
• Higher CPU usage• Higher bandwidth• Higher client memory usage• Security -> Encryption needed
• Session memory per user• Session memory old views• Clustering needs session sync.• More difficult to test
• Low server memory• Easier to cluster• Ability to restart server• No concurrency issues• (Browser back button)
• Low CPU usage• Low bandwidth• Easier integration with Ajax• Security
Client sideServer side
DOING ENTERPRISE JAVA RIGHT FROM THE START!
JSF Performance pitfalls
DOING ENTERPRISE JAVA RIGHT FROM THE START!
Bottlenecks (1/2)
• Memory– Session size caused by server side state saving– Session scoped view beans
• CPU– State saving and rendering due to high number of components
• Bandwidth– Especially with client side state saving
DOING ENTERPRISE JAVA RIGHT FROM THE START!
Bottlenecks (2/2)
• Quiz question– View state size?– Time to process?– Total page size?
• Hints– Client side
state saving– Multiple forms– No compression– Encryption– Role-based menu
• Answer– Memory: 300kB view state– CPU: 550ms– Bandwidth: 1,5MB page size
DOING ENTERPRISE JAVA RIGHT FROM THE START!
Profiling JSF applications
DOING ENTERPRISE JAVA RIGHT FROM THE START!
Locating performance problems
• Locate source of the problem– Client, server or network
• Determine lifecycle phase containing performance issues– MyFaces: log4j.logger.org.apache.myfaces.lifecycle=DEBUG
– JSF PhaseListener combined with JAMon
• Determine end-to-end time– JMeter, also for regression stress testing
• Network = Total - Server
2007-10-08 20:18:25,846 DEBUG - entering RESTORE_VIEW(1)2007-10-08 20:18:25,856 DEBUG - exiting RESTORE_VIEW(1) 2007-10-08 20:18:25,856 DEBUG - entering APPLY_REQUEST_VALUES(2)…2007-10-08 20:18:27,618 DEBUG - entering RENDER_RESPONSE(6)2007-10-08 20:18:27,799 DEBUG - exiting RENDER_RESPONSE(6)
DOING ENTERPRISE JAVA RIGHT FROM THE START!
The Java Application Monitor (JAMon)
• “The Java Application Monitor (JAMon) is a free,open source, easy to use, high performance,thread safe, Java API that allows developers to easilymonitor applications in development, test and production.”
• Includes a web application for viewing reports andcontrolling behavior
• Example Monitor monitor = MonitorFactory.start(“myPage.jsp”); // Code to be monitored monitor.stop();
DOING ENTERPRISE JAVA RIGHT FROM THE START!
JAMon performance listener (1/2)
• Example: PerformancePhaseListenerpublic class PerformancePhaseListener implements PhaseListener { … public void beforePhase(PhaseEvent phaseEvent) { String phaseName = phases.get(phaseEvent.getPhaseId()); HttpServletRequest request = getRequest(phaseEvent); Monitor monitor = MonitorFactory.start(phaseName + "." + request.getRequestURI()); getRequest(phaseEvent).setAttribute("monitor", monitor); }
public void afterPhase(PhaseEvent phaseEvent) { Monitor monitor = (Monitor)getRequest(phaseEvent) .getAttribute("monitor"); monitor.stop(); }
public PhaseId getPhaseId() {return PhaseId.ANY_PHASE;}
DOING ENTERPRISE JAVA RIGHT FROM THE START!
JAMon performance listener (2/2)
• Example: JAMon output
• Note: Render response phase includes lazy loading
DOING ENTERPRISE JAVA RIGHT FROM THE START!
JAMon view state listener
• Example: ViewStatePhaseListenerpublic class ViewStatePhaseListener implements PhaseListener { … public void beforePhase(PhaseEvent phaseEvent) { … LRUMap viewStateMap = getViewStateMap(phaseEvent); monitor = MonitorFactory.getMonitor(”Total view state", "KB"); monitor.add(sizeof(viewStateMap)/1024);
Object viewState = getViewState(phaseEvent); monitor = MonitorFactory.getMonitor("View state" + "." + request.getRequestURI(), "KB"); monitor.add(sizeof(viewState)/1024); }
public PhaseId getPhaseId() {return PhaseId.RESTORE_VIEW;}
DOING ENTERPRISE JAVA RIGHT FROM THE START!
Facelets debug component
• UI Debug– Display helpful information about the JSF component tree and
scoped variables in browser
• Example<ui:debug hotkey="V"/>
DOING ENTERPRISE JAVA RIGHT FROM THE START!
Improving performance
DOING ENTERPRISE JAVA RIGHT FROM THE START!
Performance improvements
• Possibilities depend on framework used– MyFaces– JSF RI 1.1/1.2– Ajax4JSF– Facelets
• Configuration parameters– State serialization– Number of views
• Application design principles– Managed bean scope– Number of components– Caching– Individual components chosen
DOING ENTERPRISE JAVA RIGHT FROM THE START!
Performance tipsConfiguration: State saving (1/7)
• Always use server side state saving– Although server memory decreases,– CPU increases significantly in Phase 1 and 6 because of
de/serialization and possibly encryption– Bandwidth usage increases as page size increases because view
state is replicated in hidden field for each form on the page!
• Example: configure in web.xml<context-param> <param-name>javax.faces.STATE_SAVING_METHOD</param-name> <param-value>server</param-value></context-param>
DOING ENTERPRISE JAVA RIGHT FROM THE START!
Performance tipsConfiguration: State saving (2/7)
• Comparison: client v.s. server side state saving (JSF RI)
1407763214ClientEnc.
106485851ClientC + E
393729Server
109466351ClientComp.
214
Page (kB)
784830Client
Total (ms)Phase 6 (ms)Phase 1 (ms)
DOING ENTERPRISE JAVA RIGHT FROM THE START!
Performance tipsConfiguration: State saving (3/7)
• CPU usage: client v.s. server side serialization (MyFaces)
Source: ApacheCon 2006
DOING ENTERPRISE JAVA RIGHT FROM THE START!
Performance tipsConfiguration: State saving (4/7)
• CPU usage: client state saving (MyFaces)
Source: ApacheCon 2006
DOING ENTERPRISE JAVA RIGHT FROM THE START!
Performance tipsConfiguration: State saving (5/7)
• Disable state serialization and compression– State serialization and compression of the view state has a
major impact on the CPU usage in phase 1 and 6.
• Example: configure in web.xml– MyFaces:
– JSF RI 1.2: com.sun.faces.serializeServerState [false] com.sun.faces.compressViewState [true]
<context-param> <param-name>org.apache.myfaces.SERIALIZE_STATE_IN_SESSION</param-name> <param-value>false</param-value></context-param><context-param> <param-name>org.apache.myfaces.COMPRESS_STATE_IN_SESSION</param-name> <param-value>false</param-value></context-param>
DOING ENTERPRISE JAVA RIGHT FROM THE START!
Performance tipsConfiguration: State saving (6/7)
• Server side state saving options (MyFaces)
Source: ApacheCon 2006
DOING ENTERPRISE JAVA RIGHT FROM THE START!
Performance tipsConfiguration: State saving (7/7)
• Limit the number of active views per session– The default of 15 logical and 15 views in session requires 10 MB
per user with an average view state of 50 kB.
• Example: configure in web.xml– JSF RI 1.2:
– MyFaces: org.apache.myfaces.NUMBER_OF_VIEWS_IN_SESSION
• Note: not configurable with latest Ajax4JSF framework
<context-param> <param-name>com.sun.faces.numberOfViewsInSession</param-name> <param-value>5</param-value></context-param><context-param> <param-name>com.sun.faces.numberOfLogicalViews</param-name> <param-value>10</param-value></context-param>
DOING ENTERPRISE JAVA RIGHT FROM THE START!
Performance tipsConfiguration: Ajax4JSF (1/1)
• Configure Ajax filter to only parse Ajax responses oreven disable parsing– Ajax4JSF Filter will parse generated (X)HTML and converts it to
valid XHTML. Disabling will accelaterate processing.
• Example<filter> <filter-name>ajax4jsf</filter-name> <filter-class>org.ajax4jsf.webapp.TidyFilter</filter-class> <init-param> <param-name>forceparser</param-name> <param-value>false</param-value> </init-param></filter>
<filter> <filter-name>ajax4jsf</filter-name> <filter-class>org.ajax4jsf.FastFilter</filter-class> <init-param> <param-name>forceparser</param-name> <param-value>false</param-value> </init-param></filter>
DOING ENTERPRISE JAVA RIGHT FROM THE START!
Performance tipsGuidelines: Bean scope (1/1)
• Make managed beans request scoped– Session scoped beans will increase the memory usage as session
size grows for each user
• Example: master/details view<t:saveState value="#{signaalSearchView.signalen}”/><t:dataTable value="#{signaalSearchView.signalen}" … var="signaal"> <t:column> <f:facet name="header"> <h:outputText value="#{msgSignalen['signaal.header.nummer']}" /> </f:facet> <t:commandLink action="#{signaalTonenTabbedPaneView.show}"> <h:outputText value="#{signaal.id}" /> <f:setPropertyActionListener value="#{signaal.id}" target="#{signaalTonenTabbedPaneView.signaalId}" /> </t:commandLink> </t:column>…
DOING ENTERPRISE JAVA RIGHT FROM THE START!
Performance tipsGuidelines: Component-tree (1/3)
• Use c:if instead of visibleOnUserRole and rendered– The attributes visibleOnUserRole and rendered still cause component
to appear in component tree, increasing memory and CPU usage– Note: this only works for values known at “compile time”
• Example: role-based menu item<t:commandLink action=”#{action}" … visibleOnUserRole="#{roles}"> <t:graphicImage value=”/wc.gif” rendered="#{not open and not disabled}"/> <t:graphicImage value=”/wo.gif” rendered="#{open and not disabled}"/> <t:graphicImage value=”/gc.gif” rendered="#{not open and disabled}"/> <t:graphicImage value=”/go.gif” rendered="#{open and disabled}"/> #{name}</t:commandLink>
<c:if test=“#{ec:isUserInRole(roles)}"> <t:commandLink action=”#{action}" …> <t:graphicImage value="#{(not open and not disabled?'/wc.gif': (open and not disabled?'/wo.gif': (not open and disabled?'/gc.gif': '/go.gif')))}"/> #{name} </t:commandLink></c:if>
DOING ENTERPRISE JAVA RIGHT FROM THE START!
<c:forEach var=“item” items=“#{itemsView.items}” <t:commandLink action="#{action}"> #{name} … </t:commandLink></c:forEach>
<ui:repeat var=“item” values=“#{itemsView.items}” <t:commandLink action="#{action}"> #{name} … </t:commandLink></ui:repeat>
Performance tipsGuidelines: Component-tree (2/3)
• Use ui:repeat instead of c:forEach– The c:forEach creates component in component tree for every object
in the list, increasing memory and CPU usage– Note: this only works for values known at “compile time”
• Example: list of shopping items
DOING ENTERPRISE JAVA RIGHT FROM THE START!
Performance tipsGuidelines: Component-tree (3/3)
• Prevent multiple components per mode or role– Although not rendered simultaneously, both components will still
be in the component tree using memory and CPU in phase 1 and 6– Use extended components instead
• Example: page with read- and edit mode
<h:outputText value="#{messages['label.email']}"/><h:panelGroup> <h:outputText value="#{myView.email}" rendered="#{myView.readMode}"/> <h:inputText value="#{myView.email}” size="20" maxlength="128” rendered="#{not myView.readMode}"> <t:validateEmail /> </h:inputText></h:panelGroup>
<h:outputText value="#{messages['label.email']}"/><h:panelGroup> <t:inputText value="#{myView.email}” size="20" maxlength="128" displayValueOnly="#{myView.readMode}"> <t:validateEmail /> </t:inputText></h:panelGroup>
DOING ENTERPRISE JAVA RIGHT FROM THE START!
Performance tipsGuidelines: Caching (1/1)
• Cache list items in converters– Converters are called multiple times within request phase 3
• Example: make converter a view bean and give it scope
<t:selectOneListbox … converter="#{rubriceringConverter}"> <t:selectItems value="#{rubriceringConverter.rubriceringen}" var="rubricering" itemValue="#{rubricering}" itemLabel="#{rubricering.naam}"/></t:selectOneListbox>
<managed-bean> <managed-bean-name>rubriceringConverter</managed-bean-name> <managed-bean-class>RubriceringConverter</managed-bean-class> <managed-bean-scope>application</managed-bean-scope> …</managed-bean>
DOING ENTERPRISE JAVA RIGHT FROM THE START!
Performance tipsGuidelines: Evil components (1/1)
• Use only stable component libraries– Very easy to add external components, like JBoss RichFaces
component library with calendar, tooltip, modalpanel, butintroduces performance problems and memory leak in IE6.
• Example: calendar component<rich:calendar value="#{projectView.startDate}” enableManualInput="true" popup="true" direction="auto" datePattern="dd-MM-yyyy”/>
<t:inputCalendar value="#{projectView.startDatum}" renderAsPopup="true"
popupDateFormat="dd-MM-yyyy"/>
DOING ENTERPRISE JAVA RIGHT FROM THE START!
Conclusion
DOING ENTERPRISE JAVA RIGHT FROM THE START!
Summary
• JSF performance pitfalls– Memory consumption– CPU usage– Bandwidth
• First profile then optimize• JSF and state saving
– Always use server side state saving– Configure number of states to be stored
• Application design guidelines– Use request-scoped view beans– Decrease number of components– Use caching in converters– Choose components wisely
DOING ENTERPRISE JAVA RIGHT FROM THE START!
Discussion
• Is JSF still a good choice? Yes,– Clean separation between component state/behavior and
rendering– Easy to use– Standard– With appropriate measures it can still perform right
• Future enhancement might be to only save state that’sdifferent from default:– Facelets XHTML defines component tree with default values– This tree is updated with delta state
DOING ENTERPRISE JAVA RIGHT FROM THE START!
Questions
DOING ENTERPRISE JAVA RIGHT FROM THE START!
References
• Tools– JMeter - http://jakarta.apache.org/jmeter/– JAMon - http://jamonapi.sourceforge.net/– YSlow - https://addons.mozilla.org/en-US/firefox/addon/5369
• Technologies– JBoss Serialization - http://labs.jboss.com/serialization/– Facelets - https://facelets.dev.java.net
• Books– High Performance Web Sites, Steve Souders,
http://developer.yahoo.com/performance/