Java中小型不可变对象的缓存策略?
我正在开发一个应用程序,它创建大量小型、不可变的 Java 对象。一个例子可能是:
public class Point {
final int x;
final int y;
final int z;
.....
}
许多 Point 实例可能需要引用相同的 (x,y,z) 位置。
在应用程序的生命周期中尝试缓存和重用此类对象有多大意义?有什么特殊的技巧来处理这种情况吗?
I am developing an app that creates a large number of small, immutable Java objects. An example might be:
public class Point {
final int x;
final int y;
final int z;
.....
}
Where it is likely that many instances of Point will need to refer to the same (x,y,z) location.
To what extent does it make sense to try to cache and re-use such objects during the lifetime of the application? Any special tricks to handle this kind of situation?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
当它成为一个问题时。否则你只是创建了一个无用的抽象层。
无论哪种方式,您都可以使用
PointFactory
轻松实现此功能,您调用该 PointFactory 来获取Point
,它始终为任何给定的 x、y 和 z 返回相同的对象实例。但是,您必须管理何时应从缓存中删除这些点,因为它们不会被垃圾收集。我说忘记它,除非这是一个实际问题。您的应用程序不应该依赖于这样的缓存机制,这将允许您在以后必要时添加它。因此,也许现在只使用一个返回新点实例的工厂。
When it becomes a problem. Otherwise you're just creating a useless layer of abstraction.
Either way, you could easily implement this with a
PointFactory
that you call to get aPoint
, which always returns the same object instance for any given x, y and z. But then you have to manage when the points should be removed from cache because they wont be garbage collected.I say forget about it unless it's an actual issue. Your application shouldn't depend on such a caching mechanism, which would allow you to add it in later if necessary. So maybe just use a factory that returns a new point instance very time for now.
您可能遇到的问题是使对象池足够轻,比仅仅创建对象更便宜。您希望池足够大,以便获得相当高的命中率。
根据我的经验,您可能会在对此进行微基准测试时遇到问题。当您在微基准测试中重复创建单个对象类型时,您会比在真实/复杂的应用程序中创建各种对象时获得更好的结果。
许多对象池方法的问题是它们a)需要一个关键对象,其成本与创建一个简单对象一样多甚至更多,b)涉及一些同步/锁定,这又可能与创建一个对象一样多c)需要一个添加到缓存时需要额外的对象(例如 Map.Entry),这意味着您的命中率必须更高,缓存才有价值。
我所知道的最轻量级但愚蠢的缓存策略是使用带有哈希码的数组。
例如
注意:数组不是线程安全的,但由于 Point 是不可变的,所以这并不重要。缓存会尽最大努力工作,并且自然会通过非常简单的驱逐策略来限制大小。
出于测试目的,您可以添加命中/未命中计数器来确定数据集的缓存有效性。
The problem you are likely to have is making the object pool light weight enough to be cheaper than just creating the objects. You want to the pool to be large enough that you get a fairly high hit rate.
In my experience, you are likely to have problems micro-benchmarking this. When you are creating a single object type repeatedly in a micro-benchmark, you get much better results than when creating a variety of objects in a real/complex application.
The problem with many object pool aproaches is that they a) require a key object, which costs as much or more than creating a simple object, b) involve some synchromization/locking which again can cost as much as creating an object c) require an extra object when adding to the cache (e.g. a Map.Entry), meaning your hit rate has to be much better for the cache to be worth while.
The most light weight, but dumb caching strategy I know is to use an array with a hashcode.
e.g.
Note: the array is not thread safe, but since the Point is immutable, this doesn't matter. The cache works on a best effort basis, and is naturally limited in size with a very simple eviction strategy.
For testing purposes, you can add hit/miss counters to determine the caches effectiveness for you data set.
这听起来几乎就像 Flyweight 模式的教科书示例。
It sounds almost like a textbook example of the Flyweight pattern.
有多少个实例会共享相同的坐标,有多少个会同时存在,有多少个会被丢弃?
仅当活动对象中很大一部分是重复的(我想说至少 20%)并且整体内存使用存在问题时,重用对象才会有好处。如果对象经常被丢弃,则必须以防止其成为内存泄漏的方式构建缓存(可能使用软/弱引用)。
How many instances will share the same coordinates, how many will exist at the same time, and how many will be discarded?
Reusing the objects only has benefits if a significant percentage of live objects at one time are duplicates (at least 20%, I'd say) and overall memory usage is problematic. And if objects are discarded frequently, you have to construct the cache in a way that prevents it from becoming a memory leak (probably using soft/weak references).
请记住,缓存这些对象会(很可能)以不好的方式影响并发性和垃圾收集。除非引用这些点的其他对象也很长寿,否则我不会这样做。
Remember that caching these objects will influence concurrency and garbage collection in (most likely) a bad way. I wouldn't do it unless the other objects that refer to the points are long lived too.
对于大多数情况:这取决于。
如果您的对象相当复杂(需要花费大量时间来实例化),并且可以用字符串表示,那么通过静态工厂方法创建和加载它们是有意义的。
如果对象的某些表示比其他表示更频繁地使用(在您的情况下可能是 Point(0,0,0)),这也是有意义
的
As for most cases: it depends.
If your object is rather complex (takes a lot of time to instatiate) put can be expressed in a string, it makes sense to create and load them through a static factory method.
This also makes sense if some representations of the object are used more often than others (in your case maybe Point(0,0,0))
e.g