Создание кеша байтовых массивов
В моем коде, который часто работает на серверах, я не контролирую конфигурацию, у меня есть коллекция пользователей, и каждый пользователь имеет массив byte[]
.
Иногда эти массивы byte[]
уникальны для пользователя. Часто, однако, будут пользователи с большим количеством чисел с точно таким же массивом byte[]
.
Я пытаюсь уменьшить потребление ОЗУ моего сервера.
Я попытался превратить массивы byte[]
в строки и интернировать их, но затем я часто сталкиваюсь с ошибками памяти PERM-GEN. Я также вижу значительное ухудшение производительности при кодировании/декодировании, когда я хочу получить доступ к массиву byte[]
для пользователя, и я вижу значительно увеличенное использование памяти в худшем случае. Существующая строка намного больше, чем массивы.
Как я могу найти поиск Set<SoftReference<byte[]>>
, когда массивы Java не являются хешируемыми, а SoftReferences не переносят хэш объекта в точку. A Map<byte[],SoftReference<byte[]>>
, очевидно, также побеждает себя, потому что ключ сам по себе и предотвращает сбор; и Set
в любом случае реализуется внутри Map
.
Итак, как я могу ставить byte[]
массивы?
Ответы
Ответ 1
Если у вас есть много одинаковых массивов, используйте HashSet<ByteBuffer>
в качестве кеша. Вы можете получить массив ByteBuffer с помощью метода array()
, а класс ByteBuffer имеет методы hashCode
и equals
. Конечно, лучше, если ваши массивы неизменны.
EDIT2 Комментарий от @Will является точным, чтобы иметь возможность вернуть массив, использовать WeakHashMap<ByteBuffer,WeakReference<ByteBuffer>>
и сделать что-то вроде этого:
public byte[] internalize(byte[] bytes) {
ByteBuffer wrapped = ByteBuffer.wrap(bytes);
if(cache.containsKey(wrapped)) {
wrapped = cache.get(wrapped).get();
}
else {
cache.put(wrapped, new WeakReference<ByteBuffer>(wrapped);
}
return wrapped.array();
}
Ответ 2
Я попытался превратить массивы байтов [] в строки и интернировать их, но затем я часто сталкиваюсь с ошибками памяти PERM-GEN.
Я согласен, что вам нужно что-то вроде String.intern()
, но стандартная реализация native
, поэтому не так много радости.
У вас может быть Map<Integer,Collection<SoftReference<byte[]>>>
, используя хэш-код массивов байтов в качестве клавиши Map
. Затем ваш метод intern
мог бы искать набор существующих массивов байтов с тем же кодом, что и заданные байтовые массивы. С хорошим хеш-кодом, который должен дать небольшой набор массивов для проверки на соответствие.
Изменить: уточнить:
Что-то вроде этого:
class ByteArrayCache
{
private final Map<Integer,Collection<SoftReference<byte[]>> map = new ...;
public final byte[] intern(byte[] byteArray)
{
final int hash = Arrays.hashCode(byteArray);
final Collection<SoftReference<byte[]>> arrays = map.get(hash);
if (arrays != null) {
// Search through arrays for a match, and return the match.
// If no match found, add byteArray to the collection and return it
} else {
// create a new map entry, add byteArray to it, and return byte array
}
}
}
Ответ 3
Я бы использовал кеш, основанный на карте слабых значений Guava. Это гарантирует, что если у вас нет более сильных ссылок на массив байтов, запись будет автоматически удалена.
class Cache {
private final ConcurrentMap<Key, byte[]> map = new MapMaker().weakValues().makeMap();
private static class Key {
byte[] a;
int hash;
Key(byte[] a) {
this.a = a;
hash = Arrays.hashCode(a);
}
@Override
public int hashCode() {
return hash;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof Key) {
return Arrays.equals(a, ((Key) obj).a);
}
return false;
}
}
public byte[] intern(byte[] a) {
byte[] a1 = map.putIfAbsent(new Key(a), a);
if (a1 != null) {
return a1;
}
return a;
}
}