Подтвердить что ты не робот

Реализация сжатия в памяти для объектов в Java

У нас есть этот прецедент, где мы хотели бы сжать и сохранить объекты (в памяти) и распаковать их по мере необходимости.

Данные, которые мы хотим сжать, весьма разнообразны: от векторов float до строк до дат.

Может кто-нибудь предложить любую хорошую технику сжатия для этого?

Мы рассматриваем простоту сжатия и скорость декомпрессии как наиболее важные факторы.

Спасибо.

4b9b3361

Ответ 1

Если вы хотите сжать экземпляры MyObject, вы можете реализовать его Serializable, а затем передать объекты в сжатый массив байтов, например:

ByteArrayOutputStream baos = new ByteArrayOutputStream();
GZIPOutputStream gzipOut = new GZIPOutputStream(baos);
ObjectOutputStream objectOut = new ObjectOutputStream(gzipOut);
objectOut.writeObject(myObj1);
objectOut.writeObject(myObj2);
objectOut.close();
byte[] bytes = baos.toByteArray();

Затем распакуйте byte[] обратно в объекты:

ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
GZIPInputStream gzipIn = new GZIPInputStream(bais);
ObjectInputStream objectIn = new ObjectInputStream(gzipIn);
MyObject myObj1 = (MyObject) objectIn.readObject();
MyObject myObj2 = (MyObject) objectIn.readObject();
objectIn.close();

Ответ 2

Одно из предложений может заключаться в использовании комбинации следующих потоков:

Ответ 3

Как и в предыдущих ответах, кроме того, я предлагаю вам использовать DeflatorOutputStream и InflatorInputStream, поскольку они проще/быстрее/меньше, чем альтернативы. Причина, по которой она меньше, - это просто сжатие, тогда как альтернативы добавляют расширения формата файла, такие как проверки CRC и заголовки.

Если размер важен, вам может потребоваться простая сериализация. Это связано с тем, что ObjectOutputStream имеет значительные накладные расходы, делающие небольшие объекты намного большими. (Он улучшает для более крупного объекта, особенно при сжатии)

например. a Integer занимает 81 байт, а сжатие не очень поможет для такого небольшого количества байтов. Это можно значительно сократить.

Ответ 4

Лучшая технология сжатия, которую я знаю, - ZIP. Java поддерживает ZipStream. Все, что вам нужно, - это сериализовать ваш объект в массив байтов, а затем закрепить его.

Советы: используйте ByteArrayOutputStream, DataStream, ZipOutputStream.

Ответ 5

В JDK реализованы различные алгоритмы сжатия. Проверьте [java.util.zip](http://download.oracle.com/javase/6/docs/api/java/util/zip/package-summary.html) на весь реализованный алгоритм. Однако может быть неплохо сжать все ваши данные. Например, сериализованный пустой массив может иметь несколько десятков байтов, так как имя базового класса находится в потоке сериализованных данных. Также большинство алгоритмов сжатия предназначены для удаления избыточности из больших блоков данных. На малых и средних объектах Java вы, вероятно, получите совсем мало или вообще не получаете выигрыш.

Ответ 6

Это сложная проблема:

Во-первых, использование ObjectOutputStream, вероятно, не является ответом. Формат потока включает в себя множество связанных с типом метаданных. Если вы сериализуете небольшие объекты, обязательные метаданные затруднят "безубыточность" алгоритма сжатия, даже если вы реализуете специальные методы сериализации.

Использование DataOutputStream с минимальной (или отсутствующей) информацией о добавленном типе даст лучший результат, но смешанные данные обычно не сжимаются с использованием алгоритмов сжатия общего назначения.

Для лучшего сжатия вам может потребоваться изучить свойства данных, которые вы сжимаете. Например:

  • Date объекты могут быть представлены как значения int, если вы знаете, что имеют точность 1 день.
  • Последовательности значений int могут быть закодированы по длине или дельта-кодированы, если они имеют правильные свойства.
  • и т.д.

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

Ответ 7

Сжатие поисковых объектов в Java обычно не очень хорошо... не так хорошо.

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

В качестве примера дадим нам двойной список. Каждый элемент имеет предыдущий и следующий указатель + вы храните длинное значение (timestamp) + byte для типа взаимодействия и два целых числа для идентификаторов пользователей. Поскольку мы используем сжатие указателя, мы имеем 6Bytes * 2 + 8 + 4 * 2 = 28Bytes. Java добавляет 8 байтов + 12 байтов для заполнения. Это составляет 48 бит на элемент.

Теперь мы создаем 10 миллионов списков с 20 элементами каждый (временные ряды событий щелчка пользователей за последние три года (мы хотим найти шаблоны)).

Итак, у нас есть 200 миллионов * 48 байтов элементов = 10 ГБ памяти (хорошо не много).

Ок рядом с сборкой мусора убивает нас и накладные расходы внутри взлётов JDK, мы заканчиваем память объемом 10 ГБ.

Теперь мы можем использовать наше собственное хранилище памяти/объекта. Мы храним его в виде таблицы данных по столбцам, где каждый объект является фактически одной строкой. Таким образом, у нас есть 200 миллионов строк в коллекции timestamp, previous, next, userIdA и userIdB.

Предыдущие и следующие теперь указывают на идентификаторы строк и становятся 4 байта (или 5 байт, если мы превысим 4 миллиарда записей (маловероятно)).

Итак, мы имеем 8 + 4 + 4 + 4 + 4 = > 24 * 200 Mio = 4.8 ГБ + отсутствие проблемы с GC.

Так как столбец временной метки хранит метки времени в режиме минимального максимума, а наши временные метки все в течение трех лет, нам нужно всего 5 байтов для хранения каждой из временных меток. Поскольку указатель теперь хранится относительный (+ и -) и из-за того, что серия кликов является своевременной тесно связанной, нам нужно всего 2 байта в среднем как для предыдущего, так и для следующего, а для идентификаторов пользователей мы используем словарь, поскольку серия кликов для примерно 500 тыс. Пользователей нам нужно всего три байта каждый.

Итак, теперь мы имеем 5 + 2 + 2 + 3 + 3 = > 15 * 200Mio = > 3GB + Словарь 4 * 500k * 4 = 8MB = 3GB + 8MB. Звук отличается от 10GB правильно?

Но мы еще не закончили. Поскольку теперь у нас нет объектов, кроме строк и данных, мы сохраняем каждую серию в виде строки таблицы и используем специальные столбцы как коллекции массива, которые фактически хранят 5 значений и указатель на следующие пять значений + указатель previous.

Итак, у нас есть списки 10Mio с 20 enries каждый (поскольку у нас есть служебные), у нас есть список 20 * (5 + 3 + 3) + 4 * 6 (добавьте некоторые накладные расходы частично заполненных элементов) = > 20 * 11 + 5 * 6 = > 250 * 10Mio = > 2,5 ГБ + мы можем получить доступ к массивам быстрее, чем элементы ходьбы.

Но hey его еще не закончилось... отметки времени теперь относительно хранятся, только требуя 3 байта на запись + 5 в первой записи. → поэтому мы сохраняем намного больше 20 * 9 + 2 + 5 * 6 = > 212 * 10Mio = > 2,12 ГБ. И теперь сохраняем все это в памяти с помощью gzip, и мы получаем 1 ГБ, так как мы можем хранить все линеарности, сначала сохраняя длину массива, все временные метки, все идентификаторы пользователей делают это очень высоко, что в битах, которые нужно сжимать, есть шаблоны, Поскольку мы используем словарь, мы просто сортируем его в соответствии с возможностью каждого пользователя, чтобы быть частью серии.

И так как все это таблица, вы можете десериализовать все на почти скорости чтения, поэтому 1 ГБ на современном SSD стоит 2 секунды для загрузки. Попробуйте это с сериализации/десериализации, и вы можете услышать внутренний крик пользователя.

Итак, прежде чем вы будете сжимать сериализованные данные, храните их в таблицах, проверяйте каждый элемент column/property, если его можно логически сжать. И, наконец, получайте удовольствие от этого.

И запомните 1TB (ECC) стоимость 10k сегодня. Ничего. И 1 ТБ SSD 340 Евро. Поэтому не тратьте свое время на этот вопрос, если вам действительно не нужно.

Ответ 8

Если вам нужно сжать произвольные объекты, возможным подходом является сериализация объекта в массив байтов, а затем использовать, например, алгоритм DEFLATE (тот, который используется GZIP) для сжатия. Когда вам нужен объект, вы можете его распаковать и десериализовать. Не уверен, насколько это было бы эффективно, но оно будет полностью общим.