Стратегия кэширования для небольших неизменяемых объектов в Java?
Я разрабатываю приложение, которое создает большое количество небольших неизменяемых объектов Java. Примером может служить:
public class Point {
final int x;
final int y;
final int z;
.....
}
где вероятно, что многие экземпляры точки должны будут ссылаться на одно и то же (x,y,z) местоположение.
в какой степени имеет смысл пытаться кэшировать и повторно использовать такие объекты в течение всего срока службы приложения? Никаких особых хитростей в этой ситуации?
6 ответов
проблема, которую вы, вероятно, будете иметь, - это сделать пул объектов достаточно легким, чтобы быть дешевле, чем просто создавать объекты. Вы хотите, чтобы пул был достаточно большим, чтобы вы получили довольно высокую скорость попадания.
по моему опыту, у вас, вероятно, возникнут проблемы с микро-бенчмаркингом. Когда вы создаете один тип объекта повторно в микро-бенчмарке, вы получаете гораздо лучшие результаты, чем при создании различных объектов в реальном / сложном приложение.
проблема со многими аппроксимациями пула объектов заключается в том, что они a) требуют ключевого объекта, который стоит столько же или больше, чем создание простого объекта, b) включают некоторую синхронизацию/блокировку, которая снова может стоить столько же, сколько создание объекта c) требует дополнительного объекта при добавлении в кэш (например, карта.Запись), то есть ваша скорость попадания должна быть намного лучше, чтобы кэш стоил того.
самая легкая, но тупая стратегия кэширования, которую я знаю, - использовать массив с хэш-кодом.
например
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);
}
Примечание: массив не является потокобезопасным, но с точки неизменна, это не имеет значения. Кэш работает на основе лучших усилий и, естественно, ограничен по размеру с очень простой стратегией выселения.
для целей тестирования можно добавить счетчики попадания/пропуска, чтобы определить эффективность кэшей для набора данных.
когда это становится проблемой. В противном случае вы просто создаете ненужный слой абстракции.
В любом случае, вы можете легко реализовать это с помощью PointFactory
что вы звоните, чтобы получить Point
, который всегда возвращает один и тот же экземпляр объекта для любого заданного x, y и z. Но тогда вам нужно управлять, когда точки должны быть удалены из кэша, потому что они не будут собирать мусор.
Я говорю, забудьте об этом, если это настоящий вопрос. Ваше приложение не должно зависеть о таком механизме кэширования, который позволит вам добавить его позже, если это необходимо. Поэтому, возможно, просто используйте фабрику,которая возвращает новый экземпляр точки очень скоро.
public class PointFactory{
public static Point get(int x, int y, int z){
return new Point(x, y, z);
}
}
сколько экземпляров будут иметь одинаковые координаты, сколько будет существовать одновременно и сколько будет отброшено?
повторное использование объектов имеет преимущества, только если значительный процент живых объектов в одно время дублируются (по крайней мере, 20%, я бы сказал), и общее использование памяти проблематично. И если объекты часто отбрасываются, вы должны построить кэш таким образом, чтобы он не стал утечкой памяти (возможно, используя мягкие/слабые ссылки).
помните, что кэширование этих объектов будет влиять на параллелизм и сборку мусора (скорее всего) плохим образом. Я бы не стал этого делать, если бы другие объекты, которые относятся к точкам, тоже не жили долго.
как в большинстве случаев: это зависит.
Если ваш объект довольно сложный (требует много времени для создания экземпляра), put может быть выражен в строке, имеет смысл создавать и загружать их с помощью статического Заводского метода.
это также имеет смысл, если некоторые представления объекта используются чаще, чем другие(в вашем случае, возможно, точка (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()
}