variable una tipos tipo referencias referenciados referencia que parametros objetos objeto memoria limpiar instancia ejemplos datos java memory-management compression

una - Implementación de compresión en memoria para objetos en Java



variable objeto java (8)

Este es un problema complicado:

Primero, usar ObjectOutputStream probablemente no sea la respuesta. El formato de secuencia incluye una gran cantidad de metadatos relacionados con el tipo. Si está serializando objetos pequeños, los metadatos obligatorios dificultarán que el algoritmo de compresión se "equilibre", incluso si implementa métodos de serialización personalizados.

El uso de DataOutputStream con información de tipo agregado mínima (o nula) dará un mejor resultado, pero los datos mixtos generalmente no son tan compresibles usando algoritmos de compresión de propósito general.

Para una mejor compresión, es posible que deba observar las propiedades de los datos que está comprimiendo. Por ejemplo:

  • Date objetos de Date se pueden representar como valores int si sabes que tienen una precisión de 1 día.
  • Las secuencias de los valores int pueden codificarse en longitud de ejecución o codificarse en delta si tienen las propiedades correctas.
  • y así.

Sin embargo, de la manera en que lo haga, tendrá que hacer una gran cantidad de trabajo para obtener una cantidad de compresión que valga la pena. OMI, una mejor idea sería escribir los objetos en una base de datos, un almacén de datos o un archivo y usar el almacenamiento en caché para mantener los objetos usados ​​frecuentemente en la memoria.

Tenemos este caso de uso en el que nos gustaría comprimir y almacenar objetos (en la memoria) y descomprimirlos cuando sea necesario.

Los datos que queremos comprimir son bastante variados, desde vectores flotantes hasta cadenas y fechas.

¿Alguien puede sugerir alguna buena técnica de compresión para hacer esto?

Estamos viendo la facilidad de compresión y la velocidad de descompresión como los factores más importantes.

Gracias.


Hay varios algoritmos de compresión implementados en el JDK. Compruebe el [java.util.zip](http://download.oracle.com/javase/6/docs/api/java/util/zip/package-summary.html) para todos los algoritmos implementados. Sin embargo, puede no ser bueno comprimir todos sus datos. Por ejemplo, una matriz vacía serializada puede tener varias docenas de bytes de longitud ya que el nombre de la clase subyacente se encuentra en la secuencia de datos serializada. Además, la mayoría de los algoritmos de compresión están diseñados para eliminar la redundancia de grandes bloques de datos. En objetos Java pequeños o medianos, probablemente tendrá muy poca o ninguna ganancia.


La compresión de objetos buscados en Java generalmente no es buena ... no tan buena.

En primer lugar, debe comprender que un objeto Java tiene mucha información adicional que no es necesaria. Si tiene millones de objetos, tiene esta sobrecarga millones de veces.

Como ejemplo, nos deja una lista de doble enlace. Cada elemento tiene un puntero anterior y uno siguiente + almacena un valor largo (marca de tiempo) + byte para el tipo de interacción y dos enteros para los identificadores de usuario. Como usamos la compresión del puntero, somos 6Bytes * 2 + 8 + 4 * 2 = 28Bytes. Java agrega 8 Bytes + 12bytes para el relleno. Esto hace 48Bytes por Elemento.

Ahora creamos 10 millones de listas con 20 elementos cada una (serie temporal de eventos de clics de usuarios durante los últimos tres años (queremos encontrar patrones)).

Entonces tenemos 200Million * 48 Bytes de elementos = 10GB de memoria (bueno, no mucho).

Ok al lado de la recolección de basura nos mata y la sobrecarga en el cielo JDK, terminamos con 10 GB de memoria.

Ahora usemos nuestro propio almacenamiento de memoria / objeto. Lo almacenamos como una tabla de datos en columnas donde cada objeto es en realidad una sola fila. Así que tenemos 200 millones de filas en una marca de tiempo, anterior, siguiente, colección de userIdA y userIdB.

Anterior y siguiente ahora apuntan a los identificadores de filas y se convierten en 4 bytes (o 5 bytes si excedemos las 4 billones de entradas (poco probable)).

Entonces tenemos 8 + 4 + 4 + 4 + 4 => 24 * 200 Mio = 4.8GB + sin problema de GC.

Dado que la columna de marca de tiempo almacena las marcas de tiempo de forma mínima y nuestras marcas de tiempo están dentro de los tres años, solo necesitamos 5 bytes para almacenar cada una de las marcas de tiempo. Dado que el puntero ahora está almacenado relativo (+ y -) y debido a que las series de clic están estrechamente relacionadas oportunamente, solo necesitamos 2 bytes en promedio tanto para el anterior como para el siguiente y para los identificadores de usuario usamos un diccionario ya que la serie de clic es para aproximadamente 500k usuarios solo necesitamos tres bytes cada uno.

Entonces ahora tenemos 5 + 2 + 2 + 3 + 3 => 15 * 200Mio => 3GB + Diccionario de 4 * 500k * 4 = 8MB = 3GB + 8MB. Suena diferente a 10GB ¿verdad?

Pero aún no hemos terminado. Como ahora no tenemos más objetos que filas y datos, almacenamos cada serie como una fila de tabla y usamos columnas especiales que son colecciones de matriz que en realidad almacenan 5 valores y un puntero a los siguientes cinco valores + un puntero anterior.

Así que tenemos listas 10Mio con 20 enries cada uno (ya que tenemos sobrecarga), tenemos por lista 20 * (5 + 3 + 3) + 4 * 6 (vamos a agregar algunos gastos generales de elementos parcialmente llenos) => 20 * 11 + 5 * 6 => 250 * 10Mio => 2,5 GB + podemos acceder a las matrices más rápido que los elementos que caminan.

Pero bueno, aún no ha terminado ... las marcas de tiempo ahora están relativamente almacenadas y solo requieren 3 bytes por entrada + 5 en la primera entrada. -> entonces ahorramos mucho más 20 * 9 + 2 + 5 * 6 => 212 * 10Mio => 2,12 GB. Y ahora lo guardamos todo en la memoria usando gzip it y da como resultado 1GB ya que podemos almacenarlo todo lineal primero almacenando la longitud de la matriz, todas las marcas de tiempo, todos los identificadores de usuario, lo que hace muy recomendable que haya patrones en los bits para ser comprimibles . Como utilizamos un diccionario, simplemente lo clasificamos de acuerdo con la capacidad de propagación de cada ID de usuario para formar parte de una serie.

Y como todo es una tabla, puedes deserializar todo en velocidad casi de lectura, por lo que 1GB en un SSD moderno cuesta 2 segundos para cargarse. Pruebe esto con la serialización / deserialización y puede escuchar llorar al usuario interno.

Por lo tanto, antes de comprimir datos serializados, almacenarlos en tablas, verificar cada columna / propiedad si se puede comprimir lógicamente. Y finalmente diviértete con eso.

Y recuerde 1TB (ECC) cuesta 10k hoy. No es nada. Y 1TB SSD 340 Euro. Así que no pierdas tu tiempo en ese tema a menos que realmente tengas que hacerlo.


La mejor tecnología de compresión que conozco es ZIP. Java es compatible con ZipStream. Todo lo que necesita es serializar su objeto en una matriz de bytes y luego comprimirlo.

Sugerencias: utilice ByteArrayOutputStream, DataStream, ZipOutputStream.


Si necesita comprimir objetos arbitrarios, un posible enfoque es serializar el objeto en una matriz de bytes, y luego usar el algoritmo DEFLATE (el usado por GZIP) para comprimirlo. Cuando necesite el objeto, puede descomprimirlo y deserializarlo. No estoy seguro de cuán eficiente sería, pero será completamente general.


Similar a las respuestas anteriores, excepto que sugiero que use DeflatorOutputStream e InflatorInputStream ya que son más simples / más rápidas / más pequeñas que las alternativas. La razón por la que es más pequeño es que solo hace la compresión, mientras que las alternativas agregan extensiones de formato de archivo, como las comprobaciones y los encabezados de CRC.

Si el tamaño es importante, es posible que desee tener una serialización simple propia. Esto se debe a que ObjectOutputStream tiene una sobrecarga significativa que hace que los objetos pequeños sean mucho más grandes. (Mejora para objetos más grandes especialmente cuando está comprimido)

por ejemplo, un Integer toma 81 bytes, y la compresión no ayudará mucho para una cantidad tan pequeña de bytes. Es posible cortar esto significativamente.



Si desea comprimir instancias de MyObject , puede hacer que implemente Serializable y luego transmitir los objetos a una matriz de bytes comprimida, de esta forma:

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();

Luego, descomprime tu byte[] en los objetos:

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();