This topic illustrates the performance improvement best practices in Servlets with the following sections:
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.
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