Tengo una aplicación que lee un archivo CSV con montones de filas de datos. Le doy al usuario un resumen del número de filas en función de los tipos de datos, pero quiero asegurarme de no leer demasiadas filas de datos y causar OutOfMemoryError
s. Cada fila se traduce en un objeto. ¿Hay una manera fácil de averiguar el tamaño de ese objeto mediante programación? ¿Hay alguna referencia que defina qué tan grandes son los tipos primitivos y las referencias de objeto para a VM
?
En este momento, tengo un código que dice leer hasta 32,000 filas , pero también me gustaría tener un código que diga leer tantas filas como sea posible hasta que haya usado 32 MB de memoria. Tal vez esa es una pregunta diferente, pero aún me gustaría saber.
Respuestas:
Puede usar el paquete java.lang.instrument
Compile y ponga esta clase en un JAR:
Agregue lo siguiente a su
MANIFEST.MF
:Use getObjectSize:
Invocar con:
fuente
byte[0]
,byte[1]
,byte[5]
,int[0]
,int[1]
,int[2]
utilizando el enfoque que usted describe? Sería bueno si los resultados incluyen sobrecarga para la longitud de la matriz y la alineación de la memoria.Debería usar jol , una herramienta desarrollada como parte del proyecto OpenJDK.
Para obtener los tamaños de primitivas, referencias y elementos de matriz, use
VMSupport.vmDetails()
. En Oracle JDK 1.8.0_40 que se ejecuta en Windows de 64 bits (utilizado para todos los ejemplos siguientes), este método devuelvePuede obtener el tamaño superficial de una instancia de objeto usando
ClassLayout.parseClass(Foo.class).toPrintable()
(opcionalmente pasando una instancia atoPrintable
). Este es solo el espacio consumido por una sola instancia de esa clase; no incluye ningún otro objeto referenciado por esa clase. Es no incluyen VM sobrecarga para la cabecera del objeto, la alineación de campo y el relleno. Parajava.util.regex.Pattern
:Puede obtener una vista de resumen del tamaño profundo de una instancia de objeto utilizando
GraphLayout.parseInstance(obj).toFootprint()
. Por supuesto, algunos objetos en la huella podrían compartirse (también referenciados desde otros objetos), por lo que es una aproximación excesiva del espacio que podría recuperarse cuando ese objeto se recolecta basura. Para el resultado dePattern.compile("^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$")
(tomado de esta respuesta ), jol informa una huella total de 1840 bytes, de los cuales solo 72 son la instancia del Patrón en sí.Si en su lugar usa
GraphLayout.parseInstance(obj).toPrintable()
, jol le dirá la dirección, el tamaño, el tipo, el valor y la ruta de las desreferencias de campo para cada objeto referenciado, aunque eso suele ser demasiado detalle para ser útil. Para el ejemplo de patrón continuo, puede obtener lo siguiente. (Las direcciones probablemente cambiarán entre ejecuciones).Las entradas "(algo más)" describen otros objetos en el montón que no forman parte de este gráfico de objetos .
La mejor documentación de jol son las muestras de jol en el repositorio de jol. Los ejemplos demuestran operaciones jol comunes y muestran cómo puede usar jol para analizar VM y elementos internos del recolector de basura.
fuente
vmDetails
es ahoraVM.current().details()
.GraphLayout.parseInstance(instance).toFootprint()
me pareció más útil comprender los tamaños de los objetosAccidentalmente encontré una clase de Java "jdk.nashorn.internal.ir.debug.ObjectSizeCalculator", ya en jdk, que es fácil de usar y parece bastante útil para determinar el tamaño de un objeto.
resultados:
fuente
ObjectSizeCalculator
solo es compatible con HotSpot VMAlgunos años atrás, Javaworld tenía un artículo sobre la determinación del tamaño de los objetos compuestos y potencialmente anidados de Java , básicamente caminan a través de la creación de una implementación sizeof () en Java. El enfoque básicamente se basa en otro trabajo en el que las personas identificaron experimentalmente el tamaño de los primitivos y los objetos típicos de Java y luego aplican ese conocimiento a un método que recorre recursivamente un gráfico de objetos para contar el tamaño total.
Siempre será algo menos preciso que una implementación nativa de C simplemente debido a lo que sucede detrás de escena de una clase, pero debería ser un buen indicador.
Alternativamente, un proyecto de SourceForge apropiadamente llamado sizeof que ofrece una biblioteca Java5 con una implementación sizeof ().
PD: No utilice el enfoque de serialización, no hay correlación entre el tamaño de un objeto serializado y la cantidad de memoria que consume cuando está en vivo.
fuente
En primer lugar, "el tamaño de un objeto" no es un concepto bien definido en Java. Podría referirse al objeto en sí, con solo sus miembros, el Objeto y todos los objetos a los que se refiere (el gráfico de referencia). Puede querer decir el tamaño en la memoria o el tamaño en el disco. Y la JVM puede optimizar cosas como Strings.
Entonces, la única forma correcta es preguntarle a la JVM, con un buen generador de perfiles (uso YourKit ), que probablemente no sea lo que desea.
Sin embargo, según la descripción anterior, parece que cada fila será autónoma y no tendrá un gran árbol de dependencias, por lo que el método de serialización probablemente será una buena aproximación en la mayoría de las JVM. La forma más fácil de hacer esto es la siguiente:
Recuerde que si tiene objetos con referencias comunes, esto no dará el resultado correcto, y el tamaño de la serialización no siempre coincidirá con el tamaño en la memoria, pero es una buena aproximación. El código será un poco más eficiente si inicializa el tamaño ByteArrayOutputStream a un valor razonable.
fuente
Si solo desea saber cuánta memoria se está utilizando en su JVM y cuánta es gratis, puede intentar algo como esto:
editar: pensé que esto podría ser útil ya que el autor de la pregunta también afirmó que le gustaría tener una lógica que maneje "leer tantas filas como sea posible hasta que haya usado 32 MB de memoria".
fuente
Cuando trabajaba en Twitter, escribí una utilidad para calcular el tamaño de objeto profundo. Tiene en cuenta diferentes modelos de memoria (32 bits, oops comprimidos, 64 bits), relleno, relleno de subclase, funciona correctamente en estructuras de datos circulares y matrices. Simplemente puede compilar este archivo .java; no tiene dependencias externas:
https://github.com/twitter/commons/blob/master/src/java/com/twitter/common/objectsize/ObjectSizeCalculator.java
fuente
Muchas de las otras respuestas proporcionan tamaños poco profundos, por ejemplo, el tamaño de un HashMap sin ninguna de las claves o valores, que probablemente no sea lo que desea.
El proyecto jamm usa el paquete java.lang.instrumentation anterior, pero recorre el árbol y, por lo tanto, puede darle un uso profundo de la memoria.
https://github.com/jbellis/jamm
fuente
Tienes que caminar los objetos usando la reflexión. Ten cuidado como lo haces:
byte
sea teóricamente 1 byte no significa que solo se necesita uno en la memoria.HashMap
o algo así usando object-equals como el comparador para eliminar bucles infinitos.@ jodonnell: Me gusta la simplicidad de su solución, pero muchos objetos no son serializables (por lo que esto generaría una excepción), los campos pueden ser transitorios y los objetos pueden anular los métodos estándar.
fuente
Debe medirlo con una herramienta, o estimarlo a mano, y depende de la JVM que esté utilizando.
Hay una sobrecarga fija por objeto. Es específico de JVM, pero generalmente calculo 40 bytes. Luego tienes que mirar a los miembros de la clase. Las referencias a objetos son 4 (8) bytes en una JVM de 32 bits (64 bits). Los tipos primitivos son:
Las matrices siguen las mismas reglas; es decir, es una referencia de objeto, por lo que toma 4 (u 8) bytes en su objeto, y luego su longitud multiplicada por el tamaño de su elemento.
Intentar hacerlo programáticamente con llamadas a
Runtime.freeMemory()
simplemente no le da mucha precisión, debido a las llamadas asincrónicas al recolector de basura, etc. Perfilar el montón con -Xrunhprof u otras herramientas le dará los resultados más precisos.fuente
boolean[]
. En realidad, todos los tipos primitivos no dobles / largos son de 4 bytes. Los últimos son 8 (la respuesta los coloca erróneamente como 4 también)La
java.lang.instrument.Instrumentation
clase proporciona una buena manera de obtener el tamaño de un Objeto Java, pero requiere que defina aypremain
ejecute su programa con un agente Java. Esto es muy aburrido cuando no necesita ningún agente y luego tiene que proporcionar un agente Jar ficticio para su aplicación.Entonces obtuve una solución alternativa usando la
Unsafe
clase desun.misc
. Entonces, considerando la alineación del montón de objetos de acuerdo con la arquitectura del procesador y calculando el desplazamiento de campo máximo, puede medir el tamaño de un Objeto Java. En el siguiente ejemplo, uso una clase auxiliarUtilUnsafe
para obtener una referencia alsun.misc.Unsafe
objeto.fuente
También existe la herramienta Memory Measurer (anteriormente en Google Code , ahora en GitHub ), que es simple y publicada bajo la licencia comercial Apache 2.0 , como se discutió en una pregunta similar .
También, requiere un argumento de línea de comandos para el intérprete de Java si desea medir el consumo de bytes de memoria, pero de lo contrario parece funcionar bien, al menos en los escenarios que lo he usado.
fuente
Sin tener que meterse con la instrumentación, etc., y si no necesita saber el tamaño exacto en bytes de un objeto, puede seguir el siguiente enfoque:
De esta manera, lee la memoria usada antes y después, y llamando al GC justo antes de obtener la memoria usada, reduce el "ruido" casi a 0.
Para obtener un resultado más confiable, puede ejecutar su trabajo n veces, y luego dividir la memoria utilizada por n, obteniendo la cantidad de memoria que requiere una ejecución. Aún más, puedes ejecutarlo todo más veces y hacer un promedio.
fuente
System.gc()
solo te notifica que quieres GC? No se garantiza que se llame al GC en absoluto.Aquí hay una utilidad que hice usando algunos de los ejemplos vinculados para manejar 32 bits, 64 bits y 64 bits con OOP comprimido. Lo utiliza
sun.misc.Unsafe
.Se utiliza
Unsafe.addressSize()
para obtener el tamaño de un puntero nativo yUnsafe.arrayIndexScale( Object[].class )
el tamaño de una referencia de Java.Utiliza el desplazamiento de campo de una clase conocida para calcular el tamaño base de un objeto.
fuente
Instrumentation
porque no inicio tomcat,ObjectSizeCalculator
porque no estoy seguro del tipo de VM (HotSpot) yJOL
los granos de primavera bacouse. Utilizo esto y agrego un segundo parámetro para ignorar las señales simplesAbstractRefreshableApplicationContext.getBeanFactory().getSingletonMutex()
y elinternalSizeOf
código de refactorización para ignorar Class y EnumEstaba buscando un cálculo de tiempo de ejecución de un tamaño de objeto que cumpliera con los siguientes requisitos:
Lo siguiente se basa en el código central del artículo original de especialistas de Java ( https://www.javaspecialists.eu/archive/Issue078.html ) y algunos bits de la versión insegura en otra respuesta a esta pregunta.
Espero que alguien lo encuentre útil.
}
fuente
No hay una llamada al método, si eso es lo que está pidiendo. Con un poco de investigación, supongo que podrías escribir la tuya. Una instancia particular tiene un tamaño fijo derivado del número de referencias y valores primitivos más datos de contabilidad de la instancia. Simplemente caminaría el gráfico de objetos. Cuanto menos variados son los tipos de filas, más fácil.
Si eso es demasiado lento o simplemente más problemas de lo que vale, siempre hay una buena regla de conteo a la antigua.
fuente
Escribí una prueba rápida una vez para estimar sobre la marcha:
El concepto general es asignar objetos y medir el cambio en el espacio de almacenamiento dinámico libre. La clave es
getFreeMemory()
, que solicita ejecuciones de GC y espera a que se estabilice el tamaño de almacenamiento dinámico libre informado . La salida de lo anterior es:Que es lo que esperamos, dado el comportamiento de alineación y la posible sobrecarga del encabezado del bloque de montón.
El método de instrumentación detallado en la respuesta aceptada aquí es el más preciso. El método que describí es preciso pero solo bajo condiciones controladas donde ningún otro hilo está creando / descartando objetos.
fuente
Solo usa java visual VM.
Tiene todo lo que necesita para perfilar y depurar problemas de memoria.
También tiene una consola OQL (Object Query Language) que le permite hacer muchas cosas útiles, una de las cuales es
sizeof(o)
fuente
Cuando use JetBrains IntelliJ, primero habilite "Adjuntar agente de memoria" en Archivo | Configuraciones | Construcción, Ejecución, Implementación | Depurador
Al depurar, haga clic con el botón derecho en una variable de interés y elija "Calcular tamaño retenido":
fuente
Mi respuesta se basa en el código proporcionado por Nick. Ese código mide la cantidad total de bytes que están ocupados por el objeto serializado. Entonces, esto realmente mide cosas de serialización + huella de memoria de objeto simple (solo serializa, por ejemplo,
int
y verás que la cantidad total de bytes serializados no lo es4
). Entonces, si desea obtener el número de bytes sin procesar utilizado exactamente para su objeto, debe modificar ese código un poco. Al igual que:He probado esta solución con tipos primitivos, String y en algunas clases triviales. Puede que no haya casos cubiertos también.
ACTUALIZACIÓN: Ejemplo modificado para admitir el cálculo de la huella de memoria de los objetos de la matriz.
fuente
Podría generar un volcado de almacenamiento dinámico (con jmap, por ejemplo) y luego analizar la salida para encontrar tamaños de objeto. Esta es una solución fuera de línea, pero puede examinar tamaños poco profundos y profundos, etc.
fuente
size le proporciona el aumento en el uso de memoria de jvm debido a la creación de objetos y que normalmente es el tamaño del objeto.
fuente
Esta respuesta no está relacionada con el tamaño del objeto, pero cuando está utilizando una matriz para acomodar los objetos; cuánto tamaño de memoria asignará al objeto.
Por lo tanto, las matrices, la lista o el mapa de todas esas colecciones no van a almacenar objetos realmente (solo en el momento de las primitivas, se necesita un tamaño de memoria de objeto real), solo almacenará referencias para esos objetos.
Ahora el
Used heap memory = sizeOfObj + sizeOfRef (* 4 bytes) in collection
Primitivas
OBJETOS
Quiero decir que todo el objeto REFERENCE necesita solo 4 bytes de memoria. Puede ser una referencia de cadena o una referencia de objeto doble, pero depende de la creación del objeto, la memoria necesaria variará.
ej.) Si creo un objeto para la clase siguiente, se crearán
ReferenceMemoryTest
4 + 4 + 4 = 12 bytes de memoria. La memoria puede diferir cuando intenta inicializar las referencias.Entonces, cuando se crea una matriz de objeto / referencia, todo su contenido estará ocupado con referencias NULL. Y sabemos que cada referencia requiere 4 bytes.
Y finalmente, la asignación de memoria para el siguiente código es de 20 bytes.
ReferenceMemoryTest ref1 = new ReferenceMemoryTest (); (4 (ref1) + 12 = 16 bytes) ReferenceMemoryTest ref2 = ref1; (4 (ref2) + 16 = 20 bytes)
fuente
Supongamos que declaro una clase llamada
Complex
como:Para ver cuánta memoria se asigna a las instancias activas de esta clase:
fuente
Para JSONObject, el siguiente código puede ayudarlo.
devuelve tamaño en bytes
Lo comprobé con mi objeto JSONArray escribiéndolo en un archivo. Está dando el tamaño del objeto.
fuente
Dudo que quieras hacerlo mediante programación a menos que solo quieras hacerlo una vez y almacenarlo para usarlo en el futuro. Es algo costoso de hacer. No hay un operador sizeof () en Java, e incluso si lo hubiera, solo contaría el costo de las referencias a otros objetos y el tamaño de las primitivas.
Una forma de hacerlo es serializar la cosa en un archivo y ver el tamaño del archivo, así:
Por supuesto, esto supone que cada objeto es distinto y no contiene referencias no transitorias a otra cosa.
Otra estrategia sería tomar cada objeto y examinar sus miembros por reflexión y sumar los tamaños (boolean & byte = 1 byte, short & char = 2 bytes, etc.), avanzando en la jerarquía de miembros. Pero eso es tedioso y costoso y termina haciendo lo mismo que haría la estrategia de serialización.
fuente
java.lang.Integer
produce alrededor de 80 bytes, donde la representación del montón generalmente es 32 (a diferencia de la representación del flujo de objetos, la representación del montón depende del tamaño del puntero y la alineación del objeto). En contraste, unanull
referencia serializada requiere un byte en lugar de los cuatro u ocho bytes en la memoria de almacenamiento dinámico.