2

We added some choice Cache.getOrElse() statements in our Play 2.2.2 application to store some intermediate products of some rather CPU intensive calculations. Seemed like an easy win! Several of these are rather complex case classes and we noticed that after implementing this we observed two things (a) the memory usage of the JVM goes up substantially (b) we get a bunch of warnings like the following:

 [warn] 20:01:57  net.sf.ehcache.pool.sizeof.SizeOf: The configured limit of 100,000      object references was reached while attempting to calculate the size of the object graph. 
 This can be avoided by adding stop points with @IgnoreSizeOf annotations. Since the CacheManger or Cache <sizeOfPolicy> elements maxDepthExceededBehavior is set to "abort", the sizing operation has stopped and the reported cache size is not accurate. 
 If performance degradation is NOT an issue at the configured limit, raise the limit value using the CacheManager or Cache <sizeOfPolicy> elements maxDepth attribute. 
 For more information, see the Ehcache configuration documentation.

My initial thinking was that because we are storing these complex classes, that the Cache was sucking in other parts of the object that were required to "fully reconstitute" the object once returned out of the cache. So we created some more simple objects - stripping companion objects, and re-typing any fields in the classes that are not Strings or Ints. But even really simple maps of strings seem to be storing a lot of objects (and therefore I presume a lot of memory used) in the Cache.

An example that stores a map of stringsfrom a redis object:

val value = Cache.getOrElse[Map[String,String]]("Cache:PicturesForFolios", expiration = 840000){
      client.hgetAll("Bookmarks:Photos")
}

This map of redis results we are storing has precisely 256 key -> values and the jedis library returns this as a Map[String,String]. So why would this result in that warning of > 100000 objects being calculated for the cache? Performance-wise this is a huge problem because the calculation of the cache size takes a long time and I'm not sure setting the cache to ignore calculation is a wise choice when ehcache is being used extensively!

What am I not getting about ehcache and how this works? Do I need to do my own serialization of any object I want to put in the cache so that everything is forced to a single key -> value???

andrewl
  • 453
  • 1
  • 6
  • 15

1 Answers1

0

The warning you are seeing tells you that you're sizing an entry (i.e. the one or two object graphs that the Key & Value represent) which holds more than 100k references. Generally it means you are caching more than you'd expect, as this is a very big object graph. In this case, probably it is related to what the client returns there... If it isn't that, I can only think of how Play uses Ehcache, but I know nothing about that. You'd need to investigate what really makes it in the cache.

Obviously you could not use ARC to size your caches (i.e. use count based sizing), but you probably would only hiding the problem, the cache would still consume much memory. That would lower the latency of puts though, as Ehcache wouldn't try to size your entries anymore.

Lastly, this hasn't anything to do with serialization, as the sizing is about object graphs on the java heap.

Alex Snaps
  • 1,230
  • 10
  • 10
  • I get what you are saying and hope someone who knows more about the Play implementation can weigh in. I just don't understand how a Map with 256 keys and strings as values can trigger this alert? Is it possible that the Play implementation is optimized for inserting Strings in ehcache, and any attempt to insert something else deeply copies the entire class of the object (ie. the Scala Map class in this case)? – andrewl May 30 '14 at 16:43
  • Stuff I'd investigate: what are the "real" types returned by hgetAll()? Is there some other things referenced in there that might blow the amount of objects cached? What does Play do? What does Scala do? – Alex Snaps Jun 03 '14 at 15:09
  • The library returns a Map[String, String]. That part is pretty unambiguous otherwise the code wouldn't compile. I'm looking for someone who knows what Play does at the ehcache layer .... – andrewl Jun 04 '14 at 14:27
  • Right, but Map (in java at least, not sure what this Map type is there) is an interface... Which could be implemented by numerous actual classes, including ones that say have the complete client hanging off that graph... – Alex Snaps Jun 05 '14 at 11:43
  • I agree with you ... therefore looking for someone to chime in with specific knowledge of this. – andrewl Jun 06 '14 at 15:11