Respuestas:
Mindprod señala que esta no es una pregunta fácil de responder:
Una JVM es libre de almacenar datos de la forma que desee internamente, con endian grande o pequeño, con cualquier cantidad de relleno o sobrecarga, aunque las primitivas deben comportarse como si tuvieran los tamaños oficiales.
Por ejemplo, la JVM o el compilador nativo podría decidir almacenar unboolean[]
fragmento de 64 bits como aBitSet
. No tiene que decírtelo, siempre que el programa dé las mismas respuestas.
- Puede asignar algunos objetos temporales en la pila.
- Puede optimizar algunas variables o llamadas a métodos totalmente fuera de existencia reemplazándolas por constantes.
- Puede versionar métodos o bucles, es decir, compilar dos versiones de un método, cada una optimizada para una determinada situación, y luego decidir de antemano a cuál llamar.
Entonces, por supuesto, el hardware y el sistema operativo tienen cachés multicapa, en caché de chip, caché de SRAM, caché de DRAM, conjunto de trabajo de RAM normal y almacén de respaldo en el disco. Sus datos pueden estar duplicados en cada nivel de caché. Toda esta complejidad significa que solo puede predecir de manera aproximada el consumo de RAM.
Puede usar Instrumentation.getObjectSize()
para obtener una estimación del almacenamiento consumido por un objeto.
Para visualizar el diseño real del objeto, la huella y las referencias, puede usar la herramienta JOL (Diseño de objetos Java) .
En un JDK moderno de 64 bits, un objeto tiene un encabezado de 12 bytes, rellenado con un múltiplo de 8 bytes, por lo que el tamaño mínimo del objeto es de 16 bytes. Para las JVM de 32 bits, la sobrecarga es de 8 bytes, acolchada a un múltiplo de 4 bytes. (A partir de la respuesta de Dmitry Spikhalskiy , la respuesta de Jayen y JavaWorld ).
Por lo general, las referencias son de 4 bytes en plataformas de 32 bits o en plataformas de 64 bits hasta -Xmx32G
; y 8 bytes por encima de 32 Gb ( -Xmx32G
). (Ver referencias de objetos comprimidos ).
Como resultado, una JVM de 64 bits normalmente requeriría un 30-50% más de espacio de almacenamiento dinámico. ( ¿Debo usar una JVM de 32 o 64 bits?, 2012, JDK 1.7)
Las envolturas en caja tienen gastos generales en comparación con los tipos primitivos (de JavaWorld ):
Integer
: El resultado de 16 bytes es un poco peor de lo que esperaba porque unint
valor puede caber en solo 4 bytes adicionales. El uso deInteger
me cuesta una sobrecarga de memoria del 300 por ciento en comparación con cuando puedo almacenar el valor como un tipo primitivo
Long
: 16 bytes también: claramente, el tamaño real del objeto en el montón está sujeto a una alineación de memoria de bajo nivel realizada por una implementación particular de JVM para un tipo de CPU en particular. Parece que aLong
es 8 bytes de sobrecarga de objetos, más 8 bytes más para el valor largo real. En contraste,Integer
tenía un agujero de 4 bytes sin usar, muy probablemente porque la JVM que uso fuerza la alineación de objetos en un límite de palabra de 8 bytes.
Otros contenedores también son costosos:
Matrices multidimensionales : ofrece otra sorpresa.
Los desarrolladores suelen emplear construcciones comoint[dim1][dim2]
en computación numérica y científica.En una
int[dim1][dim2]
instancia de matriz, cadaint[dim2]
matriz anidada es unObject
en sí misma. Cada uno agrega la sobrecarga habitual de la matriz de 16 bytes. Cuando no necesito una matriz triangular o irregular, eso representa pura sobrecarga. El impacto crece cuando las dimensiones de la matriz difieren mucho.Por ejemplo, una
int[128][2]
instancia ocupa 3.600 bytes. En comparación con los 1.040 bytes queint[256]
usa una instancia (que tiene la misma capacidad), 3.600 bytes representan una sobrecarga del 246 por ciento. En el caso extremo debyte[256][1]
, el factor de gastos generales es casi 19! Compare eso con la situación de C / C ++ en la que la misma sintaxis no agrega ninguna sobrecarga de almacenamiento.
String
: elString
crecimiento de la memoria de a sigue el crecimiento de su matriz de caracteres internos. sin embargo, elString
clase agrega otros 24 bytes de sobrecarga.Para un
String
tamaño no vacío de 10 caracteres o menos, el costo indirecto agregado en relación con la carga útil (2 bytes por cada carácter más 4 bytes para la longitud), varía de 100 a 400 por ciento.
Considere este objeto de ejemplo :
class X { // 8 bytes for reference to the class definition
int a; // 4 bytes
byte b; // 1 byte
Integer c = new Integer(); // 4 bytes for a reference
}
Una suma ingenua sugeriría que una instancia de X
usaría 17 bytes. Sin embargo, debido a la alineación (también llamada relleno), la JVM asigna la memoria en múltiplos de 8 bytes, por lo que en lugar de 17 bytes asignaría 24 bytes.
Depende de la arquitectura / jdk. Para un JDK moderno y una arquitectura de 64 bits, un objeto tiene un encabezado de 12 bytes y un relleno de 8 bytes, por lo que el tamaño mínimo del objeto es de 16 bytes. Puedes usar una herramienta llamada Java Object Layout para determinar un tamaño y obtener detalles sobre el diseño del objeto y la estructura interna de cualquier entidad o adivinar esta información por referencia de clase. Ejemplo de una salida para Integer en mi entorno:
Running 64-bit HotSpot VM.
Using compressed oop with 3-bit shift.
Using compressed klass with 3-bit shift.
Objects are 8 bytes aligned.
Field sizes by type: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
Array element sizes: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
java.lang.Integer object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 12 (object header) N/A
12 4 int Integer.value N/A
Instance size: 16 bytes (estimated, the sample instance is not available)
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
Entonces, para Integer, el tamaño de la instancia es de 16 bytes, porque 4 bytes int se compacta en su lugar justo después del encabezado y antes del límite de relleno.
Código de muestra:
import org.openjdk.jol.info.ClassLayout;
import org.openjdk.jol.util.VMSupport;
public static void main(String[] args) {
System.out.println(VMSupport.vmDetails());
System.out.println(ClassLayout.parseClass(Integer.class).toPrintable());
}
Si usa maven, para obtener JOL:
<dependency>
<groupId>org.openjdk.jol</groupId>
<artifactId>jol-core</artifactId>
<version>0.3.2</version>
</dependency>
Cada objeto tiene una cierta sobrecarga para su monitor asociado y la información de tipo, así como los propios campos. Más allá de eso, los campos se pueden establecer más o menos como la JVM lo considere apropiado (creo), pero como se muestra en otra respuesta , al menos algunas JVM se empacarán bastante. Considere una clase como esta:
public class SingleByte
{
private byte b;
}
vs
public class OneHundredBytes
{
private byte b00, b01, ..., b99;
}
En una JVM de 32 bits, esperaría que 100 instancias SingleByte
tomaran 1200 bytes (8 bytes de sobrecarga + 4 bytes para el campo debido al relleno / alineación). Esperaría que una instancia OneHundredBytes
tomara 108 bytes: la sobrecarga y luego 100 bytes empaquetados. Sin embargo, puede variar según la JVM: una implementación puede decidir no empaquetar los campos enOneHundredBytes
, lo que lleva a que tome 408 bytes (= 8 bytes por encima + 4 * 100 bytes alineados / rellenados). En una JVM de 64 bits, la sobrecarga también puede ser mayor (no estoy seguro).
EDITAR: Vea el comentario a continuación; aparentemente HotSpot se adapta a límites de 8 bytes en lugar de 32, por lo que cada instancia SingleByte
tomaría 16 bytes.
De cualquier manera, el "objeto grande único" será al menos tan eficiente como múltiples objetos pequeños, para casos simples como este.
La memoria total utilizada / libre de un programa se puede obtener en el programa a través de
java.lang.Runtime.getRuntime();
El tiempo de ejecución tiene varios métodos que se relacionan con la memoria. El siguiente ejemplo de codificación demuestra su uso.
package test;
import java.util.ArrayList;
import java.util.List;
public class PerformanceTest {
private static final long MEGABYTE = 1024L * 1024L;
public static long bytesToMegabytes(long bytes) {
return bytes / MEGABYTE;
}
public static void main(String[] args) {
// I assume you will know how to create a object Person yourself...
List < Person > list = new ArrayList < Person > ();
for (int i = 0; i <= 100000; i++) {
list.add(new Person("Jim", "Knopf"));
}
// Get the Java runtime
Runtime runtime = Runtime.getRuntime();
// Run the garbage collector
runtime.gc();
// Calculate the used memory
long memory = runtime.totalMemory() - runtime.freeMemory();
System.out.println("Used memory is bytes: " + memory);
System.out.println("Used memory is megabytes: " + bytesToMegabytes(memory));
}
}
Parece que cada objeto tiene una sobrecarga de 16 bytes en sistemas de 32 bits (y 24 bytes en sistemas de 64 bits).
http://algs4.cs.princeton.edu/14analysis/ es una buena fuente de información. Un ejemplo entre muchos buenos es el siguiente.
http://www.cs.virginia.edu/kim/publicity/pldi09tutorials/memory-efficient-java-tutorial.pdf también es muy informativo, por ejemplo:
¿El espacio de memoria consumido por un objeto con 100 atributos es el mismo que el de 100 objetos, con un atributo cada uno?
No.
¿Cuánta memoria se asigna a un objeto?
¿Cuánto espacio adicional se usa al agregar un atributo?
La pregunta será muy amplia.
Depende de la variable de clase o puede llamar como estados el uso de memoria en java.
También tiene algunos requisitos de memoria adicionales para encabezados y referencias.
La memoria de almacenamiento dinámico utilizada por un objeto Java incluye
memoria para campos primitivos, de acuerdo con su tamaño (ver a continuación los tamaños de los tipos primitivos);
memoria para campos de referencia (4 bytes cada uno);
un encabezado de objeto, que consta de unos pocos bytes de información de "limpieza";
Los objetos en Java también requieren cierta información de "limpieza", como el registro de la clase de un objeto, la identificación y los indicadores de estado, como si el objeto está actualmente accesible, actualmente bloqueado por sincronización, etc.
El tamaño del encabezado del objeto Java varía en 32 y 64 bits jvm.
Aunque estos son los principales consumidores de memoria, jvm también requiere campos adicionales a veces, como la alineación del código, etc.
Tamaños de tipos primitivos
booleano y byte - 1
char y short - 2
int & float - 4
largo y doble - 8
Obtuve muy buenos resultados del enfoque java.lang.instrument.Instrumentation mencionado en otra respuesta. Para obtener buenos ejemplos de su uso, consulte la entrada Contador de memoria de instrumentación del Boletín de especialistas de Java y la biblioteca java.sizeOf en SourceForge.
En caso de que sea útil para alguien, puede descargar desde mi sitio web un pequeño agente Java para consultar el uso de memoria de un objeto . También le permitirá consultar el uso de memoria "profunda".
(String, Integer)
utiliza un caché de guayaba, por elemento. ¡Gracias!
Las reglas sobre cuánta memoria se consume dependen de la implementación de JVM y la arquitectura de la CPU (32 bits frente a 64 bits, por ejemplo).
Para conocer las reglas detalladas de SUN JVM, consulte mi antiguo blog
Saludos, Markus