Java Performance Tuning
Java(TM) - see bottom of page
Our valued sponsors who help make this site possible
JProfiler: Get rid of your performance problems and memory leaks!
Tips August 2003
Get rid of your performance problems and memory leaks!
Get rid of your performance problems and memory leaks!
Back to newsletter 033 contents
At JavaPerformanceTuning.com, we scan the internet for any articles with interesting
Java performance information. When we find one, we add it to our
huge article list,
but we also extract the performance tips from those articles, and list
those extracted tips in our newsletter. Below, you can see this month's extracted
Proper handling of the four session scopes (Page last updated 2003 July, Added 2003-08-26, Author Kyle Gabhart, Publisher IBM). Tips:
- J2EE Web applications, there are four session scopes of increasing durations: Page; Request; Session; Application. Getting the scope wrong can increase overheads and limit scalability.
- Page scope (javax.servlet.jsp.PageContext.getAttribute()) spans a single JSP page, and can be used for local variables. JSP data should use page scope.
include directives combine mutliple pages to one page scope; for the
include action you would need to use the request scope to share data between the pages.
- Request (javax.servlet.http.HttpServletRequest.getAttribute()) spans a request which may involve multiple servlets and JSPs. Pages linked with
include actions can use this scope to share data.
- Session (javax.servlet.http.HttpSession.getAttribute()) spans a session, i.e. persistent data for one user. (Include <%@ page session="true" %> to use sessions in JSPs.)
- Application (javax.servlet.ServletContext.getAttribute()) is essentially global. This should be used sparingly and reserved for data that truly needs to be shared between components and across user sessions, for objects that are shared by all Web components within the application. Typical examples include a cached DAO, cache of JNDI references, or any sort of common factory or other component that needs to use the Singleton pattern.
An Automatic Wait Cursor: WaitCursorEventQueue (Page last updated 2003 July, Added 2003-08-26, Author Nathan Arthur, Publisher Kabutz). Tips:
- If interrupt() is called during a wait(), an InterruptedException will not always be thrown. Specifically the InterruptedException will only be thrown if the delay timeout has not ended yet. If the timeout has ended, but the thread is still in contention for the object's monitor, interrupt() will simply set the interrupt status of the thread to true, without throwing the exception.
- [Article discusses implementing an efficient automatic wait cursor].
Add concurrent processing with message-driven beans (Page last updated 2003 Jul, Added 2003-08-26, Author Amit Poddar, Publisher JavaWorld). Tips:
- The EJB specification restricts spawning new user threads in an EJB container and methods on a session bean can only be invoked synchronously. Messaging's inherent asynchronous nature can avoid these limitations.
- Concurrency improves a program's throughput, execution speed, and responsiveness.
- Even on a single processor system, concurrent programs efficiently use computer resources by overlapping slow I/O (input/output) operations with computational tasks.
- Session EJB components are limited to synchronous invocation and must be invoked serially. Message Driven Beans can be invoked asynchronously on message events and hence can process requests in parallel.
- The JMS selector feature tends to slow down with numerous messages in a queue.
- Concurrency does not come free and requires many more resources in a short time span. The peak load requirement for a concurrent application is much higher as compared to a serial one.
- For multiple concurrent search requests, you are better off ignoring the results of any malfunctioning or long delayed requests and returning the remaining results to the user in an acceptable timeframe.
- Use QueueReceiver.receive(long waitimeout) to timeout waiting for responses from requests sent through queues.
- Always use a timeout to invoke external systems not in your control.
ConcurrentHashMap and CopyOnWriteArrayList offer thread safety and improved scalability (Page last updated 2003 Jul, Added 2003-08-26, Author Brian Goetz, Publisher IBM). Tips:
- Synchronizing the methods of a class is an impediment to scalability, because only one thread can access the class at a time.
- Synchronizing the methods of a class is insufficient to provide true thread safety: many common compound operations still require additional synchronization, e.g. put-if-absent.
- Synchronized collections wrappers and similarly synchronized classes are sometimes called conditionally thread-safe -- all individual operations are thread-safe, but sequences of operations where the control flow depends on the results of previous operations may be subject to data races.
- Locking the entire List while iterating collections may be necessary to avoid concurrency problems, but this could block other threads from accessing the list for a long time.
- The iterators implemented in the java.util Collections classes are fail-fast, which means that if one thread changes a collection while another thread is traversing it through an Iterator, the next Iterator.hasNext() or Iterator.next() call will throw ConcurrentModificationException.
- Locking a shared resource for exclusive access is a scalability bottleneck -- it prevents other threads from being able to access that resource, even if idle processors are available to schedule those threads.
- To achieve scalability, you must eliminate or reduce your dependence on exclusive resource locks.
- The most common Map operations, get() and put(), may involve more computation than is obvious, e.g. calling equals() and hashCode() on many elements.
- The primary purpose of a cache is to reduce service time and increase throughput by reusing the results of a previous computation.
- A typical characteristic of cache workload is that retrievals are much more common than updates, so (ideally) a cache would offer very good get() performance.
- A cache that impedes application performance is worse than no cache at all.
- If you use synchronizedMap to implement a cache, you are introducing a potential scalability bottleneck into your application: only one thread can access the Map at once.
- You can improve concurrency of a class by using mutliple locks for disjoint data structures within the class. This approach normally comes at the cost that some methods will need to obtain multiple locks to operate.
- java.util.concurrent.ConcurrentHashMap is a thread-safe implementation of Map that offers better concurrency than synchronizedMap for both multiple-readers and multiple-writers.
- java.util.concurrent.ConcurrentReaderHashMap is a thread-safe implementation of Map that offers better concurrency than synchronizedMap for both multiple-readers and single-writers.
- java.util.concurrent.CopyOnWriteArrayList is intended as a replacement for ArrayList in concurrent applications where traversals greatly outnumber insertions or removals, e.g. when ArrayList is used to store a list of listeners.
- If you are using an ordinary ArrayList to store a list of listeners, as long as the list remains mutable and may be accessed by multiple threads, you must either lock the entire list during iteration or clone it before iteration, both of which have a significant cost. CopyOnWriteArrayList instead creates a fresh copy of the list whenever a mutative operation is performed.
Continuous Performance (Page last updated 2003 Jul, Added 2003-08-26, Author Cliff Sharples, Publisher DevX). Tips:
- Long-running queries, unnecessary executions, excessive results sets, and other performance issues usually don't surface until the acceptance testing phase - the fifth stage of development - or later.
- Programmers can spend weeks finding and fixing performance problems, sometimes spending as much as 20 percent of the development process.
- Sluggish performance can lead to design changes, which is increasingly difficult as the codebase grows.
- Performance problems can lead to late delivery or shipping poorly performing code.
- The more closely an application adheres to performance specs as lines of code mount, the easier it is to manage performance throughout the development process.
- Include performance in the requirements definition.
- Work performance testing into the development timeline.
- Conduct performance tests on a regular basis, at least nightly.
- Constantly monitor performance activity.
- Track the performance history as the application grows.
- Use performance history data to get a fix on the code responsible for bottlenecks, and use that knowledge to efficiently tune only the code that caused the performance problem.
Compilation speed, exceptions, and heap size (Page last updated 2003 Jul, Added 2003-08-26, Authors Jack Shirazi, Kirk Pepperdine, Publisher IBM). Tips:
- Jikes is designed as a fast Java compiler and can speed up your compilation process.
- Throwing new exceptions is expensive, specifically because creating a new exception calls Throwable.fillInStackTrace() which takes a full stack trace (which is slow).
- Throwing pre-existing Exception objects can avoid Throwable.fillInStackTrace() and is more efficient (but loses the contextual stack trace).
- Don't let your heap size become larger than available RAM (real memory), or paging can occur which will enormously slow performance.
Deciding between iterators and lists for returned values (Page last updated 2003 Jul, Added 2003-08-26, Author Ryan Brase, Publisher builder.com). Tips:
- Using an iterator to access elements of a list, rather than directly accessing list elements, allows you to start manipulating data before the full collection is available, e.g. as with ResultSets.
- Iterators which support accessing the underlying collection elements without first completing building the underlying collection, supports efficient access to data sources that produce data slowly.
Performance Tuning To Make WebSphere App Servers Sing (Page last updated 2003 June, Added 2003-08-26, Author Gian Trotta, Publisher Candle). Tips:
- Always use connection pooling.
- Obtain and close connections in the same method.
- Develop separate methods for expensive JNDI lookups.
- Don't declare connections as static objects.
- Don't close connections in the finalize method.
- Close any connections you open.
- Don't manage data access in Container Managed Persistence beans.
- Use Prepared Statements as opposed to regular or callable ones.
- Enable session persistence (incorporate java.io.Serializable into your code) and keep session size down to 2K or smaller (once sessions hit 4-5K, "your system basically starts choking.")
- Choose your JDBC driver wisely.
- Pick a solid Performance/Monitoring Tool.
The Necessity of Performance Profiling (Page last updated 2002 Jul, Added 2003-08-26, Author Peter Varhol, Publisher JavaPro). Tips:
- Code in one part of an application could well affect the performance in another part, even running on a different server, and the relationship between the two may not be evident. Finding that relationship by observing and stepping through execution may be difficult and will certainly be tedious. Profiling is essential to productively identify bottlenecks.
- Performance bottlenecks can occur for three reasons: poor or inefficient code at a critical point in the execution; and because the order of execution hasn't taken into account the time required to perform individual steps, for example it may be more efficient to open a database connection a single time per session rather than multiple times; and finally that the code isn't scalable.
- The most obvious place to find a bottleneck is in a database call, typically caused from how the code uses the database rather than inefficiencies in the database.
- Spinning off many threads and blocking an activity until they return can inadvertently affect the performance of the entire application.
- In more complex applications identifying a potential bottleneck before all the pieces of the application come together might be impossible.
- Determining just what information each component needs, and how it will get that information through well-defined interfaces, can help ensure that data mismatches don't contribute to bottlenecks.
- Benchmarking, comparing the results from a previous version of the application, lets you see how changes made with subsequent versions of the code have affected the performance of your application.
- Load testers simulate heavy loads and help to performance profile systems.
Back to newsletter 033 contents
Last Updated: 2017-11-28
Copyright © 2000-2017 Fasterj.com. All Rights Reserved.
All trademarks and registered trademarks appearing on JavaPerformanceTuning.com are the property of their respective owners.
Java is a trademark or registered trademark of Oracle Corporation in the United States and other countries. JavaPerformanceTuning.com is not connected to Oracle Corporation and is not sponsored by Oracle Corporation.
RSS Feed: http://www.JavaPerformanceTuning.com/newsletters.rss
Trouble with this page? Please contact us