Java – Tomcat Session Eviction to Avoid OutOfMemoryError

javamemorysessionsession-timeouttomcat

We are running a vendor-supplied webapp in Tomcat 5.5 using the StandardManager for sessions (in memory). As sessions can get quite large (20M+), running out of heap space is a serious concern. Users want to keep sessions around for a couple of hours if possible but would rather evict sessions than run out of heap space. It does not appear that the vendor properly implemented Serializable in session'ed objects, so switching to the persistent session manager implementation isn't an option.

Tomcat allows one to set a maxActiveSessions property that will restrict the overall number of sessions in the manager. However, when that limit is reached, no new sessions may be created until some existing sessions expire. We want to clobber the least recently used sessions first.

Ideally, we would like to expire some not recently used sessions when heap usage approaches the "Xmx" setting even if they are not old enough to expire unconditionally. A very old Tomcat developer mailing list thread suggested that this might allow a denial of service attack*, but, as this application is only available within the corporate network, we are unconcerned.

I thought about extending StandardManager to override processExpires() and clobber additional sessions if heap usage is greater than, say, 85% of max. However, this seems a bit problematic in practice. What if muuch of the heap is unreferenced, and the garbage collector would be able to collect a ton of objects (if it bothered to run) to reduce heap to 50% of max? I'd be expiring sessions unnecessarily. I guess we could mitigate this risk with some aggressive garbage collection settings. Also, how would I know how much memory I had saved by expiring a session? I would have to wait for a few GC cycles to know for sure. Perhaps I could take a conservative approach and remove at most N sessions per background process cycle until memory drops below an acceptable threshold. I could serialize the session as a way to estimate how much stuff would be GC'ed, but that relies on the vendor implementing Serializable and marking instance variables as transient appropriately.

Has anyone solved this problem? As a short-term fix, we are increasing heap size, but that band-aid has its drawbacks as well.

  • They were referring to a public site where sessions would be created pre-login. Someone could cause many new sessions to be created and crowd out the ones actually in use.

Update: We really don't have much control over the system's architecture, and we specifically can't reduce session use. However, we can futz with the container as much as we'd like. Tomcat is the only open source servlet container that's vendor-supported, however.

Best Answer

Your options seem to be:

  1. reduce the idle session timeout,
  2. make sessions persistent (maybe only after the user has logged in),
  3. reduce the memory used by each session object,
  4. increase the memory for your Tomcat instance,
  5. run multiple instances of your service, and put a load balancer in front of it/them.

From a technical standpoint 3 is the best solution ... if it works. The others all have a down-side.

Doing clever things with memory is only a band-aid. From the users' perspective, it makes your site's behaviour harder to understand. Besides, if your user base / traffic is trending upwards, you are only putting off the problem of finding a sustainable solution.

Related Topic