The performance of EJB-based J2EE systems overwhelmingly
depends on getting the design right. Use performance-optimizing
design patterns: Value Object, Page-by-Page Iterator,
ValueListHandler, Data Access Object, Fast Lane
Reader, Service Locator, Verified Service Locator,
EJBHomeFactory, Session Façade,
CompositeEntity, Factory, Builder, Director,
Recycler, Optimistic Locking, Reactor, Front Controller, Proxy,
Decorator, and Message Façade.
Explicitly remove beans from the container when a session is expired.
Leaving beans too long will get them serialized by the container,
which can dramatically decrease performance.
Coarse-grained EJBs are faster. Remote EJB calls should be combined
to reduce the required remote invocations.
Design the application to access entity beans from session beans.
Collocated EJBs should be defined as Local EJBs (from EJB 2.0),
collocated within an application server that can optimize local EJB
communications, or built as normal JavaBeans and then wrapped in an
EJB to provide one coarse-grained EJB
(CompositeEntity design pattern).
EJBs should not be simple wrappers on database data rows; they should
have business logic. To simply access data, use JDBC directly.
Stateless session beans are faster than stateful session beans. If
you have stateful beans in your design, convert them to stateless
session beans by adding parameters that hold the extra state to the
bean methods.
Optimize read-only EJBs to use their own design, their own
application server, read-only transactions, and their own optimal
configuration.
Cache JNDI lookups.
Use container-managed persistence (CMP) by default. Profile the
application to determine which beans cause bottlenecks from their
persistency, and implement bean-managed persistence (BMP) for those
beans.
Use the Data Access Object design pattern to abstract your BMP
implementations so you can take advantage of optimizations possible
when dealing with multiple beans or database-specific features.
Minimize the time spent in any
transaction, but don't shorten transactions so much
that you are unnecessarily increasing the total number of
transactions. Combine transactions that are close in time to minimize
overall transaction time. This may require controlling the
transaction manually (i.e., turning off auto-commit for JDBC
transactions or using TX_REQUIRED for EJBs).
J2EE transactions are defined with several isolation modes. Choose
the lowest-cost transaction isolation level that avoids corrupting
the data. Transaction levels in order of increasing cost are:
TRANSACTION_READ_UNCOMMITTED,
TRANSACTION_READ_COMMITTED,
TRANSACTION_REPEATABLE_READ, and
TRANSACTION_SERIALIZABLE.
Don't leave transactions open, relying on the user
to close them. There will inevitably be times when the user does not
close the transaction, and the consequent long transaction will
decrease the performance of the system significantly.
Bulk or batch updates are usually more efficiently performed in
larger transactions.
Lock only where the design absolutely requires it.
Beans.instantiate( ) incurs a filesystem check to
create new bean instances in some application servers. Use the
Factory pattern with new to avoid the filesystem
check.
Tune the message-driven beans' pool size to optimize
the concurrent processing of messages.
Use initialization and finalization methods to cache bean-specific
resources. Good initialization locations are
setSessionContext( ), ejbCreate(
), setEntityContext( ), and
setMesssageDrivenContext( ); good finalization
locations are ejbRemove( ) and
unSetEntityContext( ).
Tune the application server's JVM heap, pool sizes,
and cache sizes.