Sun Java System Application Server Standard and Enterprise Edition 7 2004Q2 Performance and Tuning Guide |
Chapter 2
Tuning Your ApplicationThe following sections provide a comprehensive guide to tuning your applications for maximum performance. A complete guide to writing high performance Java and J2EE applications is beyond the scope of this document.
The following topics are discussed in this chapter:
Java Programming GuidelinesThis section covers issues related to Java coding and performance. The guidelines outlined are not specific to Application Server, but are general rules that are useful in many situations. For a complete discussion of Java coding best practices, refer to the Java BluePrints at http://java.sun.com/blueprints/performance/index.html.
Avoid serialization and deserialization, if possible
In Java, serialization and deserialization of objects is a CPU-intensive procedure and is likely to slow down your application. Use the transient keyword to reduce the amount of data serialized. Customized readObject() and writeObject() methods may be beneficial, in some cases.
Use StringBuffer.append() instead of "+"
In Java, Strings are immutable – they never change after creation. In the following sequence:
String str = "testing";
str = str + "abc";is understood by the compiler as:
String str = "testing";
StringBuffer tmp = new StringBuffer(str);
tmp.append("abc");
str = tmp.toString();Therefore, copying is inherently expensive and can become a significant factor in hindering performance in case it is overused. Instead, the use of StringBuffer.append() is recommended.
Assign null to variables you’re finished with
Explicitly assigning a null value to variables that are no longer needed helps the garbage collector to easily identify the parts of memory that can be safely reclaimed. While Java automates memory management, it does not prevent usage of excessive amounts of memory or memory leaks.
An application may induce memory leaks by holding on to objects without releasing references. This prevents the Java garbage collector from reclaiming those objects, and results in increasing amounts of memory being used. Explicitly nullifying references to unnecessary variables, after each transaction, allows the garbage collector to reclaim memory.
One way to detect memory leaks is to employ profiling tools and take memory snapshots after each transaction. A memory leak free application, in steady state, will show a steady active heap memory, after garbage collections.
Only declare methods as final when absolutely necessary
Modern optimizing dynamic compilers can perform inlining and other inter-procedural optimizations, even if Java methods are notfinal. Use the keyword as it was originally intended i.e., for program architecture and maintainability reasons. If you are absolutely certain that a method must not be overridden, use the final keyword.
Declare constants as static final
The dynamic compiler can perform some constant folding optimizations easily, when the hint is provided.
Avoid finalizers
Adding finalizers to your code makes the garbage collector more expensive and unpredictable. The virtual machine does not guarantee the time at which finalizers are run. Finalizers may not always be executed, before the program exits. Releasing critical resources in finalize() methods may lead to unpredictable application behavior.
Declare method arguments final
Declare method arguments final if they are not modified in the method. In general, declare all variables final if they are not modified after being initialized or set to some value.
Synchronize only when necessary
Do not synchronize code blocks or methods unless synchronization is required. Keep synchronized blocks or methods as short as possible to avoid scalability bottlenecks. Use the Java Collections Framework for unsynchronized data structures instead of more expensive alternatives such asjava.util.HashTable.
Use DataHandlers for SOAP attachments
Using a javax.activation.DataHandler for a SOAP attachment to improve performance.
JAXRPC specifies:
As a result, you can send an attachment (.gif or XML document) as a SOAP attachment to an RPC style webservice by utilizing the Java type mappings. When passing in any of the mandated Java type mappings (appropriate for your attachment mime type) as an argument for your webservice, JAXRPC runtime will handle these as SOAP attachments for you.
For example, to send out an image/gif attachment, you could use java.awt.Image, or you could create a DataHandler wrapper over your image. The advantages of using the wrapper are:
You can reuse "generic attachment code" to handle the attachments, because the DataHandler determines the content type of the contained data automatically. This feature is especially useful when using a document style service. Since the content is known at runtime, there is no need to make calls to attachment.setContent(stringContent, "image/gif"), for example.
For more information, consult the Application Server Developer's Guide to Web Services.
J2EE Programming GuidelinesThe J2EE model defines a framework for enterprise application development. It defines containers for the basic software components (JSPs, servlets and EJBs) and container services (JAAS, JDBC, JNDI, and JTA for example). While all parts of the J2EE model have their uses, the following sections discuss issues to keep in mind while designing the application architecture.
Servlets and JSPs
Many applications running on the Application Server are serviced by JSPs or servlets in the presentation tier. Servlets and JSPs are entry points to EJBs, on which more complex transactional business logic is implemented. It is not uncommon to see moderately complex transactional business applications coded entirely using Servlets and other J2EE APIs.
Avoid shared, modified class variables
In the case of the servlet multithread model which is the default model, a single instance of a servlet is created for each application server instance. All requests for a servlet on that application instance share the same servlet instance. This can lead to thread contention if there are synchronization blocks in the servlet code. So avoid using shared modified class variables, since they creates the need for synchronization.
Create sessions sparingly, invalidate whenever possible
Session creation is not for free. If a session is not required do not create one. Invalidate sessions when they are no longer needed.
Use <%page session="false"%>
Use the directive <%page session="false"%> to prevent HTTP Sessions from being automatically created when they are not necessary in JSPs.
Avoid large object graphs in an HttpSession
Do not store large object graphs inside an HttpSession. They force Java serialization and add computational overhead.
Don’t cache transaction data in HttpSession
Note that HttpSession access is not transactional. Do not use it as a cache of transactional data, which is typically kept in the database and accessed using Entity beans. Transactions will rollback, upon failures, to their original state. However, stale and inaccurate data may remain in HttpSession objects. Application Server provides “read-only” bean managed persistence entity beans for more cached access to read only data.
Set the reap interval value carefully
Modifying the reap interval setting (reapIntervalSeconds in server.xml) can improve performance, but setting it without considering the nature of your sessions and business logic can cause data inconsistency.
For example, if you set the reap interval parameter to 60 seconds, the value of session data will be recorded every 60 seconds. But if your servlet's client accesses the servlet to update a counter (for example, bidding price) at 20 second increments, then inconsistencies will result.
Here’s a possible scenario:
- Bidding starts at $5, in 60 seconds the value recorded will be $8 (+ 3 "20 second intervals").
- During the next 40 seconds, the client starts incrementing the price. The value the client sees is $10.
- During the client's 20 second rest, the Application Server stops and starts in 10 seconds. As a result, the latest value recorded at the 60 second interval ($8) is be loaded into the session.
- The Client clicks again expecting to see $11; but instead sees is $9, which is incorrect.
So, to avoid data inconsistencies, take into the account the expected behavior of the application when adjusting the reap interval.
Keep session size small
If possible, keep the session size small, so you incur less latency in response times. If possible, you should keep the session size below 7 Kb.
Checkpoint only when needed
In High Availability mode, when using Stateful session beans, consider checkpointing only those methods that alter the state of the bean significantly. This will reduce the number of times the bean state has to be checkpointed into the persistent store.
EJBs
The following guidelines can improve performance of EJB components in J2EE Applications. For more information, see the Application Server Developer’s Guide to Enterprise Java Beans Technology.
Cache EJB references in the servlet
To avoid a JNDI lookup for every request, cache EJB references in the servlet.
Cache EJBHomes in the servlet's init() method
Repeated lookups to a frequently used home interface can be expensive.
Use setSessionContext() or ejbCreate() to cache bean resources
This is again an example of using bean lifecycle methods to perform application actions, only once, where possible. Do not forget to place code to release acquired resources in the ejbRemove() method.
Minimize the database transaction isolation level
Reduce the database transaction isolation level when appropriate. Reduced isolation levels reduce work in the database tier, and could lead to better application performance. However, this must be done after carefully analyzing the database table usage patterns. The Application Server allows database isolation level to be set under <jdbc-connection-pool> in server configuration file. For more details on server configuration see the Application Server Administrators Guide and the Application Server Administrator’s Configuration File Reference.
Use rmic without -nolocalstubs when co-hosting
The Application Server ORB provides a mechanism for optimizing calls made from clients which are co-hosted in the same Java Virtual Machine as the server—for example, when a servlet calls an Enterprise Java Bean or one Enterprise Java Bean calling another enterprise bean located on the same server instance. When the servlets and EJB's are running in the same Java virtual machine, run the rmic compiler without the -nolocalstubs flag. This is the default setting i.e -nolocalstubs does not appear in the server configuration file.
Use rmic with -nolocalstubs when running remotely
If the application architecture is such that EJBs are hosted on a remote Application Server, the default behavior must be changed. This can be done via the administration command-line interface asadmin, on the server instance where the application is going to be deployed. The rmic options appear under the JVM Settings tab of the browser based administration interface. Use of local stubs gives significant performance enhancements and is the default behavior of the stub generator.
Use pass by reference, when possible
If you are sure about the access paths to the underlying data, then the beans can be configured to use pass by reference. This avoids argument copying on method invocations and result copying on method return. However, problems will arise if the data is modified by another source, during the invocation. This value can be set in the sun-ejb-jar.xml deployment descriptor as follows: <pass-by-reference>true</pass-by-reference>, on a per EJB granularity.
Remove Stateful session beans when they are no longer needed.
Removing them avoids passivation of the Stateful Session beans, and the attendant disk I/O operations.
Prefer high performance beans
Here are the EJB types are listed below, from the highest performance to the lowest:
EJB Pool and Cache
Both stateless session beans and entity beans can be pooled to improve server performance. In addition, both stateful session beans and entity beans can be cached to improve performance.
Table 2-1 Bean Type Pooling or Caching
Bean Type
Pooled
Cached
Stateless Session
Yes
No
Stateful Session
No
Yes
Entity
Yes
Yes
The difference between a pooled bean and a cached bean is that pooled beans are all equivalent and indistinguishable from one another. Cached beans, on the contrary, contain conversational state in the case of Stateful Session beans, and are associated with a primary key in the case of Entity beans. Entity beans are removed from the pool and added to the cache on ejbActivate() and removed from the cache and added to the pool on ejbPassivate(). ejbActivate() is called by the container when a needed Entity bean is not in the cache. ejbPassivate() is called by the container when the cache grows beyond its configured limits.
Here are some steps you can take when tuning the EJB pool and cache settings:
Tune the pool size
The EJB pool is used by Stateless Session and Entity EJB's. Keeping in mind how you use Stateless Session EJB's and the amount of traffic your server handles, tune the pool size to prevent excessive creation and deletion. Refer to the bean-pool sun-ejb-jar.xml deployment descriptor.
Tune the EJB cache size and time-out settings
The Cache is used by Stateful Session EJB's. Keeping in mind how your applications uses Stateful Session EJB's and the amount of traffic your server handles, tune the EJB cache size and time-out settings to minimize the number of activations and passivations. Refer to the bean-cache element in the sun-ejb-jar.xml deployment descriptor.
Explicitly call remove()
Allow Stateful Session EJB's to be removed from the container cache by explicitly calling of the remove() method in the client.
Tune the entity EJB's pool size
Entity Beans use both the EJB pool and cache settings. Tune the entity EJB's pool size to minimize the creation and destruction of beans. Prepopulating the pool with a non-zero steady size is useful to get better response for initial requests.
Cache and release bean specific resources
Use the setEntityContext() method to cache bean specific resources and release them using the unSetEntityContext() method.
Use Lazy Loading
Use Lazy Loading to avoid unnecessary preloading of child data.
Identify read-only beans
Configure read-only entity beans for read only operations.
Transactions
Here are some steps you can take when using transactions.
Use container managed transactions
Container managed transactions are preferred for consistency, and provide better performance.
Don’t encompass user input time
To avoid resources being held unnecessarily for long periods, a transaction should not encompass user input or user think time.
Identify non-transactional methods
Declare non-transactional methods of session EJB's with 'NotSupported' or 'Never' transaction attributes. These attributes can be found in the ejb-jar.xml deployment descriptor file. Transactions should span the minimum time possible since they lock database rows.
Use TX_REQUIRED for long transaction chains
For very large transaction chains, use the transaction attribute TX_REQUIRED. To ensure EJB methods in a call chain, use the same transaction.
Use lowest cost database locking
Use the lowest cost locking available from the database that is consistent with any transaction. Commit the data after the transaction completes rather than after each method call.
Use XA capable data sources only when needed
When multiple database resources, connector resources and/or JMS resources are involved in one transaction, a distributed or global transaction needs to be performed. This requires XA capable resource managers and data sources. Use XA capable data sources, only when two or more data source are going to be involved in a transaction. If a database participates in some distributed transactions, but mostly in local or single database transactions, it is advisable to register two <jdbc-resource> elements in server configuration file and use the appropriate resource in the application.
JDBC and Database Access
Here are some steps you can take when tuning the JDBC Connection Pool:
For large amounts of data, use JDBC directly
When dealing with large amounts of data, such as searching a large database, use JDBC directly rather than using Entity EJB's.
Encapsulate business logic in Entity EJBs
Combine business logic with the Entity EJB that holds the data needed for that logic to process.
Close connections after use
To ensure that connections are returned to the pool, always close the connections after use.
JMS
Here are some steps you can take when using JMS:
Tune the Message-Driven EJB's pool size
Tune the Message-Driven EJB's pool size to optimize the concurrent processing of messages.
Cache and release bean specific resources
Use the setMesssageDrivenContext() or ejbCreate() method to cache bean specific resources, and release those resources from the ejbRemove() method.
Limit Usage of JMS connections
When designing an application that utilizes JMS connections make sure you use a methodology that sparingly uses connections, by either pooling them or using the same connection for multiple sessions.
There is a limitation in iMQ, in that it doesn't pool connections, so the application developer has to work around that limitation (which is not keeping in the spirit of the Application Server will provide all resources).
The JMS connection uses 2 threads and the sessions use 1 thread each. Since these threads are not taken from a pool and the resultant objects aren't pooled, you could run out of memory during periods of heavy usage.
One workaround is to move createTopicConnection into the init of the jsp.
Note
Make sure you specifically close you session, or it will stay open, which will tie up resources.
References
- For details on performance guidelines regarding Java, see
http://java.sun.com/blueprints/performance/index.html- For details on optimizing EJB's, see
http://developer.java.sun.com/developer/technicalArticles/ebeans/sevenrules/- For tips on maximizing the Web Server’s performance, see the Sun ONE Web Server 6.1 Performance Tuning, Sizing, and Scaling Guide at
http://docs.sun.com/db/doc/817-1836-10