Servlet and JSP performance tuning

Improve your enterprise application's performance by tweaking your servlets and JSPs

Do your J2EE applications run slow? Can they sustain rising traffic? This article describes performance-tuning techniques (PTT) for developing high performance and scalable JSP (JavaServer Pages) pages and servlets. That means building applications that are reasonably and consistently fast, and can scale up to the increasing number of users and/or requests. In this article, I walk you through the practical and proven performance-tuning techniques that will boost the performance of your servlets and JSP pages tremendously, thus improving the performance of your J2EE applications. Some of these techniques apply during the development phase, i.e., while you design your application and write the code. And some of these techniques are configuration-related.

PTT 1: Use the HttpServlet init() method for caching data

The server calls the servlet's init() method after the server constructs the servlet instance and before the servlet handles any requests. It is called only once in a servlet's lifetime. init() can be used to improve performance by caching the static data and/or completing the expensive operations that need to be performed only during initialization.

For example, it is a best practice to use JDBC (Java Database Connectivity) connection pooling, which involves the use of the javax.sql.DataSource interface. DataSource is obtained from the JNDI (Java Naming and Directory Interface) tree. Performing the JNDI lookup for DataSource for every SQL call is expensive and severely affects an application's performance. Servlet's init() method should be used to acquire DataSource and cache it for later reuse:

public class ControllerServlet extends HttpServlet
{
   private javax.sql.DataSource testDS = null;
   public void init(ServletConfig config) throws ServletException
   {
      super.init(config);   
      Context ctx  = null;
      try
      { 
         ctx = new InitialContext();
         testDS = (javax.sql.DataSource)ctx.lookup("jdbc/testDS");
      }
      catch(NamingException ne)
      {
         ne.printStackTrace();              
       }
       catch(Exception e)
       {
          e.printStackTrace();
       }
   }
   public javax.sql.DataSource getTestDS()
      {
         return testDS;
      }
   ...
   ...   
}

PTT 2: Disable servlet and JSP auto-reloading

Servlet/JSP auto-reloading proves useful during the development phase because it reduces development time, as you do not have to restart the server after every change in the servlet/JSP. However, it is expensive in the production phase; servlet/JSP auto-reloading gives poor performance because of unnecessary loading and burdening on the classloader. Also, it may put your application in strange conflicts when classes loaded by a certain classloader cannot cooperate with classes loaded by the current classloader. So turn off auto-reloading for servlet/JSP in a production environment to receive better performance.

PTT 3: Control HttpSession

Many applications require a series of client requests so they can associate with one another. Web-based applications are responsible for maintaining such state, called a session, because the HTTP protocol is stateless. To support applications that must maintain state, Java servlet technology provides an API for managing sessions and allows several mechanisms for implementing sessions. Sessions are represented by an HttpSession object, but a cost is involved while using it. An HttpSession must be read by the servlet whenever it is used and rewritten when it is updated. You can improve performance by applying the following techniques:

  • Do not create HttpSessions in JSP pages by default: By default, JSP pages create HttpSessions. If you do not use HttpSession in your JSP pages, to save some performance overhead, use the following page directive to prevent HttpSessions from being created automatically when they are unnecessary in JSP pages:

    <%@ page session="false"%>
    
  • Do not store large object graphs inside an HttpSession: If you store the data in the HttpSession as one large object graph, the application server will have to process the entire HttpSession object each time. This forces Java serialization and adds computational overhead. The throughput decreases as the size of the objects stored in the HttpSession increases because of the serialization cost.
  • Release HttpSessions when done: Invalidate sessions when they are no longer needed using the HttpSession.invalidate() method.
  • Set session time-out value: A servlet engine has a default session time-out value set. If you do not either remove the session or use it for the time equal to the session time-out, the servlet engine will remove the session from memory. The larger the session time-out value, the more it affects scalability and performance because of overhead on memory and garbage collection. Try to keep the session time-out value as low as possible.

PTT 4: Use gzip compression

Compression is the act of removing redundant information, representing what you want in as little possible space. Using gzip (GNU zip) to compress the document can dramatically reduce download times for HTML files. The smaller your information's size, the faster it can all be sent. Therefore, if you compress the content your Web application generates, it will get to a user faster and appear to display on the user's screen faster. Not every browser supports gzip compression, but you can easily check whether a browser supports it and then send gzip-compressed content to only those browsers that do.

Here is the code snippet that shows how to send compressed content whenever possible:

public void doGet(HttpServletRequest request, HttpServletResponse response)
          throws IOException, ServletException 
{
          
   OutputStream out = null
   // Check the Accepting-Encoding header from the HTTP request.
   // If the header includes gzip, choose GZIP.
   // If the header includes compress, choose ZIP.
   // Otherwise choose no compression.
   String encoding = request.getHeader("Accept-Encoding");    
      
   if (encoding != null && encoding.indexOf("gzip") != -1)
   {
       response.setHeader("Content-Encoding" , "gzip");
       out = new GZIPOutputStream(response.getOutputStream());
   }
   else if (encoding != null && encoding.indexOf("compress") != -1)
   {
       response.setHeader("Content-Encoding" , "compress");
       out = new ZIPOutputStream(response.getOutputStream());
   } 
   else
   {
       out = response.getOutputStream();
   }
   ...
   ...                        
}  

PTT 5: Do not use SingleThreadModel

SingleThreadModel ensures that servlets handle only one request at a time. If a servlet implements this interface, the servlet engine will create separate servlet instances for each new request, which will cause a great amount of system overhead. If you need to solve thread safety issues, use other means instead of implementing this interface. SingleThreadModel interface is deprecated in Servlet 2.4.

PTT 6: Use thread pool

A servlet engine creates a separate thread for every request, assigns that thread to the service() method, and removes that thread after service() executes. By default, the servlet engine may create a new thread for every request. This default behavior reduces performance because creating and removing threads is expensive. Performance can be improved by using the thread pool. Depending on the expected number of concurrent users, configure a thread pool by setting the values for minimum and maximum number of both threads in a pool and increments. At startup, the servlet engine creates a thread pool with number of threads in a pool equal to the minimum number of threads configured. Then the servlet engine assigns a thread from the pool to every request instead of creating a new thread every time, and returns that thread to the pool after completion. Using the thread pool, performance can drastically improve. If needed, more threads can be created based on the values for maximum number of threads and increments.

PTT 7: Choose the right include mechanism

There are two ways you can include files in a JSP page: include directive (<%@ include file="test.jsp" %>) and include action (<jsp:include page="test.jsp" flush="true" />). The include directive includes the specified file's content during the translation phase; i.e., when the page converts to a servlet. The include action includes the file's content during the request processing phase; i.e., when a user requests the page. Include directive is faster than include action. So unless the included file changes often, use include directive for better performance.

PTT 8: Choose the right scope in useBean action

One of the most powerful ways to use JSP pages is in cooperation with a JavaBeans component. JavaBeans can be directly embedded in a JSP page using the <jsp:useBean> action tag. The syntax is as follows:

<jsp:useBean id="name" scope="page|request|session|application" class=
  "package.className" type="typeName">
</jsp:useBean>

The scope attribute specifies the scope of the bean's visibility. The default value for scope attribute is page. You should select the correct scope based on your application's requirements, otherwise it will affect application performance.

For example, if you need an object only for a particular request, but your scope is set to session, that object will remain in memory even after you are done with the request. It will stay in memory until you explicitly remove it from memory, you invalidate the session, or the session times out as per the session time-out value configured with the servlet engine. If you do not select the right scope attribute value, it will affect the performance because of overhead on memory and garbage collection. So set the exact scope value for the objects and remove them immediately when finished with them.

Miscellaneous techniques

  • Avoid string concatenation: The use of the + operator to concatenate strings results in the creation of many temporary objects because strings are immutable objects. The more you use +, the more temporary objects are created, which will adversely affect performance. Use StringBuffer instead of + when you concatenate multiple strings.
  • Avoid the use of System.out.println: System.out.println synchronizes processing for the duration of disk I/O, and that can slow throughput significantly. As much as possible, avoid the use of System.out.println. Even though sophisticated debugging tools are available, sometimes System.out.println remains useful for tracing purpose, or for error and debugging situations. You should configure System.out.println so it turns on during error and debugging situations only. Do that by using a final Boolean variable, which, when configured to false, optimizes out both the check and execution of the tracing at compile time.
  • ServletOutputStream versus PrintWriter: Using PrintWriter involves small overhead because it is meant for character output stream and encodes data to bytes. So PrintWriter should be used to ensure all character-set conversions are done correctly. On the other hand, use ServletOutputStream when you know that your servlet returns only binary data, thus you can eliminate the character-set conversion overhead as the servlet container does not encode the binary data.

Conclusion

The goal of this article was to present you with some of the practical and proven performance-tuning techniques that will boost the performance of servlets and JSP pages tremendously, thus improving the performance of J2EE applications. The next step would be to look into the performance-tuning techniques for other related technologies such as EJB (Enterprise JavaBeans), JMS (Java Message Service), and JDBC (Java Database Connectivity).

Rahul Chaudhary is a lead Java developer/architect at Dell. Although he has experience in many different languages, he has been deeply into Java over the past few years, and has extensive experience with Java, J2EE, and its related technologies. Programming is his passion. His interests include artificial intelligence and open source. Rahul has a bachelor's degree in electrical engineering and a master's degree in computer science.

Learn more about this topic

Join the discussion
Be the first to comment on this article. Our Commenting Policies