Once you've got an idea how your application and Tomcat instance respond to load, you can begin some performance tuning. There are two basic categories of tuning detailed here:
- External tuning
Tuning that involves non-Tomcat components, such as the operating system that Tomcat runs on and the Java virtual machine running Tomcat.
- Internal tuning
Tuning that deals with Tomcat itself, ranging from changing settings in configuration files to modifying the Tomcat source code. Modifications to your web application also fall into this category.
Tomcat doesn't run directly on a computer; there is a JVM and an operating system between it and the underlying hardware. There are relatively few complete and fully compatible Java virtual machines to choose from for any given operating system and architecture combination, so most people will probably stick with Sun's or their own operating system vendor's implementation.
If your goal is to run the fastest Java runtime and squeeze the most performance out of your webapp, you should benchmark Tomcat and your webapp on each of the Java VMs that are available for your hardware and operating system combination. Do not assume that the Sun Java VM is going to be the fastest because that is often not the case (at least in our experience). You should try other brands and even different major version numbers of each brand to see what runs your particular webapp fastest.
If you choose just one version of the Java class file format that JVMs you use must support (for example, you want to compile your webapp for Java 1.6 JVMs), you can benchmark each available JVM brand that supports that level of the bytecodes, and choose one that best fits your needs. For instance, if you choose Java 1.6, you could benchmark Sun's 1.6 versus IBM's 1.6 versus BEA's 1.6. One of these will run Tomcat and your webapp the fastest. All of these brands are used in production by a large number of users and are targeted at slightly different user bases. See Appendix A for information about some of the JDKs that may be available for your operating system.
As a generic example of performance improvements between major versions of one JVM brand, a major version upgrade could buy you a 10 percent performance increase. That is, upgrading from a Java 1.5 JVM to a Java 1.6 JVM your webapp may run 10 percent faster, without changing any code in it whatsoever. This is a ballpark figure, not a benchmark result; your mileage may vary, depending on the brands and versions you test and what your webapp does.
It is likely true that newer JVMs have both better performance and less stability, but the longer a major version of the JVM has been released as a final/stable version, the less you have to worry about its stability. A good rule of thumb is to get the latest stable version of the software, except when the latest stable version is the first or second stable release of the next major version of the software. For example, if the latest stable version is 1.7.0, you may opt for 1.6.29 instead if it is more stable and performs well enough.
It is often the case that people try to modify the JVM startup switches to make their Tomcat JVM serve their webapp's pages faster. This can help, but does not usually yield a high percentage increase in performance. The main reason it does not help much: the JVM vendor did their own testing before releasing the JDK, found which settings yield the best performance, and made those settings the defaults.
If you change a JVM startup switch to activate a setting that is not the default, chances are that you will slow down your JVM. You have been warned! But, in case you would like to see which Sun JVM settings you could change, have a look at http://www.md.pp.ru/~eu/jdk6options.html.
One exception here is the JVM's heap memory allocation. By default, vendors choose for
the JVM to start by allocating a small amount of memory (32 MB in the Sun JVM's case), and
if the Java application requires more memory, the JVM's heap size is reallocated larger
while the application is paused. The JVM may do this a number of times in small memory
increments before it hits a heap memory size ceiling. Because the application is paused
each time the heap size is increased, performance suffers. If that is happening while
Tomcat is serving a webapp's pages, the page responses will appear to take far longer than
normal to all web clients whose requests are outstanding at the time the pause begins. To
avoid these pauses, you can set the minimum heap size and the maximum heap size to be the
same. That way, the JVM will not attempt to expand the heap size during runtime. To do
this to Tomcat's JVM startup switches, just set the
JAVA_OPTS environment variable to something such as
-Xms512M -Xmx512M. (This means that the maximum and minimum heap size should
be set to
512 MB.) Set the size to an appropriate value
on your machine, based on how much memory it has free after it boots.
You can also try benchmarking different garbage collection algorithm settings,
however, as we stated earlier you may find that the default settings are always fastest.
You never know until you benchmark it, though. Check the documentation for the JVM you're
benchmarking to find the startup switch that will enable a different garbage collection
algorithm because these settings are JVM implementation-specific. Again, you'll want to
set it in
JAVA_OPTS to get Tomcat to start the JVM that
And what about the OS? Is your server operating system optimal for running a large, high-volume web server? Of course, different operating systems have very different design goals. OpenBSD, for example, is aimed at security, so many of the limits in the kernel are set small to prevent various forms of denial-of-service attacks (one of OpenBSD's mottoes is "Secure by default"). These limits will most likely need to be increased to run a busy web server.
Linux, on the other hand, aims to be easy to use, so it comes with the limits set higher. The BSD kernels come out of the box with a "generic" kernel, that is, most of the drivers are statically linked in. This makes it easier to get started, but if you're building a custom kernel to raise some of those limits, you might as well rip out unneeded devices. Linux kernels have most of the drivers dynamically loaded. On the other hand, memory itself is getting cheaper, so the reasoning that led to loadable device drivers is less important. What is important is to have lots and lots of memory and to make a lot of it available to the server.
Memory is cheap these days, but don't buy cheap memory—brand name memory costs only a little more and repays the cost in reliability.
If you run any variant of Microsoft Windows, be sure you have the server version (e.g., Windows Vista Server instead of just Windows Vista Pro). In other nonserver versions, the end user license agreement and/or the operating system's code itself may restrict the number of users, or the number of network connections that you can use, or place other restrictions on what you can run. Additionally, be sure you obtain the latest Microsoft service packs frequently, for the obvious security reasons (this is true for any system, but is particularly important for Windows).