Java内存感知缓存

4

我正在寻找一些想法,如果有人知道任何实现细节更好,但我愿意自己编写所需的缓存。

我想要一个只缓存我配置的内存大小的缓存。与应用程序的其他部分相比,缓存部分将使用近乎100%的内存,因此我们可以将应用程序使用的内存总量视为缓存大小(+垃圾)。

是否有获取内存使用量的方法?还是更好地依赖软指针?使用软指针并始终运行在jvm内存限制的顶部可能非常低效,需要大量的cpu周期来进行内存清除?我能否对现有对象进行一些分析,例如myObject.getMemoryUsage()

LinkedHashMap已经具有我的目的所需的足够的缓存命中率,因此我不必编写某些缓存策略怪物,但我不知道如何正确解决这个内存问题。有什么建议? 我不希望 OOME 到处都飞。

最佳实践是什么?

4个回答

2

SoftReference并不是一个好的选择,因为它们往往会一次性清除。这意味着当您从GC中获得性能损失时,您还需要重新构建缓存。

您可以使用Instrumentation.getObjectSize()来获取对象的浅层大小,并使用反射来获取深层大小。但是,这样做相对昂贵,不是您想经常执行的操作。

为什么不能将大小限制为一定数量的对象?实际上,我会从最简单的缓存开始,只添加您真正需要的内容。

Java中的LRU缓存。

编辑:跟踪您正在使用多少内存的一种方法是将值序列化并存储为byte[]。这可以给您相当精确的控制,但可能会使您的解决方案减慢高达1000倍。(没有免费的午餐;)


1
您可以使用Runtime.freeMemory()方法来获取可用内存,但是当进行垃圾回收后,实际上可能并没有问题,因为此时可能会有大量的空闲内存。 - Peter Lawrey
您可以调整LinkedHashMap的大小和最大大小。 - Peter Lawrey
是的,但这会在执行完全垃圾回收时停止您的应用程序。(假设未忽略提示)通常可能会停顿0.5到5秒钟。如果您不介意,您可以这样做。;) - Peter Lawrey
完美,垃圾回收时间不重要,响应时间也不重要,我只需要高总吞吐量。 - Franz Kafka
因此,每当内存变慢并且在执行GC后再次检查时,您都可以调用GC。这很可能非常昂贵,但如果您无法控制缓存中的对象,则可能是最佳选择。 - Peter Lawrey
显示剩余3条评论

1

我建议使用Java Caching System。但是,如果你想自己编写,我不知道如何获取对象在内存中的大小。你最好的选择是扩展AbstractMap并在SoftReferences中包装值。然后,你可以将Java堆大小设置为所需的最大大小。不过,你的实现还必须查找和清除过期数据。使用JCS可能更容易。


1
SoftReference存在的问题是它们会给垃圾收集器带来更多的工作。虽然它不符合您的要求,但HBase有一种非常有趣的策略,以防止缓存对垃圾收集暂停的贡献:它们将缓存存储在本地内存中: 一个好的开始是将所有数据存储在磁盘上。这可能看起来很天真,但由于I/O缓存,经常访问的数据将驻留在内存中。我强烈建议阅读Varnish缓存系统的这些架构笔记:

0

我发现最佳实践是,如果可能的话,将缓存功能委托给 Java 之外的系统。Java 可以很好地管理内存,但对于任何比简单的 LRU 缓存更复杂的内容,都应该使用专门的缓存系统。

一旦GC启动,就会有很大的成本。

EHCache 是我知道的较为流行的缓存系统之一。另一个答案中提到的 Java Caching System 也不错。

然而,通常我会将这项工作转移到底层函数(通常是应用服务器的 JPA 持久化层),我让它在那里处理,这样就不必在应用程序层面上处理它。

如果您要缓存其他数据,例如 Web 请求,http://hc.apache.org/httpclient-3.x/ 也是另一个很好的选择。

但请记住,您还有“文件系统”,将检索到的数据写入文件系统是完全没有问题的。我已经多次使用这种技术来修复由于错误使用 ByteArrayOutputStreams 导致的内存溢出错误。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接