Стратегия кэширования для небольших неизменяемых объектов в Java? - PullRequest
9 голосов
/ 26 апреля 2011

Я занимаюсь разработкой приложения, которое создает большое количество небольших неизменяемых объектов Java. Примером может быть:

public class Point {
  final int x;
  final int y;
  final int z;
  .....
}

Там, где существует вероятность того, что многие экземпляры Point должны будут ссылаться на одно и то же (x, y, z) местоположение.

В какой степени имеет смысл пытаться кэшировать и повторно использовать такие объекты в течение всего жизненного цикла приложения? Какие-нибудь специальные уловки, чтобы справиться с такой ситуацией?

Ответы [ 6 ]

11 голосов
/ 26 апреля 2011

Когда это становится проблемой. В противном случае вы просто создаете бесполезный слой абстракции.

В любом случае, вы можете легко реализовать это с помощью PointFactory, который вы вызываете для получения Point, который всегда возвращает один и тот же экземпляр объекта для любых заданных x, y и z. Но тогда вам нужно решить, когда точки должны быть удалены из кэша, поскольку они не будут собирать мусор.

Я говорю, забудь об этом, если это не актуальная проблема. Ваше приложение не должно зависеть от такого механизма кэширования, который позволит вам добавить его позже, если это необходимо. Так что, может быть, просто используйте фабрику, которая возвращает время для нового точечного экземпляра очень вовремя.

public class PointFactory{
    public static Point get(int x, int y, int z){
        return new Point(x, y, z);
    }
}
8 голосов
/ 26 апреля 2011

Проблема, с которой вы, вероятно, столкнетесь, заключается в том, чтобы облегчить пул объектов настолько, чтобы он был дешевле, чем просто создание объектов.Вы хотите, чтобы пул был достаточно большим, чтобы у вас был довольно высокий коэффициент попадания.

По моему опыту, у вас, вероятно, будут проблемы с микротестированием.Когда вы несколько раз создаете один тип объекта в микро-бенчмарке, вы получаете гораздо лучшие результаты, чем при создании множества объектов в реальном / сложном приложении.

Проблема со многими подходами к пулу объектов заключается в том, что ониа) требует ключевой объект, который стоит столько же или больше, чем создание простого объекта, б) включает некоторую синхронизацию / блокировку, которая снова может стоить столько же, сколько создание объекта в) требует дополнительного объекта при добавлении в кэш (например,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);
}

Примечание: массив не является потокобезопасным, но поскольку Point является неизменным, это не имеет значения.Кэш работает с максимальной отдачей и, естественно, имеет ограниченный размер с очень простой стратегией удаления.

В целях тестирования вы можете добавить счетчики попаданий / промахов, чтобы определить эффективность кэшей для вашего набора данных.

4 голосов
/ 26 апреля 2011

Это звучит почти как пример из учебника Flyweight .

3 голосов
/ 26 апреля 2011

Сколько экземпляров будет иметь одинаковые координаты, сколько будет существовать одновременно и сколько будет отброшено?

Повторное использование объектов имеет преимущества только в том случае, если значительный процент живых объектов одновременно является дубликатами (по крайней мере, 20%, я бы сказал) и общее использование памяти проблематично. И если объекты часто отбрасываются, вы должны создать кеш таким образом, чтобы он не стал утечкой памяти (возможно, с использованием мягких / слабых ссылок).

1 голос
/ 26 апреля 2011

Помните, что кэширование этих объектов повлияет на параллелизм и сборку мусора (скорее всего) плохим образом.Я бы не стал этого делать, если бы другие объекты, относящиеся к точкам, тоже не жили долго.

0 голосов
/ 26 апреля 2011

Как и в большинстве случаев: это зависит.

Если ваш объект довольно сложный (требует много времени для установки), выражение можно выразить в виде строки, имеет смысл создавать и загружать их черезстатический метод фабрики.

Это также имеет смысл, если некоторые представления объекта используются чаще, чем другие (в вашем случае это может быть Point (0,0,0))

например

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()
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...