2

我们在 Play 2.2.2 应用程序中添加了一些选择 Cache.getOrElse() 语句来存储一些 CPU 密集型计算的一些中间产品。看起来很容易获胜!其中一些是相当复杂的案例类,我们注意到在实现它之后我们观察到两件事(a)JVM的内存使用量大幅上升(b)我们收到一堆警告,如下所示:

 [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.

我最初的想法是,因为我们正在存储这些复杂的类,所以缓存正在吸收对象的其他部分,一旦从缓存中返回,就需要“完全重构”对象。所以我们创建了一些更简单的对象——剥离伴随对象,并重新键入类中不是字符串或整数的任何字段。但即使是非常简单的字符串映射似乎也在缓存中存储了很多对象(因此我假设使用了很多内存)。

存储来自 redis 对象的字符串映射的示例:

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

我们存储的这个 redis 结果映射正好有 256 个键 -> 值,jedis 库将其作为 Map[String,String] 返回。那么为什么这会导致为缓存计算超过 100000 个对象的警告呢?就性能而言,这是一个巨大的问题,因为缓存大小的计算需要很长时间,而且当 ehcache 被广泛使用时,我不确定将缓存设置为忽略计算是一个明智的选择!

我对 ehcache 没有什么了解以及它是如何工作的?我是否需要对要放入缓存中的任何对象进行自己的序列化,以便将所有内容强制为单个键-> 值???

4

1 回答 1

0

您看到的警告告诉您正在调整包含超过 100k 引用的条目(即键和值表示的一个或两个对象图)的大小。通常,这意味着您的缓存超出了您的预期,因为这是一个非常大的对象图。在这种情况下,可能与客户端返回的内容有关...如果不是,我只能想到Play如何使用Ehcache,但我对此一无所知。您需要调查缓存中真正的原因。

显然,您不能使用 ARC 来调整缓存的大小(即使用基于计数的大小),但您可能只会隐藏问题,缓存仍然会消耗大量内存。不过,这会降低 put 的延迟,因为 Ehcache 不会再尝试调整条目的大小。

最后,这与序列化没有任何关系,因为大小是关于 Java 堆上的对象图。

于 2014-05-30T15:26:43.400 回答