17.2 Efficient Page Creation and Output

The following principles will help you optimize page creation and output and thus improve the performance of your servlet.

17.2.1 Minimize Output and Logging

Output slows down the servlet. Minimize your output as much as possible. If you are logging from your servlet, log only essential data and buffer the output. (Don't turn off logging completely; monitoring servlet performance is essential to maintaining good performance, and eliminating logging is counterproductive. Without logging, it is more difficult to determine if there is a performance problem and where it might be). A dynamically configurable logging framework, such as the java.util.logging package briefly covered in Chapter 8, or the open source Log4J, available from http://jakarta.apache.org/log4j/docs/index.html, is very helpful, as you can put in a great deal of logging and selectively turn on those log statements you need at runtime.

For both logging and page output, use the print( ) methods in preference to the println( ) methods, where appropriate. System.out, println( ) can cause output to be flushed, prematurely ending the effectiveness of buffer optimization. For HTML output, println( ) adds only nonsignificant whitespace to the output, adding overhead with no benefit. For JSPs, you can set the output buffer size with the directive <%@ page buffer="12kb" %> (or a similar amount).

17.2.2 Use Strings Efficiently

Time spent constructing HTML page output is significant for many servlets. Use efficient String manipulation techniques, as described in Chapter 5.

When you need to build strings internally, use StringBuffers or other efficient String or byte array-building mechanisms. Avoid generating intermediate Strings and other objects whenever possible. Avoid the + and += concatenation operators.

17.2.3 Use Bytes Versus Chars

HTML uses bytes, so you don't need to use chars unless it is required for your application. Simply using chars does not cause any overhead; the conversion between chars and bytes creates overhead (i.e., bytes are read and written on the socket).

17.2.4 Use Network Buffers Efficiently

You will output the results, and the output goes to a network buffer. Although the network buffer flushing is not under your control, it will be consistent for any one platform, so try to find its operational capabilities. Use the network buffer by using print( ) on partial strings rather than building the strings in memory and then writing them. However, the network stack can be suboptimal in flush timing. Tests by Acme Laboratories identified that the amount of data sent in the first network packet was crucial to optimal performance. Sending the response headers and the first load of data as a single packet instead of as two separate packets improved performance significantly (see http://www.acme.com/software/thttpd/benchmarks.html). Doing so may require building the data in memory, and then writing it in one chunk.

17.2.5 Display Static Pages Quickly

Static pages display more quickly than dynamic pages. You can gain improved performance by taking advantage of this fact, using static pages and page elements when possible.

Browsers take time to calculate how much space each element should take. Precalculate all formatting that is constant for all generated HTML pages. Use cached in-memory Strings or byte arrays to hold the portions of pages that are common to multiple pages. This should be faster than repeatedly generating the same elements. The headers are usually mostly the same, and most web sites have a look and feel that involves the same elements in many pages. Formatting precalculation is done automatically for JSP pages in the compilation phase.

High-volume web applications pre-render pages that are the same for all users. Those pages can be served directly from a separate web server optimized for serving static pages, taking away a significant load from the servlet.

Some complete or partial pages can become temporarily static. Cache these pages or sections, and regenerate them only when they need to change.

Even more efficient than returning a cached page is to tell the browser to use its own cached page. As part of its request, the browser can send a header telling the server that it has a cached copy of the requested page, including when the copy was cached. The server can reply that the cached copy is valid without resending the page, in which case the browser simply displays the page from its cache. This capability is supported by servlets through the getLastModified( ) method. Implement the getLastModified( ) method in your servlet to return the page's last modified timestamp and allow the browser to use its cached page when possible.

17.2.6 Optimize Data Conversions

Optimize data conversions that you need to make when generating your HTML output. For example, use timestamps instead of formatted Dates, or if you need to format a Date, don't do so from scratch each time. Instead, use a partially cached formatted Date and fill in the changed values. (The date changes only once a day, hours only change once an hour, etc.) There is seldom any requirement to display the current time in a page, and even when it is required, it cannot be accurate to the second because of download time and time differences between machines.

17.2.7 Use ServletOutputStream Directly (Servlets Only)

Use the ServletOutputStream directly to send binary data rather than wrapping the ServletOutputStream in a PrintWriter. JSPs cannot do this; they always use the PrintWriter, which is one of the reasons why a JSP may be slightly slower than the equivalent servlet. A JSP could forward to a plain servlet when binary data needs to be sent.

If you will use a PrintWriter, initialize its buffer in the constructor with the optimal size for the pages you write.

17.2.8 Optimize Partial Page Display

Flushing the HTML output in sections lets the browser display partial pages more quickly. As already mentioned, putting more than the header in the first section improves performance. But bear in mind that the browser can display partial pages if it has enough information, so try to send the page sections that help the browser display partial pages quickly. Explicitly flush those sections, rather than waiting for the network buffer to fill and flush the data, to give the user the impression of a faster display.