Sun Java logo     Previous      Contents      Index      Next     

Sun logo
Sun Java System Application Server Standard and Enterprise Edition 7 2004Q2 Performance and Tuning Guide 

Chapter 2
Tuning Your Application

The 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 Guidelines

This 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:

For more information, consult the Application Server Developer's Guide to Web Services.


J2EE Programming Guidelines

The 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:

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.


Note

Keep in mind that decomposing an application into a moderate to large number of separate EJBs can create an application performance degradation and more overhead. EJBs, unlike JavaBeans, are not simply Java objects. EJBs are higher level entities than Java objects. They are components with remote call interface semantics, security semantics, transaction semantics, and properties.


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



Previous      Contents      Index      Next     


Copyright 2004 Sun Microsystems, Inc. All rights reserved.