Tuning JSF applications
Session size matters
LinkedIn: http://www.linkedin.com/in/eklaver Member of Know IT knowledge network
DOING ENTERPRISE JAVA RIGHT FROM THE START!
And finally just one tiny little thin Java bean, 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 be sure) 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 ways to measure and improve the scalability of JSF applications After the session you will understand the performance implications of conguration options and design decisions 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 JSF JSF JSF 1.0 released in Feb 2004 1.1 released in May 2004 1.2 released in Nov 2006 currently defacto standard for web applications
Advantages over other frameworks like ability to create reusable 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.
UIViewRoot HTMLForm HTMLPanelGrid HTMLOutputLabel HTMLInputText HTMLOutputLabel HTMLInputSecret HTMLCommandButton
DOING ENTERPRISE JAVA RIGHT FROM THE START!
Component tree
JSF: Lifecycle
Response complete
Send Request
Restore View
Apply Request Values
Process Events
Conversion and Validation
Response complete
Process Events
Render Response
Client
Response complete
Server
Response complete
Update Model
Render Page
Render Response
Process Events
Invoke Application
Process Events
Render Response
DOING ENTERPRISE JAVA RIGHT FROM THE START!
Render Response
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
Example
public 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 Server side Low CPU usage Low bandwidth Easier integration with Ajax Security Session memory per user Session memory old views Clustering needs session sync. More difficult to test Client side Low server memory Easier to cluster Ability to restart server No concurrency issues (Browser back button) Higher CPU usage Higher bandwidth Higher client memory usage Security -> Encryption needed
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
2007-10-08 2007-10-08 2007-10-08 2007-10-08 2007-10-08 20:18:25,846 DEBUG - entering RESTORE_VIEW(1) 20:18:25,856 DEBUG - exiting RESTORE_VIEW(1) 20:18:25,856 DEBUG - entering APPLY_REQUEST_VALUES(2) 20:18:27,618 DEBUG - entering RENDER_RESPONSE(6) 20:18:27,799 DEBUG - exiting RENDER_RESPONSE(6)
JSF PhaseListener combined with JAMon
Determine end-to-end time
JMeter, also for regression stress testing
Network = Total - Server
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 easily monitor applications in development, test and production. Includes a web application for viewing reports and controlling 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: PerformancePhaseListener
public 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: ViewStatePhaseListener
public 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 tips Configuration: 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 tips Configuration: State saving (2/7)
Comparison: client v.s. server side state saving (JSF RI)
Page (kB) Phase 1 (ms) Phase 6 (ms) Total (ms) Client Client Comp. Client Enc. Client C+E Server 214 51 214 51 9 30 63 63 58 2 48 46 77 48 37 78 109 140 106 39
DOING ENTERPRISE JAVA RIGHT FROM THE START!
Performance tips Configuration: State saving (3/7)
CPU usage: client v.s. server side serialization (MyFaces)
Source: ApacheCon 2006
DOING ENTERPRISE JAVA RIGHT FROM THE START!
Performance tips Configuration: State saving (4/7)
CPU usage: client state saving (MyFaces)
Source: ApacheCon 2006
DOING ENTERPRISE JAVA RIGHT FROM THE START!
Performance tips Configuration: 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:
<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>
JSF RI 1.2: com.sun.faces.serializeServerState [false] com.sun.faces.compressViewState [true]
DOING ENTERPRISE JAVA RIGHT FROM THE START!
Performance tips Configuration: State saving (6/7)
Server side state saving options (MyFaces)
Source: ApacheCon 2006
DOING ENTERPRISE JAVA RIGHT FROM THE START!
Performance tips Configuration: 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:
<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>
MyFaces: org.apache.myfaces.NUMBER_OF_VIEWS_IN_SESSION
Note: not configurable with latest Ajax4JSF framework
DOING ENTERPRISE JAVA RIGHT FROM THE START!
Performance tips Configuration: Ajax4JSF (1/1)
Configure Ajax filter to only parse Ajax responses or even 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.FastFilter</filter-class> <filter-class>org.ajax4jsf.webapp.TidyFilter</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 tips Guidelines: 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 tips Guidelines: 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}"> <c:if test=#{ec:isUserInRole(roles)}"> <t:graphicImageaction=#{action}" > <t:commandLink value=/wc.gif rendered="#{not <t:graphicImage value="#{(not open and not disabled}"/> disabled?'/wc.gif': <t:graphicImage value=/wo.gif and not disabled?'/wo.gif': (open rendered="#{openopen not disabled}"/> (not and and disabled?'/gc.gif': <t:graphicImage value=/gc.gif '/go.gif')))}"/> rendered="#{not open and disabled}"/> #{name} <t:graphicImage value=/go.gif </t:commandLink> rendered="#{open and disabled}"/> </c:if> #{name} </t:commandLink>
DOING ENTERPRISE JAVA RIGHT FROM THE START!
Performance tips Guidelines: 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
<c:forEach var=item items=#{itemsView.items} <ui:repeat values=#{itemsView.items} <t:commandLink action="#{action}"> #{name} </t:commandLink> </c:forEach> </ui:repeat>
DOING ENTERPRISE JAVA RIGHT FROM THE START!
Performance tips Guidelines: 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> <t:inputText value="#{myView.email} size="20" maxlength="128" <h:outputText value="#{myView.email}" displayValueOnly="#{myView.readMode}"> rendered="#{myView.readMode}"/> <t:validateEmail /> <h:inputText value="#{myView.email} size="20" maxlength="128 </t:inputText> rendered="#{not myView.readMode}"> </h:panelGroup> <t:validateEmail /> </h:inputText> </h:panelGroup>
DOING ENTERPRISE JAVA RIGHT FROM THE START!
Performance tips Guidelines: 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
<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> <t:selectOneListbox converter="#{rubriceringConverter}"> <t:selectItems value="#{rubriceringConverter.rubriceringen}" var="rubricering" itemValue="#{rubricering}" itemLabel="#{rubricering.naam}"/> </t:selectOneListbox>
DOING ENTERPRISE JAVA RIGHT FROM THE START!
Performance tips Guidelines: Evil components (1/1)
Use only stable component libraries
Very easy to add external components, like JBoss RichFaces component library with calendar, tooltip, modalpanel, but introduces performance problems and memory leak in IE6.
Example: calendar component
<rich:calendar value="#{projectView.startDate} <t:inputCalendar value="#{projectView.startDatum}" renderAsPopup="true" enableManualInput="true" popupDateFormat="dd-MM-yyyy"/> popup="true" direction="auto" datePattern="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 thats different 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/
DOING ENTERPRISE JAVA RIGHT FROM THE START!