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 June 2005
Get rid of your performance problems and memory leaks!
Get rid of your performance problems and memory leaks!
Back to newsletter 055 contents
Tuning Your Stress Test Harness (Page last updated June 2005, Added 2005-06-30, Author Kirk Pepperdine, Publisher TheServerSide). Tips:
- The common means of performing a stress test is to make repeated requests against the server in a tight loop - but this is likely to overload the server as the test is not representative of user activity.
- Stress tests are often defined in terms of client activity, but they should be viewed through a server-centric eye.
- The overall trend of times in a stress will be determined by the average case: convergent systems may have momentary buildups but the queue will empty out as the tendency of a convergent system is to move towards being idle; Divergent systems will continue to grow until reaching system boundaries and stay there.
- Experience tells us that many internet applications find that 10% of their users are active at any point in time.
- Define realistic stress tests: For example, if you have 1000 defined users, you might expect that only 100 will be using the system at any time - so your stress test should simulate 100 users that repeatedly perform some series of requests.
- If you let the simulation thread make requests as fast as they can, you have a situation where these threads are simulating the all possible users all making requests all at the same time - which is a much heavier load then would really occur.
- A divergent system can appear to be in a steady-state because you have limited the number threads making requests - the response time for each successive user will be longer than that experienced by the previous one, the average response time will continue to grow without bounds - until the artificial limited of the number of clients is reached and response time will stabilize. The net result is that your test is limiting your ability to determine the scaling characteristics of your system.
- You need to know the rate at which users/threads are producing requests.
- The sum of the rates from all users translates into the rate at which the server is receiving requests. Once you?ve established this value, you can make some adjustments to rate at which the harness makes requests per thread.
- You need to always be aware that a test harness that is starved for resources will limit your ability to test effectively.
- Measure both the request response time and the rate of requests handled by the system (throughput) at the same time to get a good measure of your system capacity.
- When the server is running at full capacity, more requests will simply enter a queue and wait for the server to service it, so the request response time will grow as the queue wait time is part of the response time.
- Always confirm that the stress test harness does not interfere with the test - a well configured harness will not cause you to measure things that you shouldn?t measure.
- The stress test harness should not flood the server with requests, there should be pauses between requests.
- Focus initially on getting the stress test harness working correctly, before actually using the measurements it gives to measure or tune the server.
Timing is Everything (Page last updated May 2005, Added 2005-06-30, Author Chet Haase, Publisher Sun). Tips:
- Class javax.swing.Timer fires one or more action events after a specified delay. For example, an animation object can use a javax.swing.Timer as the trigger for drawing its frames. Setting up a timer involves creating a Timer object, registering one or more action listeners on it, and starting the timer using the start method.
- Class java.util.Timer is a facility for threads to schedule tasks for future execution in a background thread. Tasks may be scheduled for one-time execution, or for repeated execution at regular intervals. each Timer object is a single background thread that is used to execute all of the timer's tasks, sequentially.
- If a java.util.Timer task takes excessive time to complete, it "hogs" the timer's task execution thread. This can, in turn, delay the execution of subsequent tasks, which may "bunch up" and execute in rapid succession when (and if) the offending task finally completes.
- After the last live reference to a java.util.Timer object goes away and all outstanding tasks have completed execution, the timer's task execution thread terminates gracefully (and becomes subject to garbage collection). However, this can take arbitrarily long to occur.
- The Timer utilities assume one-time or indefinite operation and must be manually stopped when you are finished with them.
- java.util.Timer is thread-safe: multiple threads can share a single Timer object without the need for external synchronization.
- If you rely on timer resolution, your application performance is not system independent - especially important for animation frame rates.
- The best way to handle animations so that they perform similarly across a wide variety of platforms is to base the animations on elapsed time instead of just the frequency of timing events.
- If you wanted your icon to move at a constant rate of 200 pixels/second, then you should calculate the number of seconds elapsed in the animation from left to right and multiply that number by 200 to get the proper x value.
- All platforms have different constraints on the resolution of their default timing systems, e.g. Thread.sleep() is dependent upon the resolution of the underlying timing system, as is a call to System.currentTimeMillis().
- One of the new features in JDK 1.5 is the new System.nanoTime() method. This uses, in general, a higher-resolution timing facility on the native platform. It returns a different number than System.currentTimeMillis() (for one thing, it's 1,000,000 larger since it's in nanoseconds; for another thing, it's a time value that is useful only when compared to other return values from that function).
- Even if you are stuck with a timer with a lower resolution than you think you need, calculating your values based on elapsed times should give you what you need. For example a frame rate of 5000 times per second is not possible with callbacks 60 times per second, but using elapsed times your icon will still look pretty smoothly animated at that lower rate, and it should look to the user as if it is moving at the same speed. It is just that the sprite's position will not be updated as often as you wanted.
Interview with Chet Haase (Page last updated March 2005, Added 2005-06-30, Author Martin Perez, Publisher javahispano). Tips:
- If you want to play with OpenGl drivers on your system, you can enable it with a runtime switch (-Dsun.java2d.opengl=true).
- In 5.0, most types of images created in any way possible are automatically accelerated. Once you start copying the image around, we check to see whether we can accelerate it and, if we can, we do so. You don't have to do anything different here; you just create the images you need, do the operations you want, and we will do the tedious work of making it faster.
- If you want to draw a filled rectangle, it is *much* faster to call fillRect() than drawLine(). There are so many factors that must be taken into account with lines (including endcaps, joins, and more general routines for drawing potentially diagonal lines), that it is far easier for Java2D to accelerate a simple rectangle fill than to draw a wide line.
- Performing time-intensive operations on the GUI thread is perhaps the biggest area of performance concern that we see in the Real World. This isn't so much "performance" as "perceived performance"; it's not necessarily a problem that the app takes a long time doing an operation, but that the user thinks the app is dead while that operation is happening. The best way to solve this problem is to use different threads to perform this (and other) non-GUI-related work.
- We do not up to 1.5.x accelerate anti-aliased text by default, so if you enable this feature, you may see that your application paints its text somewhat slower than it might otherwise.
- We enable enough hardware acceleration for simple 2D graphics in Java2D that you can write traditional sprite-based games and achieve performance that is on a par with native applications (check out http://ping.dev.java.net).
- The overhead inherent in the generalized Java3D scene graph can be more than such developers want. For this type of developer, the OpenGL bindings (JOGL) are probably more appropriate.
- The main frameworks to aid in game development are Swing/Java2D for basic 2D functionality, Java3D for a 3D scene-graph approach to 3D applications, and JOGL for OpenGL bindings for Java. There are also frameworks and other graphics APIs being developed outside of Sun that are worth investigating. Some of the ones that I know of include Lightweight Java Game Library (http://lwjgl.org) and Xith3D (http://xith.org). There are discussions on these and other gaming issues and frameworks at http://javagaming.org.
- JOGL is a great approach for developers that want low-level access to a hardware-accelerated 3D API. If you are trying to develop a simple game or graphics application and you are not familiar with OpenGL, this may not be the approach you want. But if you are a game developer interested in getting the latest 3D capabilities with the highest performance and the lowest to-the-metal access to the graphics resources, this is probably the best approach to use.
Build Java Apps that Can Multitask (Page last updated February 2005, Added 2005-06-30, Author Raghu Donepudi, Publisher DevX). Tips:
- J2SE 5.0 platform includes a new package of concurrency utilities: A high-performance, flexible thread pool; A framework for asynchronous execution of tasks; A host of collection classes optimized for concurrent access.
- The Executor framework provides simple standardized extensible classes for invocation, scheduling, and execution. It provides support for controlling asynchronous tasks according to a set of execution policies.
- The ExecutorService class provides methods for managing termination and tracking the progress of one or more asynchronous tasks.
- The ScheduledExecutorService class is very handy for scheduling tasks that run periodically.
- The Future interface or the FutureTask class lets you get a return value from the asynchronous execution of the thread. The Future interface provides methods to check if the computation is complete, retrieve the results of the computation, or cancel the computation.
- ThreadPoolExecutor provides many features for configuring and tuning a server including: Core and maximum poolsizes; On-demand construction; Keep-alive times; Queuing; Hook methods.
- ThreadPoolExecutor Queuing follows the following rules: If fewer than corePoolSize threads are running, the Executor always prefers adding a new thread rather than queuing; If corePoolSize or more threads are running, the Executor always prefers queuing a request rather than adding a new thread; If a request cannot be queued, a new thread is created unless this would exceed maximumPoolSize, in which case, the task will be rejected.
- The ConcurrentHashMap class provides full thread-safe concurrency support for retrievals and adjustable expected concurrency for updates.
- CopyOnWriteArraySet is a thread-safe variant of set, and CopyOnWriteArrayList is a thread-safe variant of ArrayList. Each makes a copy of the underlying array or sets it before modifying the original. As a result, the reads are fast but the updates are slow.
- JDK 1.5 provides advanced classes like Semaphore, CountDownLatch, CyclicBarrier, and an Exchanger for special-purpose synchronization.
Maximize J2EE and database interoperability for performance (Page last updated February 2005, Added 2005-06-30, Author Robert Maness, Publisher Javaworld). Tips:
- [Article includes an example of tracking a db communications performance problem]
- Use an APM tool [like IronEye] to display all the SQL statements issued by the application, sorted by response time, so you can see if any SQL statement is taking too long for the wrong reason (some SQL statements simply take a long time?such as a request for a list of all the transactions performed by the account for an entire year).
- Ask the following questions: Is this something that originates in the application tier? Who is invoking this statement? How many times?
- If an application invokes a SQL statement many times more than you expect, you may reasonably suspect that the application is at fault.
- You need to pinpoint the program component that must be investigated.
- Good APM tools can provide clear suggestions for dealing with a problem.
- It may be that the disk on which the table space is located has a bad response time, and a move to another disk is indicated.
- It may be that an index is missing, and you can speed data retrieval by creating a new index.
- Perhaps there are too many threads running in parallel on the database, and you need to do some partitioning to alleviate concurrency problems.
- Data retrieval may be taking too long because there's a long wait to get a database connection.
- You can query the application server to learn how many connections have been defined, compare that number with the typical number of concurrent requests, and quickly determine whether more connections are needed.
Back to newsletter 055 contents
Last Updated: 2017-10-01
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