Performance tuning servlets requires a slightly different mindset than performance tuning normal Java applications or applets. The reason is that the JVM running the servlets is expected to simultaneously handle dozens, if not hundreds, of threads, each executing a servlet. These coexisting servlets have to share the resources of the JVM in a way that normal applications do not. The traditional performance-tuning tricks still apply, of course, but they have a different impact when used in a heavily multithreaded system. What follows are some of the tricks that have the largest special impact on servlet developers.
Avoid the unnecessary creation of objects. This has always been good advice--creating unnecessary objects wastes memory and wastes a fair amount of time as the objects are created. With servlets, it's even better advice. All but the most recent JVMs have a global object heap that must be locked for each new memory allocation. While any servlet is creating a new object or allocating additional memory, no other servlet can do so.
Avoid concatenating several strings together. Use the append() method of StringBuffer instead. This too has always been good advice, but with servlets it's particularly tempting to write code like this to prepare a string for later output:
String output; output += "<TITLE>"; output += "Hello, " + user; output += "</TITLE>";
Although this code looks nice and neat, when it runs it executes as if written roughly as follows, with a new StringBuffer and new String created on each line:
String output; output = new StringBuffer().append("<TITLE>").toString(); output = new StringBuffer(output).append("Hello, ").toString(); output = new StringBuffer(output).append(user).toString(); output = new StringBuffer(output).append("</TITLE>").toString();
When efficiency counts, rewrite the original code to look like the following, so just one StringBuffer and one String are created:
StringBuffer buf = new StringBuffer(); buf.append("<TITLE>"); buf.append("Hello, ").append(user); buf.append("</TITLE); output = buf.toString();
Note that using an array of bytes is even more efficient.
Synchronize whenever necessary, but no more. Every synchronized block in a servlet slows the servlet's response time. Because the same servlet instance may handle multiple concurrent requests, it must, of course, take care to protect its class and instance variables with synchronized blocks. All the time one request thread is in a servlet's synchronized block, however, no other thread can enter the block. Therefore, it's generally best to keep these blocks as small as possible.
You should also take a look at the worst-case result of thread contention. If the worst case is bearable (as with the counter example from Chapter 3, "The Servlet Life Cycle"), you can consider removing synchronization blocks entirely. Also consider using the SingleThreadModel tag interface, where the server manages a pool of servlet instances to guarantee each instance is used at most by one thread at a time. Servlets that implement SingleThreadModel don't need to synchronize access to their instance variables.
Buffer your input and your output, all your storage files, any streams loaded from a database, and so on. This almost always improves performance, but the improvement can be especially profound with servlets. The reason is that reading and writing one unit at a time can slow down the entire server due to the frequent context switches that have to be made. Fortunately, you generally don't need to buffer when writing to a servlet's PrintWriter or ServletOutputStream or when reading from a servlet's BufferedReader or ServletInputStream. Most server implementations already buffer these streams.
Copyright © 2001 O'Reilly & Associates. All rights reserved.