Qual é o consumo de memória de um objeto em Java?


216

O espaço de memória consumido por um objeto com 100 atributos é igual ao de 100 objetos, com um atributo cada?

Quanta memória é alocada para um objeto?
Quanto espaço adicional é usado ao adicionar um atributo?

Respostas:


180

A Mindprod ressalta que essa não é uma pergunta direta a ser respondida:

Uma JVM é livre para armazenar dados da maneira que desejar internamente, grande ou pequeno endian, com qualquer quantidade de preenchimento ou sobrecarga, embora os primitivos devam se comportar como se tivessem os tamanhos oficiais.
Por exemplo, a JVM ou o compilador nativo pode decidir armazenar um boolean[]em pedaços longos de 64 bits como um BitSet. Não precisa lhe dizer, desde que o programa dê as mesmas respostas.

  • Pode alocar alguns objetos temporários na pilha.
  • Pode otimizar algumas variáveis ​​ou chamadas de método totalmente inexistentes, substituindo-as por constantes.
  • Pode versão de métodos ou loops, ou seja, compilar duas versões de um método, cada uma otimizada para uma determinada situação, e então decidir antecipadamente qual chamar.

É claro que o hardware e o sistema operacional têm caches multicamadas, no cache de chips, no cache SRAM, no cache DRAM, no conjunto de trabalho comum da RAM e no armazenamento de backup em disco. Seus dados podem ser duplicados em todos os níveis de cache. Toda essa complexidade significa que você pode prever apenas aproximadamente o consumo de RAM.

Métodos de medição

Você pode usar Instrumentation.getObjectSize()para obter uma estimativa do armazenamento consumido por um objeto.

Para visualizar o layout, a área de cobertura e as referências reais do objeto, você pode usar a ferramenta JOL (Java Object Layout) .

Cabeçalhos e referências a objetos

Em um JDK moderno de 64 bits, um objeto possui um cabeçalho de 12 bytes, preenchido com um múltiplo de 8 bytes; portanto, o tamanho mínimo do objeto é 16 bytes. Para JVMs de 32 bits, a sobrecarga é de 8 bytes, preenchida com um múltiplo de 4 bytes. (Da resposta de Dmitry Spikhalskiy , a resposta de Jayen e JavaWorld .)

Normalmente, as referências são de 4 bytes em plataformas de 32 bits ou em plataformas de 64 bits até -Xmx32G; e 8 bytes acima de 32Gb ( -Xmx32G). (Consulte as referências de objetos compactados .)

Como resultado, uma JVM de 64 bits normalmente exigiria de 30 a 50% mais espaço de heap. ( Devo usar uma JVM de 32 ou 64 bits?, 2012, JDK 1.7)

Tipos, matrizes e seqüências de caixa

Os wrappers in a box têm sobrecarga em comparação com os tipos primitivos (do JavaWorld ):

  • Integer: O resultado de 16 bytes é um pouco pior do que eu esperava, porque um intvalor pode caber em apenas 4 bytes extras. Usar um Integercusto me custa uma sobrecarga de memória de 300% em comparação com quando posso armazenar o valor como um tipo primitivo

  • Long: 16 bytes também: Claramente, o tamanho real do objeto no heap está sujeito ao alinhamento de memória de baixo nível feito por uma implementação JVM específica para um tipo de CPU específico. Parece que a Longé 8 bytes de sobrecarga de objeto, mais 8 bytes a mais para o valor longo real. Por outro lado, Integertinha um orifício de 4 bytes não utilizado, provavelmente porque a JVM I utiliza forças no alinhamento de objetos em um limite de palavras de 8 bytes.

Outros contêineres também são caros:

  • Matrizes multidimensionais : oferece outra surpresa.
    Os desenvolvedores geralmente empregam construções como int[dim1][dim2]na computação numérica e científica.

    Em uma int[dim1][dim2]instância de matriz, toda int[dim2]matriz aninhada é uma Objectpor si só. Cada um deles adiciona a sobrecarga usual da matriz de 16 bytes. Quando não preciso de uma matriz triangular ou irregular, isso representa uma sobrecarga pura. O impacto aumenta quando as dimensões da matriz diferem bastante.

    Por exemplo, uma int[128][2]instância ocupa 3.600 bytes. Comparado aos 1.040 bytes que uma int[256]instância usa (que tem a mesma capacidade), 3.600 bytes representam uma sobrecarga de 246%. No caso extremo de byte[256][1], o fator de sobrecarga é quase 19! Compare isso com a situação C / C ++ na qual a mesma sintaxe não adiciona sobrecarga de armazenamento.

  • String: Stringo crescimento da memória de a controla o crescimento de sua matriz de caracteres interna. No entanto, a Stringclasse adiciona outros 24 bytes de sobrecarga.

    Para um não vazio Stringde tamanho 10 caracteres ou menos, o custo adicional adicionado em relação à carga útil (2 bytes para cada caractere mais 4 bytes para o comprimento) varia de 100 a 400 por cento.

Alinhamento

Considere este objeto de exemplo :

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
}

Uma soma ingênua sugeriria que uma instância de Xusaria 17 bytes. No entanto, devido ao alinhamento (também chamado de preenchimento), a JVM aloca a memória em múltiplos de 8 bytes, portanto, em vez de 17 bytes, alocaria 24 bytes.


int [128] [6]: 128 matrizes de 6 polegadas - 768 polegadas no total, 3072 bytes de dados + 2064 bytes Sobrecarga de objeto = total de 5166 bytes. int [256]: 256 ints no total - portanto, não comparáveis. int [768]: 3072 bytes de dados + 16 bytes de sobrecarga - cerca de 1/5 do espaço da matriz 2D - não 246% de sobrecarga!
JeeBee

Ah, o artigo original usou int [128] [2] e não int [128] [6] - pergunto como isso mudou. Também mostra que exemplos extremos podem contar uma história diferente.
JeeBee

2
A sobrecarga é de 16 bytes nas JVM de 64 bits.
Tim Cooper

3
@ AlexWien: Alguns esquemas de coleta de lixo podem impor um tamanho mínimo de objeto separado do preenchimento. Durante a coleta de lixo, depois que um objeto é copiado de um local antigo para um novo, o local antigo pode não precisar mais conter os dados do objeto, mas precisará manter uma referência ao novo local; também pode ser necessário armazenar uma referência ao local antigo do objeto em que a primeira referência foi descoberta e o deslocamento dessa referência no objeto antigo [já que o objeto antigo ainda pode conter referências que ainda não foram processadas].
supercat

2
@AlexWien: O uso de memória no local antigo de um objeto para armazenar as informações de contabilidade do coletor de lixo evita a necessidade de alocar outra memória para esse fim, mas pode impor um tamanho mínimo de objeto maior do que seria necessário. Eu acho que pelo menos uma versão do coletor de lixo .NET usa essa abordagem; certamente seria possível para alguns coletores de lixo Java fazê-lo também.
Supercat

34

Depende da arquitetura / jdk. Para uma arquitetura moderna de JDK e 64 bits, um objeto possui um cabeçalho de 12 bytes e um preenchimento de 8 bytes - portanto, o tamanho mínimo do objeto é 16 bytes. Você pode usar uma ferramenta chamada Java Object Layout para determinar um tamanho e obter detalhes sobre o layout do objeto e a estrutura interna de qualquer entidade ou adivinhar essas informações por referência de classe. Exemplo de uma saída para Inteiro no meu ambiente:

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

Portanto, para Inteiro, o tamanho da instância é de 16 bytes, porque 4 bytes int compactados no local logo após o cabeçalho e antes do limite do preenchimento.

Exemplo de código:

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

Se você usa o maven, para obter o JOL:

<dependency>
    <groupId>org.openjdk.jol</groupId>
    <artifactId>jol-core</artifactId>
    <version>0.3.2</version>
</dependency>

28

Cada objeto possui uma certa sobrecarga para as informações de tipo e monitor associadas, bem como os próprios campos. Além disso, os campos podem ser definidos praticamente da maneira que a JVM achar melhor (eu acredito) - mas, como mostrado em outra resposta , pelo menos algumas JVMs serão compactadas com bastante rigidez. Considere uma classe como esta:

public class SingleByte
{
    private byte b;
}

vs

public class OneHundredBytes
{
    private byte b00, b01, ..., b99;
}

Em uma JVM de 32 bits, eu esperaria 100 instâncias de SingleByte ocupassem 1200 bytes (8 bytes de sobrecarga + 4 bytes para o campo devido ao preenchimento / alinhamento). Eu esperaria que uma instância OneHundredByteslevasse 108 bytes - a sobrecarga e, em seguida, 100 bytes, compactados. Certamente, pode variar de acordo com a JVM - uma implementação pode decidir não compactar os campos emOneHundredBytes , levando a 408 bytes (= sobrecarga de 8 bytes + 4 * 100 bytes alinhados / preenchidos). Em uma JVM de 64 bits, a sobrecarga também pode ser maior (não tenho certeza).

EDIT: Veja o comentário abaixo; aparentemente o HotSpot preenche os limites de 8 bytes em vez de 32, portanto, cada instância deSingleByte leva 16 bytes.

De qualquer maneira, o "único objeto grande" será pelo menos tão eficiente quanto vários objetos pequenos - para casos simples como esse.


9
Na verdade, uma instância do SingleByte ocuparia 16 bytes em uma Sun JVM, com sobrecarga de 8 bytes, 4 bytes para o campo e 4 bytes para preenchimento de objetos, pois o compilador HotSpot arredonda tudo para múltiplos de 8.
Paul Wagland

6

A memória total utilizada / livre de um programa pode ser obtida no programa via

java.lang.Runtime.getRuntime();

O tempo de execução possui vários métodos relacionados à memória. O exemplo de codificação a seguir demonstra seu 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));
     }
 }

6

Parece que todo objeto possui uma sobrecarga de 16 bytes em sistemas de 32 bits (e 24 bytes em sistemas de 64 bits).

http://algs4.cs.princeton.edu/14analysis/ é uma boa fonte de informação. Um exemplo entre muitos bons é o seguinte.

insira a descrição da imagem aqui

http://www.cs.virginia.edu/kim/publicity/pldi09tutorials/memory-efficient-java-tutorial.pdf também é muito informativo, por exemplo:

insira a descrição da imagem aqui


"Parece que todo objeto tem uma sobrecarga de 16 bytes em sistemas de 32 bits (e 24 bytes em sistemas de 64 bits)." Não está correto, pelo menos para os JDKs atuais. Dê uma olhada na minha resposta para o exemplo Inteiro. A sobrecarga do objeto é de pelo menos 12 bytes para o cabeçalho do sistema de 64 bits e do JDK moderno. Pode ser mais por causa do preenchimento, depende do layout real dos campos no objeto.
Dmitry Spikhalskiy

O segundo link para o tutorial Java com eficiência de memória parece estar morto - eu recebo "Proibido".
tsleyson

6

O espaço de memória consumido por um objeto com 100 atributos é igual ao de 100 objetos, com um atributo cada?

Não.

Quanta memória é alocada para um objeto?

  • A sobrecarga é de 8 bytes em 32 bits, 12 bytes em 64 bits; e, em seguida, arredondado para um múltiplo de 4 bytes (32 bits) ou 8 bytes (64 bits).

Quanto espaço adicional é usado ao adicionar um atributo?

  • Os atributos variam de 1 byte (byte) a 8 bytes (long / double), mas as referências são 4 bytes ou 8 bytes, dependendo não de 32 bits ou 64 bits, mas de se -Xmx é <32 GB ou> = 32 GB: típico 64 As JVMs de bits têm uma otimização chamada "-UseCompressedOops", que comprime referências a 4 bytes se o heap estiver abaixo de 32Gb.

1
um caractere é 16 bits, não 8 bits.
comonad 26/06/19

você está certo. alguém parece ter editado a minha resposta original
Jayen

5

Não, o registro de um objeto também requer um pouco de memória. 100 objetos com um atributo ocupam mais memória.


4

A questão será muito ampla.

Depende da variável de classe ou você pode chamar como estados o uso de memória em java.

Ele também possui alguns requisitos adicionais de memória para cabeçalhos e referências.

A memória heap usada por um objeto Java inclui

  • memória para campos primitivos, de acordo com seu tamanho (veja abaixo os tamanhos dos tipos primitivos);

  • memória para campos de referência (4 bytes cada);

  • um cabeçalho de objeto, consistindo em alguns bytes de informações de "limpeza";

Os objetos em java também requerem algumas informações de "limpeza", como gravar a classe, ID e sinalizadores de status de um objeto, como se o objeto está atualmente acessível, bloqueado pela sincronização, etc.

O tamanho do cabeçalho do objeto Java varia nas jvm de 32 e 64 bits.

Embora estes sejam os principais consumidores de memória, o jvm também requer campos adicionais, algumas vezes como no alinhamento do código, etc.

Tamanhos de tipos primitivos

booleano e byte - 1

char & short - 2

int & float - 4

longa e dupla - 8


Os leitores também podem achar este artigo muito esclarecedor: cs.virginia.edu/kim/publicity/pldi09tutorials/…
quellish



1

não, 100 objetos pequenos precisam de mais informações (memória) que um grande.


0

As regras sobre quanta memória é consumida dependem da implementação da JVM e da arquitetura da CPU (32 bits versus 64 bits, por exemplo).

Para as regras detalhadas da JVM SUN, consulte meu blog antigo

Atenciosamente, Markus


Tenho certeza de que o Sun Java 1.6 de 64 bits precisa de 12 bytes para um objeto simples + 4 preenchimento = 16; um objeto + um campo inteiro = 12 + 4 = 16
AlexWien

Você encerrou seu blog?
Johan Boulé 02/02

Na verdade, não tenho certeza de que os blogs da SAP tenham se movido. A maioria pode ser encontrada aqui kohlerm.blogspot.com
kohlerm
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.