Precise Java Google
Home  | J2SE  | J2EE  | Book  | Free Books  | Contact
EJB  | JDBC  | Servlets  | JSP  | Patterns  | JMS

Best Practices to improve performance in Servlets

This topic illustrates the performance improvement best practices in Servlets with the following sections:

Overview of Servlets

Servlets represent an extension to the HTTP server. It takes http request as an input and sends the http response to the client as an output. Servlet API provides two packages they are javax.servlet and javax.http.servelt. These packages contain interfaces and classes to deal with generic and http functionality that means you can write a Servlet in java to get http request and send a http response to the client. Client is typically a browser. These interfaces are implemented by Servlet Engines. There are numerous vendors who provide Servlet Engines to work with Servlets, for example Tomcat, weblogic, webshpere etc.

Servlet is loaded into the memory by Servlet Engine and it calls init() method on first request and then onwards only service() method is called for every other request by creating a separate thread for each request and finally destroy() method is called when the Servlet is removed by the Servlet Engine. Service() method can be replaced by doGet() or doPost() method.

Note that this architecture is a multi threaded model which is generally followed in most of the applications. You can even work with single threaded model by implementing SingleThreadModel interface where the Servlet Engine creates a separate Servlet instance for each request.

Note1: This is brief explanation of Servlet architecture. For more information on Servlets, see available books and online tutorials on Servlets.

Note2: This Section assumes that reader has some basic knowledge of Servlets. 

 

Use init() method as cache

The default mechanism of a Servlet Engine is to load a Servlet in multithreaded enviroment. In this environment, a Servlet init() method is called only once in its life time. You can improve performance using init() method. You can use this method to cache static data.

Generally a Servlet generates dynamic data and static data. Programmers often make a mistake by creating both dynamic and static data from service() method. Obviously there is a reason to create dynamic data because of its nature but there is no need to create static data every time for every request in service() method.

For example, normally you would write a Servlet like this

public void service(HttpServletRequest req, HttpServletRespnse res) throws ServletException,IOException {

res.setContentType(“text/html”);

Printwriter out = req.getWriter();

out.print("<html>”);

out.print("<head><title>Hello world</title></head>”);

out.print(“<body>”);

// send the dynamic data here

out.print(“</body>”);

out.print("</html>”);

}

Here you are generating both static data and dynamic data. Instead you can modify the code as shown below

public class servlet extends HttpServlet {

            byte[] header;

            byte[] navbar;

            byte[] footer;

            byte[] otherStaticData;

public void init(ServletConfig config) throws ServletException{

//create all the static data here

StringBuffer sb = new StringBuffer(); // better to initialize the StringBuffer with some size to improve performance

sb.append("<html>”);

sb.append("<head><title>Hello world</title></head>”);

sb.append(“<body>”);

header = sb.toString().getBytes();

// do same for navbar if its data is static

// do same for footer if its data is static

}

public void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {

res.setContentType("text/html");

ServletOutputStream out = res.getOutputStream();

out.write(header);

out.write(navbar);                               

// write dynamic data here

out.write(footer);

}

Here the static data is created in init() method which means that it is created only once in the life time of Servlet and it is used in service() method to pass the data to the client. When you send a large amount of static data, then you can use this technique to see a considerable increase in performance.

 

Optimization techniques in service() method

When you write a service() method for your Servlet, you can improve performance by using following techniques.

 

1. Use StringBuffer rather than using + operator when you concatenate multiple strings

2. Use print() method instead of println() method

3. Use ServletOutputStream instead of PrintWriter

4. Initialize the PrintWriter with proper size

5. Flush the data partly

6. Minimize the amount of code in  the synchronized block

7. Set the content length

 

1. Use StringBuffer for concatenation rather than using + operator when you concatenate multiple strings. See Concatenating Strings for detailed information.

2. println() method internally calls print() method and there is no need for new line separation when generating html pages. So a small overhead of calling one more method is reduced if you use print() method directly.

3. There is a small overhead involved in PrintWriter because it is meant for character output stream and it encodes data to bytes, rather you can directly use ServletOutputStream whenever you want to send binary data.

4. The better way of creating PrintWriter is

ByteArrayOutputStream byteArray = new ByteArrayOutputStream(12288);

PrintWriter out = new PrintWriter(byteArray);

This increases the size of buffer in the PrintWriter.

5. If you want to pass the large data to the client from your servlet, user may need to wait till the ServletOutputStream or PrintWriter flushes the data. This happens generally whenever you have a number of gifs per page that you want to pass to the client. The better approach is to flush the data partly using flush() method rather than flushing whole data at a time. You can initially flush header, then navigation bar, then body content and finally footer so that the user need not wait for whole data and he sees the header data immediately and so on with navigation bar, body content and footer.

ServletOutputStream out = res.getOutputStream();

out.write(header);

out.flush(); // flush the header

out.write(navbar);                          

out.flush(); // flush the navigation bar

// write dynamic data here

out.flush(); // flush the dynamic data

out.write(footer);

out.flush(); // finally flush the footer

 

6. Minimize the amount of code in the synchronized block that is used in the service method whenever you want to the data to be thread safe.

for example, you may write like this

synchronized(this){

code line1;

code line2;

code line3;

code line4;

}

But here you might want to synchronize the data in the code line2 instead of all the lines so the better way is

code line1;

synchronized(this){code line2;}

code line3;

code line4;

This reduces the overhead of locking all the code lines.

7. Whenever the client, such as a browser requests a page it establishes socket connection internally with the web server to get the requested content. Client gets the page data with multiple connections depending on the size of the data. If the data is more, such as with multiple gifs then it needs to establish multiple connection to get the data. The number of connections established will depend upon default content length of the header, and size of the content to be traversed from web server to the client. By increasing content length, you can reduce traffic and increase the performance. Here is an example

response.setContentLength(int contentSize);

This method sets the length of the content that the server can send to client by using Content-Length header.

 

Optimization techniques in destroy() method

The destroy() method is called only once in its servlet life time when the Servlet Engine removes from memory. It is always better to remove instance variable resources such as JDBC connections, sockets and other physical resources in this method. to avoid memory leaks.

 

Cache the static and dynamic data

The use of caching in different areas of your application gives very good performance. Generally every application's database schema will have at least some read only tables. There is no need of accessing these tables every time. You can cache that data in memory and reuse it instead of accessing database every time. It reduces network traffic, consumes less CPU cycles and gives good performance.

Caching can be done in three flavors namely static data caching, semi dynamic data caching and dynamic caching. Static data means that the data doesn't change in its life time, it always constant. Semi dynamic data means that the data changes but not often. For example the data that changes after every one hour can be called as semi dynamic data, the data does not change for every request. Dynamic data means that it changes the data for every request. Often people use the word dynamic data for semi dynamic data as well. So even I followed the same terminology.  In this section, dynamic data is synonymous with semi dynamic data.

It is best to cache static data and dynamic data in order to improve performance. We will discuss a few caching techniques to improve Servlet performance. They are as follows

1. Utilizing Browser caching

2. Caching dynamic data at the server

3. Utilizing application server caching facilities

4. Utilizing Servlet API's built in facility, HttpSession and ServletContext objects

As we saw above, Caching at init() method is useful for caching static data and it reduces the creation time of static data for every request but any way finally we are passing data to the client on every request. This type of caching is useful when you want to pass both static data and dynamic data to the client. One more caching technique is utilizing the browser cache and also cache the content at the server, this can be done by avoiding a call to service() method if the output content is not changed. This technique is achieved by implementing getLastModified() method of HttpServlet class.

Web servers send the response with a 'Last-Modified' header which tells the client when the page was last changed. Web browser sends a request with 'If-Modified-Since' header which tells web server when it last downloaded the page. With this communication server can predict whether the file has changed or not, if not it sends response with 'Not Modified'-304 status code so that the browser uses its cache page instead of downloading fresh page. In order to take advantage of this technique, Servlet should implement the getLastModified() method to tell the servlet engine about last modified time. This method returns time in milliseconds.

For implementation of this technique. See,

http://www.servlets.com/soapbox/freecache.html

The third technique is that your application server may support caching facility for dynamic data. All you need to do is that configure the caching properties file which is supported by your server. You can give what Servlet you need to cache and session time out value to remove cache content. For example Websphere application server supports this type of facility. Have a look at this link

http://www-4.ibm.com/software/webservers/appserv/doc/v35/ae/infocenter/was/0606000401.html

The fourth technique is that you can use Servlet API's HttpSession and ServletContext objects for caching. HttpSession object is available for a user session across multiple requests and ServletContext object is available for all the users using the application. You can set cacheable objects into these objects and get those objects whenever you require within their scope. The methods that support caching are:

ServletContext.setAttribute(String name, Object cacheableObject);

ServletContext.getAttribute(String name);

HttpSession.setAttribute(String name, Object cacheableObject);

HttpSession.getAttribute(String name);

 

Choosing the right session mechanism

We use session mechanism to maintain client state across multiple pages. The session starts when the client, such as browser requests for a URL from the web server and it ends when the web server ends the session or web server times out the session or user logs out or user closes the browser.

There are few approaches available to maintain the session, they are using

1. HttpSession provided by servlet API

2. Hidden fields

3. Cookies

4. URL rewriting

5. Persistent mechanism

Obviously it is difficult to select one mechanism out of above mentioned approaches to maintain session data. Each one impacts performance depending on amount of the data to be stored as session data and number of concurrent users.

The following table gives you an idea of performance based on the approach used.

 

Session mechanism Performance Description
HttpSession good There is no limit on size of keeping session data
Hidden fields moderate There is no limit on size of passing session data
Cookies moderate There is a limit for cookie size
URL rewriting moderate There is a limit for URL rewriting
Persistent mechanism moderate to poor There is no limit of keeping session data

Here the Persistent mechanism means that you store the session data in the database, file storage or any other persistent storage. There are few a approaches for this mechanism, they are

1. Using your application server's persistent mechanism for session data storage

2. Using your own persistent mechanism by maintaining your own database schema

If you use the first approach, generally application server converts the session objects into BLOB data type and stores in the database. If you use second approach, you need to design the schema as per your session fields and need to store the session data by writing JDBC code for that, this gives better performance than the first approach because conversion of session object to BLOB takes more time than directly storing session data. Either of persistent mechanisms give moderate to poor performance when compared to other approaches because of overhead involved in database calls through JDBC and it makes calls to database on every request in order to store that session data and finally it needs to retrieve the whole session data from database but it scales well on increasing session data and concurrent users.

URL rewriting gives moderate performance because the data has to pass between the client and server for every request but there is a limitation on amount of data that can be passed through URL rewriting. It gives moderate performance because of overhead involved in network for passing data on every request.

Cookies also give moderate performance because they need to pass the session data between client and server. It also has the size limit of 4k for each cookie.

Like URL rewriting and Cookies, Hidden fields need to pass the data between client and server. All these three session mechanisms give moderate performance and is inversely proportional to the amount of session data.

HttpSession mechanisms gives better performance when compared to other mechanims because it stores the session data in memory and reduces overhead on network. Only session id will be passed between client and the server. But it does not scale well when the session data is huge and the concurrent number of users are more because of increase in memory overhead and also increase in overhead on garbage collection.

Remember that choosing the session mechanism from one of the above approaches is not only depends on performance, scalability and security. The best approach is to maintain a balance between performance, security and scalability by choosing a mixed approache. Mixture of  HttpSession mechanism and Hidden fields gives both performance and scalability. By putting secure data in HttpSession and non secure data on hidden fields you can achieve better security.

 

Control HttpSession

If you decided to use HttpSession for your session tracking, then you need to know how your application server/servlet engine implements HttpSession mechanism. You need to take care of the following points.

1. Remove session explicitly

2. Set Session time out value

3. Application server/servelt engine implementation

Generally, your application server/servlet engine will have default session time out value as 30 minutes which means that if you don't remove session or manipulate that session for 30 minutes then your servlet engine removes that session from memory. If you set long session time out value such as 1 hour, then it keeps all the session objects till 1 hour. This approach effects scalability and performance because of overhead on memory and garbage collection. In order to reduce memory overhead and to increase performance, it is better to remove/invalidate session explicitly using HttpSession.invalidate() method. and also try to reduce the session time out value as per your application's requirement.

Third important point is that your application server may serialize session objects after crossing certain memory limit, It is expensive and effects performance because it not only serializes the single session object but also serializes the total object hierarchy. Use 'transient' for variables to avoid unnecessary serialization. see Serialization for detailed information to improve performance. So know about your application server/servlet engine session implementation mechanism and act accordingly.

 

Disable Servlet auto reloading

Most of the application servers/servlet engines have the capability of loading servlets dynamically, that means you need not restart your server whenever you change the servlet content. Application server/servlet engine loads the servlet with auto reload each time when you configure the servlet. For example, if you configure auto reload as 1 second, then servlet engine loads that servlet after every 1 second. This feature is good at development time because it reduces the development time by avoiding restarting the server after every change in servlet. But it gives poor  performance at production by unnecessary servlet loading and burden on class loader. So turn off your auto reloading feature in the configuration file to improve performance.

 

Control Thread pool

Servlet engine creates a separate thread for every request and assigns that thread to service() method in its multithreaded servlet and finally it removes that thread after completion of service() method execution. It happens for every request. Your servlet engine may create a new thread for every request by default. This default behavior reduces performance because creating and removing threads is expensive. This can be avoided by using the thread pool. Servlet engine creates pool of threads at start up and assigns a thread from pool to every request instead of creating a fresh thread every time and it returns that thread to the pool after completion. The size of the thread pool depends upon configuration parameters of the pool. The pool will have minimum and maximum number of threads and you can configure these numbers in the configuration file of your servlet engine. The number of maximum and minimum threads in pool depend upon concurrent users for your application. You have to estimate number of concurrent users for your application and give the thread pool size based on that. Obviously there is a limit on thread pool which depends on your hardware resources. By setting thread pool size correctly, The performance of servlet improves significantly. Your application server/ JSP engine may not have facility to configure thread pool. Tomcat's Servlet Engine has the facility to configure thread pool.  Look at your application server / servlet engine documentation for information about thread pool.

 

Key Points

  1. Use init() method to cache static data
  2. Use StringBuffer rather than using + operator when you concatenate multiple strings
  3. Use print() method rather than println() method
  4. Use ServletOutputStream rather than PrintWriter to send binary data
  5. Initialize the PrintWriter with proper size
  6. Flush the data partly
  7. Minimize code in the synchronized block
  8. Set the content length
  9. Release resources in destroy() method.
  10. Implement getLastModified() method to use browser cache and server cache
  11. Use application server caching facility
  12. Use Mixed session mechanisms such as HttpSession with hidden fields
  13. Remove HttpSession objects explicitly in your program whenever you finish the task
  14. Reduce session time out value as much as possible
  15. Use 'transient' variables to reduce serialization overhead if your HttpSession tracking mechanism uses serialization process.
  16. Disable servlet auto reloading feature.
  17. Use thread pool for your servlet engine and define the size as per application requirement.

 

Feed back

We appreciate and welcome your comments on this section. Email commentsZZZ@precisejavaZZZ.com (remove ZZZ which is placed to prevent spam). Please note that we may not be able to reply to all the emails due to huge number of emails that we receive but we appreciate your comments and feedback.

 





Copyright © 2001-2005, Ravi Kalidindi and Rohini Kalidindi. All rights reserved.