我正在开发一个创建大量小型,不可变Java对象的应用程序。一个例子可能是:
public class Point {
final int x;
final int y;
final int z;
.....
}
很可能Point的许多实例需要引用相同的(x,y,z)位置。
在应用程序的生命周期内尝试缓存和重用这些对象有多大程度的意义?处理这种情况的任何特殊技巧?
答案 0 :(得分:11)
当它成为一个问题。否则你只是在创建一个无用的抽象层。
无论哪种方式,您都可以使用PointFactory
轻松实现此操作,并调用Point
来获取public class PointFactory{
public static Point get(int x, int y, int z){
return new Point(x, y, z);
}
}
,它始终为任何给定的x,y和z返回相同的对象实例。但是,你必须管理何时应该从缓存中删除这些点,因为它们不会被垃圾收集。
我说忘掉它,除非它是一个实际问题。您的应用程序不应该依赖于这样的缓存机制,这将允许您在以后必要时添加它。所以也许只是使用一个工厂,它现在很快就会返回一个新的点实例。
{{1}}
答案 1 :(得分:8)
您可能遇到的问题是使对象池重量轻,比仅创建对象便宜。你希望游泳池足够大,以获得相当高的命中率。
根据我的经验,您可能会遇到微观基准问题。当您在微基准测试中重复创建单个对象类型时,您可以获得比在实际/复杂应用程序中创建各种对象时更好的结果。
许多对象池aproaches的问题在于它们a)需要一个密钥对象,其成本与创建一个简单对象一样多或更多,b)涉及一些同步/锁定,这也可能与创建对象一样多。 )在添加到缓存时需要一个额外的对象(例如Map.Entry),这意味着你的命中率必须要高得多才能使缓存值得。
我知道最轻量级但是愚蠢的缓存策略是使用带有哈希码的数组。
e.g。
private static final int N_POINTS = 10191; // or some large prime.
private static final Point[] POINTS = new Point[N_POINTS];
public static Point of(int x, int y, int z) {
int h = hash(x,y,z); // a simple hash function of x,y,z
int index = (h & 0x7fffffff) % N_POINTS;
Point p = POINTS[index];
if (p != null && p.x == x && p.y == y && p.z == z)
return p;
return POINTS[index] = new Point(x,y,z);
}
注意:数组不是线程安全的,但由于Point是不可变的,所以这没关系。缓存在尽力而为的基础上工作,并且通过非常简单的驱逐策略自然地限制了大小。
出于测试目的,您可以添加命中/未命中计数器以确定数据集的缓存效率。
答案 2 :(得分:4)
这听起来几乎就像Flyweight模式的教科书示例。
答案 3 :(得分:3)
多少个实例将共享相同的坐标,同时存在多少个实例,以及将丢弃多少个实例?
如果一次有很大比例的活动对象是重复的(至少20%,我会说)并且整体内存使用有问题,那么重用这些对象只会带来好处。如果频繁丢弃对象,则必须以防止其成为内存泄漏的方式构造缓存(可能使用软/弱引用)。
答案 4 :(得分:1)
请记住,缓存这些对象会影响并发性和垃圾收集(很可能)是一种糟糕的方式。我不会这样做,除非引用这些点的其他对象也长寿。
答案 5 :(得分:-1)
至于大多数情况:这取决于。
如果您的对象相当复杂(需要花费大量时间进行实例化),可以用字符串表示put,通过静态工厂方法创建和加载它们是有意义的。
如果对象的某些表示比其他表示更常用(在您的情况下可能是Point(0,0,0))
,这也是有意义的。e.g
private static final HashMap<String, Point> hash = new HashMap<String, Point>();
public static Point createPoint(int x, int y, int z) {
String key = getKey(x,y,z);
Point created = hash.get(key)
if (created == null) {
created = new Point(x,y,z);
hash.put(key,created);
}
return created;
}
private static String createKey(int x, int y, int z) {
StringBuffer buffer = new StringBuffer();
buffer.append("x:");
buffer.append(x);
buffer.append("y:");
buffer.append(y);
buffer.append("z:");
buffer.append(z);
return buffer.toString()
}