tomcat-users mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Christopher Schultz <ch...@christopherschultz.net>
Subject Re: java.lang.OutOfMemoryError: Java heap space
Date Thu, 08 Oct 2009 20:22:08 GMT
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Joe,

On 10/8/2009 12:12 PM, Joe Hansen wrote:
> I will now think twice before I store anything in the session and
> will make sure I remove no longer used objects from the HTTP
> Session.

It's always a good goal to limit your session size, but it's not always
foolproof because users don't always do the things you expect. For
instance, if you have a process your user goes through, and you store a
big object (or set of objects) in the session during that process, and
the user does not finish the process, you still need to clean-up after that.

>> You can couple session-object expiration with lazy 
>> instantiation/fetch for a solution that can keep your sessions
>> lightweight yet allow insanely long session timeouts.
> 
> Chris, can you please elaborate what you mean by coupling
> session-object expiration with lazy fetch?

So, lazy instantiation (or initialization) is a design pattern where you
only create objects when you are actually going to use them, as opposed
to creating everything you /might/ need just in case it's necessary.
"Lazy fetch" is just a term I used to describe grabbing your Foto
objects only when actually needed (rather than speculatively loading them).

This concept is illustrated easily in a few lines of code:

// This would be speculative instantiation
Vector v = new Vector(1000); // Just in case

...

if(something) {
   // use the Vector object
}

// This would be lazy instantiation
Vector v = null;

if(something) {
  if(null == v)
    v = new Vector();

  // use the Vector object
}

It sounds simple, and it is. The same concept can be applied when
fetching large objects from the database and possibly storing them in
the session. You can use the session as a poor-mans cache something like
this:

// You probably already do this kind of thing in your code:
Foto foto = (Foto)session.getAttribute("myFotoObject");

// But now, you can plan for the case where the object either
// was never there, or has disappeared because it has been
// removed to save memory:

if(null == foto) {
  foto = fotoManager.fetch(whatever);

  session.setAttribute("myFotoObject", foto);
}

Using this technique, you can get the benefit of session "caching" that
is tolerant of disappearing objects due to the "lazy-fetching" technique
I describe. See below for how to hook it up to cache-flushing schemes.

>> Is it an absolute requirement that these objects be stored in the
>> session at all? If so, then maybe the caching strategy can be tweaked a
>> bit to allow both requirements to peacefully coexist.
> 
> The code is 3 years old and it does not use a caching strategy at all.
> However, I am planning to use ehcache when we developer our future
> websites.

3 years is not a ripe old age for code. Our best code is the stuff that
has lasted for 3 or more years without having to be rewritten. ;)

If ehcache is your thing, go for it. There are other possibilities, too.

>> There are even techniques that will allow your session objects to expire
>> when memory gets tight (which is super cool IMO).

One way to retrofit your web application is to use SoftReferences to
store objects within your session attributes. You can either re-write
all your code to deal with SoftReferences (see examples below), or you
can get tricky and write a simple wrapper around your request/session
objects to do the magic for you.

I'll show you how such tricks could be implemented.

Let's say that you always use the prefix "foto" for all Foto objects in
the session. You could use this technique with all session objects, but
it really only makes sense with the big ones. Here's the plan:

1. Alter your code that deals with Foto objects stored in the session to
tolerate the objects apparently disappearing from the session without
notice. That is, always check for null and re-fetch from the database or
wherever you get your Foto data.

2. SoftReference objects will be used to store your actual Foto objects
in the session.

3. Write a Filter which wraps the HttpServletRequest object to return a
wrapped HttpSession object which will handle the SoftReferences
mentioned in #2 (got all that?)

It's easier than it sounds. Let's assume #1 is already done. Here's how
to write the filter (which implements both #2 and #2 above):

public class SoftReferenceFilter
  implements Filter
{
  public void doFilter(ServletRequest request, ServletResponse response,
                       FilterChain chain)
    throws ServletException, IOException
  {
    if(request instanceof HttpServletRequest)
      request
        = new SoftReferenceRequestWrapper((HttpServletRequest)request);

    chain.doFilter(request, response);
  }

  public static class SoftReferenceRequestWrapper
    extends HttpServletRequestWrapper
  {
    public SoftReferenceRequestWrapper(HttpServletRequest request)
    {
      super(request);
    }

    // Override
    public HttpSession getSession()
    {
      HttpSession session = super.getSession();

      if(null != session)
        session = new SoftReferenceSessionWrapper(session);

      return session;
    }

    // Override
    public HttpSession getSession(boolean create)
    {
      HttpSession session = super.getSession(create);

      if(null != session)
        session = new SoftReferenceSessionWrapper(session);

      return session;
    }
  }

  public static class SoftReferenceSessionWrapper
    implements HttpSession
  {
    private HttpSession _base;

    public SoftReferenceSessionWrapper(HttpSession base)
    {
      _base = base;
    }

    // Here's where the magic happens
    public Object getAttribute(String key)
    {
      Object o = super.getAttribute(key); // Get the stored object

      // Unwrap the SoftReference if appropriate
      if(null != o
         && key.startsWith("foto")
         && o instanceof SoftReference)
        o = ((SoftReference)o).get();

      return o;
    }

    public Object setAttribute(String key, Object value)
    {
      // Wrap the value in a SoftReference if appropriate
      if(key.startsWith("foto") && null != value)
        value = new SoftReference(value);

      super.setAttribute(key, value);
    }

    // Now you get to implement all the other methods of HttpSession
    // as pass-throughs to the superclass. I'm not typing all that trash
    // right now. ;)
  }
}

It's amazing the kinds of trickery you can pull when you wrap objects
such as the request and session.

I hope that all makes sense. Feel free to post questions about this code
if you have any.

Good luck,
- -chris
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.9 (MingW32)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/

iEYEARECAAYFAkrOSfAACgkQ9CaO5/Lv0PC7YwCeI1vkuvYnfdRQArBcX66OirTT
CRMAnR53/xSUiVBiVSAXT/I2G6Pir0M1
=VUTk
-----END PGP SIGNATURE-----

---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@tomcat.apache.org
For additional commands, e-mail: users-help@tomcat.apache.org


Mime
View raw message