Java中小型不可变对象的缓存策略?

mik*_*era 9 java performance caching object immutability

我正在开发一个创建大量小型,不可变Java对象的应用程序.一个例子可能是:

public class Point {
  final int x;
  final int y;
  final int z;
  .....
}
Run Code Online (Sandbox Code Playgroud)

很多Point的实例很可能需要引用相同的(x,y,z)位置.

在应用程序的生命周期中尝试缓存和重用这些对象的程度有多大?处理这种情况的任何特殊技巧?

Jer*_*emy 11

当它成为一个问题.否则你只是在创建一个无用的抽象层.

无论哪种方式,您都可以轻松地使用PointFactory您调用get a来实现它Point,它总是为任何给定的x,y和z返回相同的对象实例.但是,你必须管理什么时候应该从缓存中删除点,因为它们不会被垃圾收集.

我说忘了它,除非它是一个实际的问题.您的应用程序不应该依赖于这样的缓存机制,这将允许您在以后必要时添加它.所以也许只是使用一个工厂,它现在很快就会返回一个新的点实例.

public class PointFactory{
    public static Point get(int x, int y, int z){
        return new Point(x, y, z);
    }
}
Run Code Online (Sandbox Code Playgroud)

  • @kuriouscoder:我没有尝试描述如何实现缓存.我的观点(嘿)是缓存不应该存在,除非存在内存问题或其他问题.我的建议是使用工厂来获取点实例*以防*需要缓存. (5认同)
  • 这并不妨碍每次创建新的点实例.理想情况下,我们想要一个Map <Coordinate,WeakReference <Point >>并维护它 - 其中Coordinate可以是x,y的唯一标识符 - 甚至x +""+ y就足够了. (3认同)

Pet*_*rey 8

您可能遇到的问题是使对象池重量轻,比仅创建对象便宜.您希望池足够大,以获得相当高的命中率.

根据我的经验,您可能会遇到微观基准问题.当您在微基准测试中重复创建单个对象类型时,您可以获得比在实际/复杂应用程序中创建各种对象时更好的结果.

许多对象池aproaches的问题在于它们a)需要一个密钥对象,其成本与创建一个简单对象一样多或更多,b)涉及一些同步/锁定,这可能与创建一个对象一样多的成本c)需要一个添加到缓存时的额外对象(例如Map.Entry),意味着您的命中率必须要高得多才能使缓存值得.

我知道最轻量级但是愚蠢的缓存策略是使用带有哈希码的数组.

例如

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);
}
Run Code Online (Sandbox Code Playgroud)

注意:该数组不是线程安全的,但由于Point是不可变的,因此无关紧要.缓存在尽力而为的基础上工作,并且通过非常简单的逐出策略自然地限制了大小.

出于测试目的,您可以添加命中/未命中计数器以确定数据集的缓存有效性.