Published on Oct 10 2014 in Java Tomcat

Using Tomcat virtual hosts with the same appBase can cause unnecessary resource drain mainly due to excessive WebappClassLoader instances. See how to save heap memory by using separate appBase for each Host.

In the example setup there were localhost and 5 other virtual Hosts defined in server.xml. All were using appBase="webapps". There were frequent OutOfMemory errors so we decided to make heap dump and look into it with MAT (Eclipse Memory Analyzer). The dump was made on the server with:

jmap -dump:format=b,file=heap.bin `pgrep -u $UID java`

and downloaded to a local PC.

After opening it in MAT (Eclipse Memory Analyzer) 'File - Open Heap Dump - Leak Suspects Report' we saw in MAT - Reports - Leak Suspects:

66 instances of "org.apache.catalina.loader.WebappClassLoader", loaded by "org.apache.catalina.loader.StandardClassLoader @ 0xe821d800" 
occupy 91,210,920 (34.09%) bytes.

Biggest instances:

 * org.apache.catalina.loader.WebappClassLoader @ 0xe82f2f90 - 5,607,928 (2.10%) bytes.
 * org.apache.catalina.loader.WebappClassLoader @ 0xf1e72720 - 4,927,568 (1.84%) bytes.
 * org.apache.catalina.loader.WebappClassLoader @ 0xf31ae2b0 - 4,864,952 (1.82%) bytes.
 * org.apache.catalina.loader.WebappClassLoader @ 0xf407e3f0 - 4,826,728 (1.80%) bytes.
 * org.apache.catalina.loader.WebappClassLoader @ 0xea8254a0 - 4,818,560 (1.80%) bytes.
 * org.apache.catalina.loader.WebappClassLoader @ 0xf54cf4c0 - 4,803,360 (1.80%) bytes.
 * org.apache.catalina.loader.WebappClassLoader @ 0xed983160 - 4,798,280 (1.79%) bytes.
 * org.apache.catalina.loader.WebappClassLoader @ 0xeca10008 - 4,729,264 (1.77%) bytes.
 * org.apache.catalina.loader.WebappClassLoader @ 0xe94cbb90 - 4,711,664 (1.76%) bytes.
 * org.apache.catalina.loader.WebappClassLoader @ 0xf0f698d0 - 4,687,168 (1.75%) bytes.
 * org.apache.catalina.loader.WebappClassLoader @ 0xeed13420 - 4,683,400 (1.75%) bytes.
 * org.apache.catalina.loader.WebappClassLoader @ 0xefc0a930 - 4,664,488 (1.74%) bytes.
 * org.apache.catalina.loader.WebappClassLoader @ 0xeb74c960 - 4,607,632 (1.72%) bytes. 
Keywords
org.apache.catalina.loader.WebappClassLoader
org.apache.catalina.loader.StandardClassLoader @ 0xe821d800

66 instances of "org.apache.naming.resources.FileDirContext", loaded by "org.apache.catalina.loader.StandardClassLoader @ 0xe821d800" 
occupy 39,786,920 (14.87%) bytes.

Keywords
org.apache.naming.resources.FileDirContext
org.apache.catalina.loader.StandardClassLoader @ 0xe821d800

See also Details section in below summary screenshot.

Eclipse_Memory_Analyzer1
$CATALINA_HOME/work contained the following directories: host1/, host2/, host3/, localhost/, host4/, host5/ and in each of these directories there were subdirectories for each of the web applications deployed under webapps (because of common appBase for all the Hosts). So looks like there was unnecessary memory overhead with creating class loaders for all web applications in each Host while only 1 of the applications was used in each of hosts (hostX) and 5 web applications were used in localhost including docs, host-manager and manager.

We then moved each application being served under a domain to its own appBase and restarted the Tomcat. The new heap dump has been performed and MAT then showed:

16 instances of "org.apache.catalina.loader.WebappClassLoader", loaded by "org.apache.catalina.loader.StandardClassLoader @ 0xe8399b08" 
occupy 21,629,256 (29.27%) bytes.

Biggest instances:

 * org.apache.catalina.loader.WebappClassLoader @ 0xeb353eb0 - 4,911,384 (6.65%) bytes.
 * org.apache.catalina.loader.WebappClassLoader @ 0xe9130ba0 - 4,838,248 (6.55%) bytes.
 * org.apache.catalina.loader.WebappClassLoader @ 0xe85857c8 - 4,489,944 (6.08%) bytes.
 * org.apache.catalina.loader.WebappClassLoader @ 0xeb1222b0 - 850,496 (1.15%) bytes.
 * org.apache.catalina.loader.WebappClassLoader @ 0xeadfb508 - 825,552 (1.12%) bytes. 
Keywords
org.apache.catalina.loader.WebappClassLoader
org.apache.catalina.loader.StandardClassLoader @ 0xe8399b08

16 instances of "org.apache.naming.resources.FileDirContext", loaded by "org.apache.catalina.loader.StandardClassLoader @ 0xe8399b08" 
occupy 9,644,376 (13.05%) bytes.

Keywords
org.apache.naming.resources.FileDirContext
org.apache.catalina.loader.StandardClassLoader @ 0xe8399b08

And the summary screen follows - compare Deatils section.

Eclipse_Memory_Analyzer3
To sum up, after the changes we managed to save 70MB with org.apache.catalina.loader.WebappClassLoader and 30MB with org.apache.naming.resources.FileDirContext not counting any other savings in other classes. A total of 100MB+ saving just by modifying appBase in server.xml and moving web apps into dedicated appBase.

Lesson to learn: use separate appBase for each virtual host in Apache Tomcat.