Sun ONE Application Server 7 Performance Tuning Guide |
Chapter 3
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 Sun ONE 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.
In Java, Strings are immutable — they never change after creation. For example, the following sequence
is understood by the compiler as:
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.
Doing this 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 when necessary, declare methods as final. 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.
- Use static final when declaring constants. The dynamic compiler can perform some constant folding optimizations easily, when the hint is provided.
- 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 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.
- Don't 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.
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.
Servlet and JSP Programming Guidelines
Many applications running on the Sun ONE 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.
- 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, the use of shared modified class variables should be avoided as it creates the need for synchronization.
- 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 the directive <%page session="false"%> to prevent HTTP Sessions from being automatically created when they are not necessary in JSPs.
- Do not store large objects graphs inside an HttpSession. This forces Java serialization and adds computational overhead.
- 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. Sun ONE Application Server provides “read-only” bean managed persistence entity beans for more cached access to read only data.
EJB Programming Guidelines
The following guidelines can improve performance of EJB components in J2EE Applications. For more information, see Sun ONE Application Server Developer’s Guide to Enterprise Java Beans Technology.
- To avoid a JNDI lookup for every request, cache EJB references at the servlet.
- Cache the EJBHomes in the servlet's init() method. Repeated lookups to an often use home interface and can be expensive.
- Use the setSessionContext() or ejbCreate() method to cache bean specific 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.
- 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. Sun ONE 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 Sun ONE Application Server Administrators Guide and the Sun ONE Application Server Administrator’s Configuration File Reference.
- The Sun ONE 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, a servlet code 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.
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.
- If the application developer is 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. This avoids passivation of the Stateful Session beans, and disk I/O operations.
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 3-1 Bean Type Pooling or Cacheing
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:
- The EJB pool is used by Stateless Session and Entity EJBs. Keeping in mind how you use Stateless Session EJBs 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.
- The Cache is used by Stateful Session EJBs. Keeping in mind how your applications uses Stateful Session EJBs 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.
- Allow Stateful Session EJB's to be removed from the container cache by explicitly calling of the remove() method in the client.
- 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.
- Use the setEntityContext() method to cache bean specific resources and release them from the unSetEntityContext() method.
- Use Lazy Loading to avoid unnecessary preloading of child data.
- Configure read-only entity beans for read only operations.
Transactions
Here are some steps you can take when using transactions:
- To avoid resources being held unnecessarily for long periods, a transaction should not encompass user input or user think time.
- Container managed transactions are preferred for consistency, and provide better performance.
- 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.
- For very large transaction chains, use the transaction attribute TX_REQUIRED. To ensure EJB methods in a call chain, use the same transaction.
- 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.
- 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:
- When dealing with large amounts of data, such as searching a large database, use JDBC directly rather than using Entity EJB's.
- Combine business logic with the Entity EJB that holds the data needed for that logic to process.
- 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 to optimize the concurrent processing of messages.
- Use the setMesssageDrivenContext() or ejbCreate() method to cache bean specific resources, and release those resources from the ejbRemove() method.
References