Em Java, qual é a melhor maneira de determinar o tamanho de um objeto?

617

Eu tenho um aplicativo que lê um arquivo CSV com pilhas de linhas de dados. Dou ao usuário um resumo do número de linhas com base nos tipos de dados, mas quero garantir que não leia muitas linhas de dados e cause OutOfMemoryErrors. Cada linha se traduz em um objeto. Existe uma maneira fácil de descobrir o tamanho desse objeto programaticamente? Existe uma referência que define como são grandes os tipos primitivos e as referências a objetos VM?

No momento, tenho um código que diz ler até 32.000 linhas , mas também gostaria de ter um código que leia o maior número possível de linhas até que eu usei 32 MB de memória. Talvez essa seja uma pergunta diferente, mas eu ainda gostaria de saber.

Jay R.
fonte
Eu adicionei o meu agente com configurações MVn e explicou como aqui: stackoverflow.com/a/36102269/711855
juanmf

Respostas:

460

Você pode usar o pacote java.lang.instrument

Compile e coloque esta classe em um JAR:

import java.lang.instrument.Instrumentation;

public class ObjectSizeFetcher {
    private static Instrumentation instrumentation;

    public static void premain(String args, Instrumentation inst) {
        instrumentation = inst;
    }

    public static long getObjectSize(Object o) {
        return instrumentation.getObjectSize(o);
    }
}

Adicione o seguinte ao seu MANIFEST.MF:

Premain-Class: ObjectSizeFetcher

Use getObjectSize:

public class C {
    private int x;
    private int y;

    public static void main(String [] args) {
        System.out.println(ObjectSizeFetcher.getObjectSize(new C()));
    }
}

Invocar com:

java -javaagent:ObjectSizeFetcherAgent.jar C
Stefan Karlsson
fonte
2
@Stefan Nice dica! Pode dizer, o que vai ser do tamanho de byte[0], byte[1], byte[5], int[0], int[1], int[2]usando a abordagem que você descreveu? Seria bom se os resultados incluíssem sobrecarga no comprimento da matriz e no alinhamento da memória.
8103 dma_k
8
Eu tentei isso e obtive resultados estranhos e inúteis. As cordas eram sempre 32, independentemente do tamanho. Eu pensei que talvez esse fosse o tamanho do ponteiro, mas para outra classe imutável que criei, tenho 24. Funciona bem para primitivos, mas você não precisa de um programa para dizer qual é o tamanho de um caractere.
Brel
6
@Brel esta solução é apenas uma "aproximação da quantidade de armazenamento consumida pelo objeto especificado", conforme especificado na documentação. Suponho também que os autores decidiram definir o tamanho de uma String como 32 bytes (apenas o ponteiro?) Por causa do pool de String do Java, o que dificulta dizer se uma instância de String é compartilhada (armazenada no pool) ou local e exclusivo para uma classe.
Andrei I
11
Como posso usar o ObjectSizeFetcher, se não exportar jar? Eu tenho o projeto java de teste no eclipse.
Yura Shinkarev 5/08
3
@brel O motivo pelo qual uma String tem apenas 32 bytes, independentemente do comprimento real, porque a parte do comprimento variável de uma string é armazenada em um char [], que é seu próprio objeto. Para obter o tamanho real de um objeto, você precisa adicionar o tamanho de si mesmo e o tamanho de cada objeto a que ele faz referência.
Tombrown52
117

Você deve usar o jol , uma ferramenta desenvolvida como parte do projeto OpenJDK.

JOL (Java Object Layout) é a pequena caixa de ferramentas para analisar esquemas de layout de objetos em JVMs. Essas ferramentas estão usando o Unsafe, JVMTI e Serviceability Agent (SA) fortemente para decodificar o layout, a área de cobertura e as referências reais do objeto. Isso torna a JOL muito mais precisa do que outras ferramentas que contam com despejos de heap, suposições de especificação etc.

Para obter os tamanhos de primitivas, referências e elementos de matriz, use VMSupport.vmDetails(). No Oracle JDK 1.8.0_40 em execução no Windows de 64 bits (usado para todos os exemplos a seguir), esse método retorna

Running 64-bit HotSpot VM.
Using compressed oop with 0-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]

Você pode obter o tamanho superficial de uma instância de objeto usando ClassLayout.parseClass(Foo.class).toPrintable()(opcionalmente passando uma instância para toPrintable). Este é apenas o espaço consumido por uma única instância dessa classe; não inclui nenhum outro objeto referenciado por essa classe. Ele não incluem VM sobrecarga para o cabeçalho objeto, o alinhamento campo e estofamento. Para java.util.regex.Pattern:

java.util.regex.Pattern object internals:
 OFFSET  SIZE        TYPE DESCRIPTION                    VALUE
      0     4             (object header)                01 00 00 00 (0000 0001 0000 0000 0000 0000 0000 0000)
      4     4             (object header)                00 00 00 00 (0000 0000 0000 0000 0000 0000 0000 0000)
      8     4             (object header)                cb cf 00 20 (1100 1011 1100 1111 0000 0000 0010 0000)
     12     4         int Pattern.flags                  0
     16     4         int Pattern.capturingGroupCount    1
     20     4         int Pattern.localCount             0
     24     4         int Pattern.cursor                 48
     28     4         int Pattern.patternLength          0
     32     1     boolean Pattern.compiled               true
     33     1     boolean Pattern.hasSupplementary       false
     34     2             (alignment/padding gap)        N/A
     36     4      String Pattern.pattern                (object)
     40     4      String Pattern.normalizedPattern      (object)
     44     4        Node Pattern.root                   (object)
     48     4        Node Pattern.matchRoot              (object)
     52     4       int[] Pattern.buffer                 null
     56     4         Map Pattern.namedGroups            null
     60     4 GroupHead[] Pattern.groupNodes             null
     64     4       int[] Pattern.temp                   null
     68     4             (loss due to the next object alignment)
Instance size: 72 bytes (reported by Instrumentation API)
Space losses: 2 bytes internal + 4 bytes external = 6 bytes total

Você pode obter uma visão resumida do tamanho profundo de uma instância de objeto usando GraphLayout.parseInstance(obj).toFootprint(). Obviamente, alguns objetos na área de cobertura podem ser compartilhados (também referenciados em outros objetos), portanto, é uma supervalorização do espaço que pode ser recuperado quando esse objeto é coletado de lixo. Para o resultado de Pattern.compile("^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$")(extraído desta resposta ), jol relata uma área total de 1840 bytes, dos quais apenas 72 são a própria instância Pattern.

java.util.regex.Pattern instance footprint:
     COUNT       AVG       SUM   DESCRIPTION
         1       112       112   [C
         3       272       816   [Z
         1        24        24   java.lang.String
         1        72        72   java.util.regex.Pattern
         9        24       216   java.util.regex.Pattern$1
        13        24       312   java.util.regex.Pattern$5
         1        16        16   java.util.regex.Pattern$Begin
         3        24        72   java.util.regex.Pattern$BitClass
         3        32        96   java.util.regex.Pattern$Curly
         1        24        24   java.util.regex.Pattern$Dollar
         1        16        16   java.util.regex.Pattern$LastNode
         1        16        16   java.util.regex.Pattern$Node
         2        24        48   java.util.regex.Pattern$Single
        40                1840   (total)

Se você usar GraphLayout.parseInstance(obj).toPrintable(), o jol informará o endereço, tamanho, tipo, valor e caminho das referências a campo para cada objeto referenciado, embora geralmente sejam muitos detalhes para serem úteis. Para o exemplo de padrão em andamento, você pode obter o seguinte. (Os endereços provavelmente mudarão entre as execuções.)

java.util.regex.Pattern object externals:
          ADDRESS       SIZE TYPE                             PATH                           VALUE
         d5e5f290         16 java.util.regex.Pattern$Node     .root.next.atom.next           (object)
         d5e5f2a0        120 (something else)                 (somewhere else)               (something else)
         d5e5f318         16 java.util.regex.Pattern$LastNode .root.next.next.next.next.next.next.next (object)
         d5e5f328      21664 (something else)                 (somewhere else)               (something else)
         d5e647c8         24 java.lang.String                 .pattern                       (object)
         d5e647e0        112 [C                               .pattern.value                 [^, [, a, -, z, A, -, Z, 0, -, 9, _, ., +, -, ], +, @, [, a, -, z, A, -, Z, 0, -, 9, -, ], +, \, ., [, a, -, z, A, -, Z, 0, -, 9, -, ., ], +, $]
         d5e64850        448 (something else)                 (somewhere else)               (something else)
         d5e64a10         72 java.util.regex.Pattern                                         (object)
         d5e64a58        416 (something else)                 (somewhere else)               (something else)
         d5e64bf8         16 java.util.regex.Pattern$Begin    .root                          (object)
         d5e64c08         24 java.util.regex.Pattern$BitClass .root.next.atom.val$rhs        (object)
         d5e64c20        272 [Z                               .root.next.atom.val$rhs.bits   [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, true, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false]
         d5e64d30         24 java.util.regex.Pattern$1        .root.next.atom.val$lhs.val$lhs.val$lhs.val$lhs.val$lhs.val$lhs (object)
         d5e64d48         24 java.util.regex.Pattern$1        .root.next.atom.val$lhs.val$lhs.val$lhs.val$lhs.val$lhs.val$rhs (object)
         d5e64d60         24 java.util.regex.Pattern$5        .root.next.atom.val$lhs.val$lhs.val$lhs.val$lhs.val$lhs (object)
         d5e64d78         24 java.util.regex.Pattern$1        .root.next.atom.val$lhs.val$lhs.val$lhs.val$lhs.val$rhs (object)
         d5e64d90         24 java.util.regex.Pattern$5        .root.next.atom.val$lhs.val$lhs.val$lhs.val$lhs (object)
         d5e64da8         24 java.util.regex.Pattern$5        .root.next.atom.val$lhs.val$lhs.val$lhs (object)
         d5e64dc0         24 java.util.regex.Pattern$5        .root.next.atom.val$lhs.val$lhs (object)
         d5e64dd8         24 java.util.regex.Pattern$5        .root.next.atom.val$lhs        (object)
         d5e64df0         24 java.util.regex.Pattern$5        .root.next.atom                (object)
         d5e64e08         32 java.util.regex.Pattern$Curly    .root.next                     (object)
         d5e64e28         24 java.util.regex.Pattern$Single   .root.next.next                (object)
         d5e64e40         24 java.util.regex.Pattern$BitClass .root.next.next.next.atom.val$rhs (object)
         d5e64e58        272 [Z                               .root.next.next.next.atom.val$rhs.bits [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false]
         d5e64f68         24 java.util.regex.Pattern$1        .root.next.next.next.atom.val$lhs.val$lhs.val$lhs (object)
         d5e64f80         24 java.util.regex.Pattern$1        .root.next.next.next.atom.val$lhs.val$lhs.val$rhs (object)
         d5e64f98         24 java.util.regex.Pattern$5        .root.next.next.next.atom.val$lhs.val$lhs (object)
         d5e64fb0         24 java.util.regex.Pattern$1        .root.next.next.next.atom.val$lhs.val$rhs (object)
         d5e64fc8         24 java.util.regex.Pattern$5        .root.next.next.next.atom.val$lhs (object)
         d5e64fe0         24 java.util.regex.Pattern$5        .root.next.next.next.atom      (object)
         d5e64ff8         32 java.util.regex.Pattern$Curly    .root.next.next.next           (object)
         d5e65018         24 java.util.regex.Pattern$Single   .root.next.next.next.next      (object)
         d5e65030         24 java.util.regex.Pattern$BitClass .root.next.next.next.next.next.atom.val$rhs (object)
         d5e65048        272 [Z                               .root.next.next.next.next.next.atom.val$rhs.bits [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false]
         d5e65158         24 java.util.regex.Pattern$1        .root.next.next.next.next.next.atom.val$lhs.val$lhs.val$lhs.val$lhs (object)
         d5e65170         24 java.util.regex.Pattern$1        .root.next.next.next.next.next.atom.val$lhs.val$lhs.val$lhs.val$rhs (object)
         d5e65188         24 java.util.regex.Pattern$5        .root.next.next.next.next.next.atom.val$lhs.val$lhs.val$lhs (object)
         d5e651a0         24 java.util.regex.Pattern$1        .root.next.next.next.next.next.atom.val$lhs.val$lhs.val$rhs (object)
         d5e651b8         24 java.util.regex.Pattern$5        .root.next.next.next.next.next.atom.val$lhs.val$lhs (object)
         d5e651d0         24 java.util.regex.Pattern$5        .root.next.next.next.next.next.atom.val$lhs (object)
         d5e651e8         24 java.util.regex.Pattern$5        .root.next.next.next.next.next.atom (object)
         d5e65200         32 java.util.regex.Pattern$Curly    .root.next.next.next.next.next (object)
         d5e65220        120 (something else)                 (somewhere else)               (something else)
         d5e65298         24 java.util.regex.Pattern$Dollar   .root.next.next.next.next.next.next (object)

As entradas "(algo mais)" descrevem outros objetos no heap que não fazem parte desse gráfico de objetos .

A melhor documentação para jol são as amostras de jol no repositório de jol. As amostras demonstram operações comuns de jol e mostram como você pode usar o jol para analisar as VMs e os coletores de lixo.

Jeffrey Bosboom
fonte
18
Esta resposta deve ter mais votos positivos. Definitivamente, uma opção muito boa para verificar. EDIT: verificado que este foi adicionado este ano, enquanto a pergunta foi feita em '08. Provavelmente, a melhor e mais fácil opção para fazer o que o OP pediu no momento.
aluga
4
O autor da ferramenta escreveu uma postagem no blog sobre Jol .
Mike
2
Para determinar o tamanho do objeto "obj", use: org.openjdk.jol.info.GraphLayout.parseInstance (obj) .totalSize ();
vigor
Note que vmDetailsé agora VM.current().details().
Miha_x64
Verificação de saída GraphLayout.parseInstance(instance).toFootprint() que achei mais útil entender o tamanho dos objetos
Mugen
82

Achei acidentalmente uma classe java "jdk.nashorn.internal.ir.debug.ObjectSizeCalculator", já em jdk, que é fácil de usar e parece bastante útil para determinar o tamanho de um objeto.

System.out.println(ObjectSizeCalculator.getObjectSize(new gnu.trove.map.hash.TObjectIntHashMap<String>(12000, 0.6f, -1)));
System.out.println(ObjectSizeCalculator.getObjectSize(new HashMap<String, Integer>(100000)));
System.out.println(ObjectSizeCalculator.getObjectSize(3));
System.out.println(ObjectSizeCalculator.getObjectSize(new int[]{1, 2, 3, 4, 5, 6, 7 }));
System.out.println(ObjectSizeCalculator.getObjectSize(new int[100]));

resultados:

164192
48
16
48
416
Tom
fonte
3
Mesmo aqui, eu estava tentando as outras soluções propostas acima e me deparei com o ObjectSizeCalculator. Acredito que ninguém mencionou isso antes, pois foi introduzido recentemente no JDK 8 como parte do projeto Nashorn . No entanto, não encontrei nenhuma documentação oficial sobre essa classe na web.
Henrique Gontijo
Parece não considerar comprimentos de string. É apenas sobre o tamanho da pilha?
Jontejj
1
Eu tenho um mapa de hash, onde com.carrotsearch.RamUsageEstimator retorna cerca da metade do ObjectSizeCalculator. Qual deles é o verdadeiro? - Qual é o mais confiável?
Badera
9
Observe que isso ObjectSizeCalculatoré suportado apenas no HotSpot VM
kellanburket
74

Alguns anos atrás, o Javaworld publicou um artigo sobre como determinar o tamanho de objetos Java compostos e potencialmente aninhados . Eles basicamente criam uma implementação sizeof () em Java. A abordagem baseia-se basicamente em outro trabalho em que as pessoas identificaram experimentalmente o tamanho de objetos primitivos e típicos de Java e aplicaram esse conhecimento a um método que percorre recursivamente um gráfico de objetos para calcular o tamanho total.

Sempre será um pouco menos preciso do que uma implementação nativa de C simplesmente por causa das coisas acontecendo nos bastidores de uma classe, mas deve ser um bom indicador.

Como alternativa, um projeto SourceForge chamado apropriadamente sizeof que oferece uma biblioteca Java5 com uma implementação sizeof ().

PS Não use a abordagem de serialização, não há correlação entre o tamanho de um objeto serializado e a quantidade de memória que ele consome ao vivo.

Boris Terzic
fonte
6
O utilitário sizeof é provavelmente o caminho mais rápido. É basicamente o que Stefan disse, mas já embalou em uma jarra pronta para usar.
Alexandre L Telles
62

Primeiro, "o tamanho de um objeto" não é um conceito bem definido em Java. Você pode significar o próprio objeto, apenas com seus membros, o Objeto e todos os objetos aos quais se refere (o gráfico de referência). Você pode significar o tamanho da memória ou o tamanho do disco. E a JVM pode otimizar coisas como Strings.

Portanto, a única maneira correta é perguntar à JVM, com um bom perfilador (eu uso YourKit ), o que provavelmente não é o que você deseja.

No entanto, a partir da descrição acima, parece que cada linha será independente e não terá uma grande árvore de dependência; portanto, o método de serialização provavelmente será uma boa aproximação na maioria das JVMs. A maneira mais fácil de fazer isso é a seguinte:

 Serializable ser;
 ByteArrayOutputStream baos = new ByteArrayOutputStream();
 ObjectOutputStream oos = new ObjectOutputStream(baos);
 oos.writeObject(ser);
 oos.close();
 return baos.size();

Lembre-se de que se você tiver objetos com referências comuns, isso não dará o resultado correto e o tamanho da serialização nem sempre corresponderá ao tamanho da memória, mas é uma boa aproximação. O código será um pouco mais eficiente se você inicializar o tamanho ByteArrayOutputStream para um valor razoável.

Nick Fortescue
fonte
2
Eu gosto dessa abordagem. Qual a distância em termos de tamanho do objeto?
Berlin Brown
1
Muito simples e eficaz. Outros métodos são muito confusos (especialmente dentro do Eclipse RCP). Obrigado.
Marcolopes
19
A serialização não controla as variáveis ​​transitórias, e o método de serialização padrão grava cadeias de caracteres em UTF-8; portanto, qualquer caractere ANSI terá apenas um byte. Se você tiver muitas cordas, seu tamanho estará tão distante que será inútil.
TMN
1
Embora isso possa não fornecer o tamanho exato, para minhas necessidades, eu só precisava de uma comparação entre 2 objetos e SizeOf não será inicializado a partir de um aplicativo Web. Obrigado!
Isaac
1
Boa recomendação do YourKit . Outras alternativas são VirtualVM e jvmmonitor
angelcervera
38

Se você gostaria de saber quanta memória está sendo usada na sua JVM e quanto é livre, tente algo como isto:

// Get current size of heap in bytes
long heapSize = Runtime.getRuntime().totalMemory();

// Get maximum size of heap in bytes. The heap cannot grow beyond this size.
// Any attempt will result in an OutOfMemoryException.
long heapMaxSize = Runtime.getRuntime().maxMemory();

// Get amount of free memory within the heap in bytes. This size will increase
// after garbage collection and decrease as new objects are created.
long heapFreeSize = Runtime.getRuntime().freeMemory();

edit: Eu pensei que isso poderia ser útil, pois o autor da pergunta também afirmou que gostaria de ter uma lógica que lide com "leia o maior número possível de linhas até que eu usei 32 MB de memória".

matt b
fonte
24
Essa não é uma boa solução, pois você nunca sabe quando uma coleta de lixo ocorrerá ou quanta memória extra será alocada para o heap de uma só vez.
Nick Fortescue
5
Isso é verdade, e eu não pretenderia que isso abordasse a questão principal deste post, mas pode ajudá-lo a saber programaticamente quando ele estiver chegando perto de atingir o tamanho máximo de heap.
matt b
1
Outro problema dessa solução é quando você está em um ambiente multithread (como em um servidor web). É possível que outros threads estivessem em execução e consumindo memória. Com essa aproximação, você está calculando a memória usada em toda a máquina virtual.
angelcervera
1
Outra desvantagem é que o freeMemory retorna uma aproximação. Tente criar um objeto javax.crypto.Cipher. A diferença entre duas chamadas para o freeMemory (para estimar o tamanho de uma cifra) não é constante!
Eugen
1
Eu acredito que você pode forçar uma coleta de lixo, assim você pode fazer algumas coisas nesta abordagem.
matanster
24

Quando trabalhei no Twitter, escrevi um utilitário para calcular o tamanho profundo do objeto. Ele leva em conta diferentes modelos de memória (32 bits, oops compactados, 64 bits), preenchimento, preenchimento de subclasse, funciona corretamente em estruturas de dados circulares e matrizes. Você pode apenas compilar este arquivo .java; não possui dependências externas:

https://github.com/twitter/commons/blob/master/src/java/com/twitter/common/objectsize/ObjectSizeCalculator.java

Attila Szegedi
fonte
1
Szia! Também gostaria de gritar sua apresentação : os slides 15 a 20 são ótimos para ajudar a ter uma ideia instintiva do custo de várias decisões sobre a estrutura de dados. Obrigado por postar isso!
Luke Usherwood
16
"não possui dependências externas" - desde quando a goiaba não é uma dependência externa?
L4mpi
2
parece muito semelhante ao github.com/JetBrains/jdk8u_nashorn/blob/master/src/jdk/nashorn/… ? : O
Francesco
Guave é uma dependência externa.
Mert Serimer
18

Muitas das outras respostas fornecem tamanhos rasos - por exemplo, o tamanho de um HashMap sem nenhuma das chaves ou valores, o que provavelmente não é o que você deseja.

O projeto jamm usa o pacote java.lang.instrumentation acima, mas percorre a árvore e, portanto, pode fornecer o uso profundo da memória.

new MemoryMeter().measureDeep(myHashMap);

https://github.com/jbellis/jamm

Para usar o MemoryMeter, inicie a JVM com "-javaagent: /jamm.jar"

rico
fonte
11

Você tem que andar pelos objetos usando reflexão. Tenha cuidado como você faz:

  • Apenas alocar um objeto tem alguma sobrecarga na JVM. A quantidade varia de acordo com a JVM, portanto, você pode tornar esse valor um parâmetro. Faça pelo menos uma constante (8 bytes?) E aplique a qualquer coisa alocada.
  • Só porque byteé teoricamente 1 byte não significa que leva apenas um na memória.
  • Haverá loops nas referências a objetos; portanto, você precisará manter um HashMapou mais itens iguais a object como o comparador para eliminar loops infinitos.

@ jodonnell: Eu gosto da simplicidade da sua solução, mas muitos objetos não são serializáveis ​​(portanto, isso geraria uma exceção), os campos podem ser transitórios e os objetos podem substituir os métodos padrão.

Jason Cohen
fonte
Os tamanhos de várias primitivas não estão definidos na Especificação Java? (§2.4.1)
erickson
4
Não no sentido de "quanta memória ela ocupa", eis a questão. Somente no sentido de como eles operam. Por exemplo, bytes, caracteres, e calções levar até uma palavra inteira na pilha Java, apesar de operar com arredondamento etc ..
Jason Cohen
1
Isso parece semelhante à medição do tamanho, como mostra Heinz em seu Boletim nº 78: javaspecialists.eu/archive/Issue078.html . Eu usei. Sua abordagem funciona.
precisa
8

Você deve medir com uma ferramenta ou estimar manualmente, e isso depende da JVM que você está usando.

Há alguma sobrecarga fixa por objeto. É específico da JVM, mas eu costumo estimar 40 bytes. Então você tem que olhar para os membros da classe. As referências de objeto são 4 (8) bytes em uma JVM de 32 bits (64 bits). Os tipos primitivos são:

  • booleano e byte: 1 byte
  • char e short: 2 bytes
  • int e float: 4 bytes
  • longo e duplo: 8 bytes

Matrizes seguem as mesmas regras; ou seja, é uma referência a objeto, que ocupa 4 (ou 8) bytes no objeto e, em seguida, seu comprimento multiplicado pelo tamanho do elemento.

Tentar fazer isso de forma programática com chamadas Runtime.freeMemory()não oferece muita precisão, devido a chamadas assíncronas para o coletor de lixo, etc. Criar um perfil do heap com -Xrunhprof ou outras ferramentas fornecerá os resultados mais precisos.

erickson
fonte
@erickson Eu não tenho certeza sobre sizeof (boolean) == 1 olhando para este tópico ( stackoverflow.com/questions/1907318/… ). Você pode comentar sobre isso?
dma_k
2
@dma_k, Java na verdade não possui booleanos reais. O tamanho do booleano é de 4 bytes fora das matrizes e 1 bytes dentro boolean[]. Na verdade, todos os tipos primitivos, não duplos / longos, têm 4 bytes. Os últimos são 8 (a resposta errada coloca-los como 4 também)
bestsss
@ bestsss: Para ser mais exato, a alocação mínima de memória depende da plataforma e implementação da JVM. Além disso, os objetos na pilha são alinhados; portanto, depois de resumir todos os tamanhos, é necessário arredondar para cima.
dma_k
6

A java.lang.instrument.Instrumentationclasse fornece uma ótima maneira de obter o tamanho de um objeto Java, mas requer que você defina umpremain execute seu programa com um agente java. Isso é muito chato quando você não precisa de nenhum agente e precisa fornecer um agente Jar fictício para o seu aplicativo.

Então, eu tenho uma solução alternativa usando a Unsafeclasse do sun.misc. Portanto, considerando o alinhamento da pilha de objetos de acordo com a arquitetura do processador e calculando o deslocamento máximo do campo, é possível medir o tamanho de um Objeto Java. No exemplo abaixo, eu uso uma classe auxiliar UtilUnsafepara obter uma referência ao sun.misc.Unsafeobjeto.

private static final int NR_BITS = Integer.valueOf(System.getProperty("sun.arch.data.model"));
private static final int BYTE = 8;
private static final int WORD = NR_BITS/BYTE;
private static final int MIN_SIZE = 16; 

public static int sizeOf(Class src){
    //
    // Get the instance fields of src class
    // 
    List<Field> instanceFields = new LinkedList<Field>();
    do{
        if(src == Object.class) return MIN_SIZE;
        for (Field f : src.getDeclaredFields()) {
            if((f.getModifiers() & Modifier.STATIC) == 0){
                instanceFields.add(f);
            }
        }
        src = src.getSuperclass();
    }while(instanceFields.isEmpty());
    //
    // Get the field with the maximum offset
    //  
    long maxOffset = 0;
    for (Field f : instanceFields) {
        long offset = UtilUnsafe.UNSAFE.objectFieldOffset(f);
        if(offset > maxOffset) maxOffset = offset; 
    }
    return  (((int)maxOffset/WORD) + 1)*WORD; 
}
class UtilUnsafe {
    public static final sun.misc.Unsafe UNSAFE;

    static {
        Object theUnsafe = null;
        Exception exception = null;
        try {
            Class<?> uc = Class.forName("sun.misc.Unsafe");
            Field f = uc.getDeclaredField("theUnsafe");
            f.setAccessible(true);
            theUnsafe = f.get(uc);
        } catch (Exception e) { exception = e; }
        UNSAFE = (sun.misc.Unsafe) theUnsafe;
        if (UNSAFE == null) throw new Error("Could not obtain access to sun.misc.Unsafe", exception);
    }
    private UtilUnsafe() { }
}
Miguel Gamboa
fonte
Abordagem interessante, mas isso não supõe que o armazenamento de objetos e campos não seja fragmentado?
nicoulaj
Sim e não conheço nenhuma implementação da JVM que faça essa fragmentação.
Miguel Gamboa
Eu não entendo A fragmentação não é uma opção :) Vamos pegar o exemplo do objeto C, que é armazenado como um campo dos objetos A e B. Não muda a coisa toda em A ou B?
nicoulaj
Desculpe, mas também não estou entendendo o seu ponto de vista. De acordo com minha interpretação, em Java, objetos não podem ser armazenados em outros objetos, como acontece com estruturas C ou Value Types em .Net. Então, quando você diz: "objeto C, que é armazenado como um campo dos objetos A e B", significa que os objetos A e B têm campos que armazenam referências (ponteiros) ao objeto C. Em seguida, o tamanho de A e B é igual a o deslocamento desse campo mais o tamanho de uma referência (ponteiro) para o objeto C. E o tamanho de uma referência é o tamanho de uma palavra.
Miguel Gamboa
Ah, ok, estamos falando de tamanho raso. Foi mal.
nicoulaj
6

Há também a ferramenta Memory Measurer (anteriormente no Google Code , agora no GitHub ), que é simples e publicada sob a licença Apache 2.0 comercial , conforme discutida em uma pergunta semelhante .

Também requer um argumento de linha de comando para o interpretador java, se você deseja medir o consumo de bytes de memória, mas parece funcionar bem, pelo menos nos cenários que eu o usei.

PNS
fonte
4

Sem precisar mexer na instrumentação e assim por diante, e se você não precisar saber o tamanho exato de bytes de um objeto, poderá seguir a seguinte abordagem:

System.gc();
Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();

do your job here

System.gc();
Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();

Dessa forma, você lê a memória usada antes e depois e, chamando o GC imediatamente antes de obter a memória usada, diminui o "ruído" quase para 0.

Para obter um resultado mais confiável, você pode executar seu trabalho n vezes e, em seguida, dividir a memória usada por n, obtendo quanta memória uma execução leva. Ainda mais, você pode executar a coisa toda mais vezes e fazer uma média.

reallynice
fonte
5
Não System.gc()apenas notifica que você deseja GC? Não é garantido que o GC seja chamado.
precisa saber é o seguinte
@reallynice. Isso não é seguro, porque você nunca pode fazer o que o GC faz ou afeta a memória entre suas linhas. Assim, "entre" dois métodos freeMemory GC pode liberar mais espaço que você não considera, portanto, o objeto vai parecer menor
Mert Serimer
O @MertSerimer "não é seguro" está em um nível totalmente diferente para mim: no máximo, isso não é tão preciso, como também afirmei. Além disso, você não pode dirigir o GC (como afirmou a Raildex), mas também neste caso sugeri inserir isso em um ciclo. Este é apenas um sistema rápido, sujo e aproximado, que funciona se o resultado não precisar ser muito confiável, conforme declarado.
reallynice
Existem muitos problemas com isso, mas isso lhe dá uma boa vantagem.
markthegrea
3

Aqui está um utilitário que eu criei usando alguns dos exemplos vinculados para lidar com 32 bits, 64 bits e 64 bits com OOP compactado. Ele usa sun.misc.Unsafe.

Ele é usado Unsafe.addressSize()para obter o tamanho de um ponteiro nativo e Unsafe.arrayIndexScale( Object[].class )o tamanho de uma referência Java.

Ele usa o deslocamento de campo de uma classe conhecida para calcular o tamanho base de um objeto.

import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.IdentityHashMap;
import java.util.Stack;
import sun.misc.Unsafe;

/** Usage: 
 * MemoryUtil.sizeOf( object )
 * MemoryUtil.deepSizeOf( object )
 * MemoryUtil.ADDRESS_MODE
 */
public class MemoryUtil
{
    private MemoryUtil()
    {
    }

    public static enum AddressMode
    {
        /** Unknown address mode. Size calculations may be unreliable. */
        UNKNOWN,
        /** 32-bit address mode using 32-bit references. */
        MEM_32BIT,
        /** 64-bit address mode using 64-bit references. */
        MEM_64BIT,
        /** 64-bit address mode using 32-bit compressed references. */
        MEM_64BIT_COMPRESSED_OOPS
    }

    /** The detected runtime address mode. */
    public static final AddressMode ADDRESS_MODE;

    private static final Unsafe UNSAFE;

    private static final long ADDRESS_SIZE; // The size in bytes of a native pointer: 4 for 32 bit, 8 for 64 bit
    private static final long REFERENCE_SIZE; // The size of a Java reference: 4 for 32 bit, 4 for 64 bit compressed oops, 8 for 64 bit
    private static final long OBJECT_BASE_SIZE; // The minimum size of an Object: 8 for 32 bit, 12 for 64 bit compressed oops, 16 for 64 bit
    private static final long OBJECT_ALIGNMENT = 8;

    /** Use the offset of a known field to determine the minimum size of an object. */
    private static final Object HELPER_OBJECT = new Object() { byte b; };


    static
    {
        try
        {
            // Use reflection to get a reference to the 'Unsafe' object.
            Field f = Unsafe.class.getDeclaredField( "theUnsafe" );
            f.setAccessible( true );
            UNSAFE = (Unsafe) f.get( null );

            OBJECT_BASE_SIZE = UNSAFE.objectFieldOffset( HELPER_OBJECT.getClass().getDeclaredField( "b" ) );

            ADDRESS_SIZE = UNSAFE.addressSize();
            REFERENCE_SIZE = UNSAFE.arrayIndexScale( Object[].class );

            if( ADDRESS_SIZE == 4 )
            {
                ADDRESS_MODE = AddressMode.MEM_32BIT;
            }
            else if( ADDRESS_SIZE == 8 && REFERENCE_SIZE == 8 )
            {
                ADDRESS_MODE = AddressMode.MEM_64BIT;
            }
            else if( ADDRESS_SIZE == 8 && REFERENCE_SIZE == 4 )
            {
                ADDRESS_MODE = AddressMode.MEM_64BIT_COMPRESSED_OOPS;
            }
            else
            {
                ADDRESS_MODE = AddressMode.UNKNOWN;
            }
        }
        catch( Exception e )
        {
            throw new Error( e );
        }
    }


    /** Return the size of the object excluding any referenced objects. */
    public static long shallowSizeOf( final Object object )
    {
        Class<?> objectClass = object.getClass();
        if( objectClass.isArray() )
        {
            // Array size is base offset + length * element size
            long size = UNSAFE.arrayBaseOffset( objectClass )
                    + UNSAFE.arrayIndexScale( objectClass ) * Array.getLength( object );
            return padSize( size );
        }
        else
        {
            // Object size is the largest field offset padded out to 8 bytes
            long size = OBJECT_BASE_SIZE;
            do
            {
                for( Field field : objectClass.getDeclaredFields() )
                {
                    if( (field.getModifiers() & Modifier.STATIC) == 0 )
                    {
                        long offset = UNSAFE.objectFieldOffset( field );
                        if( offset >= size )
                        {
                            size = offset + 1; // Field size is between 1 and PAD_SIZE bytes. Padding will round up to padding size.
                        }
                    }
                }
                objectClass = objectClass.getSuperclass();
            }
            while( objectClass != null );

            return padSize( size );
        }
    }


    private static final long padSize( final long size )
    {
        return (size + (OBJECT_ALIGNMENT - 1)) & ~(OBJECT_ALIGNMENT - 1);
    }


    /** Return the size of the object including any referenced objects. */
    public static long deepSizeOf( final Object object )
    {
        IdentityHashMap<Object,Object> visited = new IdentityHashMap<Object,Object>();
        Stack<Object> stack = new Stack<Object>();
        if( object != null ) stack.push( object );

        long size = 0;
        while( !stack.isEmpty() )
        {
            size += internalSizeOf( stack.pop(), stack, visited );
        }
        return size;
    }


    private static long internalSizeOf( final Object object, final Stack<Object> stack, final IdentityHashMap<Object,Object> visited )
    {
        // Scan for object references and add to stack
        Class<?> c = object.getClass();
        if( c.isArray() && !c.getComponentType().isPrimitive() )
        {
            // Add unseen array elements to stack
            for( int i = Array.getLength( object ) - 1; i >= 0; i-- )
            {
                Object val = Array.get( object, i );
                if( val != null && visited.put( val, val ) == null )
                {
                    stack.add( val );
                }
            }
        }
        else
        {
            // Add unseen object references to the stack
            for( ; c != null; c = c.getSuperclass() )
            {
                for( Field field : c.getDeclaredFields() )
                {
                    if( (field.getModifiers() & Modifier.STATIC) == 0 
                            && !field.getType().isPrimitive() )
                    {
                        field.setAccessible( true );
                        try
                        {
                            Object val = field.get( object );
                            if( val != null && visited.put( val, val ) == null )
                            {
                                stack.add( val );
                            }
                        }
                        catch( IllegalArgumentException e )
                        {
                            throw new RuntimeException( e );
                        }
                        catch( IllegalAccessException e )
                        {
                            throw new RuntimeException( e );
                        }
                    }
                }
            }
        }

        return shallowSizeOf( object );
    }
}
dlaudams
fonte
Você testou esta classe com valores? Eu tentei, mas para mim, valores incorretos !!!.
Débora
1
Os valores que ele me forneceu para um objeto simples estavam quase corretos, mas desatualizados em um fator de 10 para uma lista contendo objetos de 1 milhão. Ainda assim, muito bom trabalho!
Michael Böckling
Interessante. Testei-o usando JDK7u67, no Windows 7 x64 e Linux 2.6.16 / x86_64, usando cada um dos modos de endereço de 32 bits / 64 bits / oop. Comparei-o aos despejos de memória analisados ​​no Eclipse Memory Analyzer 1.3.x. Que configuração você está usando? Você tem um exemplo específico que eu poderia tentar?
dlaudams
Melhor escolha que posso fazer. Não posso usar Instrumentationporque não inicio o tomcat, ObjectSizeCalculatorporque não tenho certeza do tipo de VM (HotSpot) e do JOLfeijão de bacouse. Eu uso isso e adicione segundo parâmetro para ignorar singletons viz AbstractRefreshableApplicationContext.getBeanFactory().getSingletonMutex()e refatorar internalSizeOfcódigo para ignorar Classe e Enum
Perlos
Para comparar resultados, use ObjectSizeCalculator (Calcular o servidor inteiro de 1 GB a 10s). JOL causa MemError (6GB não é necessário) e não recebo os mesmos resultados, provavelmente porque enumerações.
Perlos
3

Eu estava procurando por um cálculo em tempo de execução de um tamanho de objeto que atendesse aos seguintes requisitos:

  • Disponível em tempo de execução, sem necessidade de incluir instrumentação.
  • Funciona com Java 9 ou superior sem acesso ao inseguro.
  • É baseado apenas na classe. Não é um tamanho profundo; isso leva em consideração os comprimentos das strings, os comprimentos da matriz etc.

O seguinte é baseado no código principal do artigo original de especialistas em java ( https://www.javaspecialists.eu/archive/Issue078.html ) e em alguns bits da versão insegura em outra resposta a esta pergunta.

Espero que alguém ache útil.

public class JavaSize {

private static final int NR_BITS = Integer.valueOf(System.getProperty("sun.arch.data.model"));
private static final int BYTE = 8;
private static final int WORD = NR_BITS / BYTE;
private static final int HEADER_SIZE = 8;

public static int sizeOf(Class<?> clazz) {
    int result = 0;

    while (clazz != null) {
        Field[] fields = clazz.getDeclaredFields();
        for (int i = 0; i < fields.length; i++) {
            if (!Modifier.isStatic(fields[i].getModifiers())) {
                if (fields[i].getType().isPrimitive()) {
                    Class<?> primitiveClass = fields[i].getType();
                    if (primitiveClass == boolean.class || primitiveClass == byte.class) {
                        result += 1;
                    } else if (primitiveClass == short.class) {
                        result += 2;
                    } else if (primitiveClass == int.class || primitiveClass == float.class) {
                        result += 4;
                    } else if (primitiveClass == double.class || primitiveClass == long.class) {
                        result += 8;
                    }

                } else {
                    // assume compressed references.
                    result += 4;
                }
            }
        }

        clazz = clazz.getSuperclass();

        // round up to the nearest WORD length.
        if ((result % WORD) != 0) {
            result += WORD - (result % WORD);
        }
    }

    result += HEADER_SIZE;

    return result;
}

}

David Ryan
fonte
2

Não há uma chamada de método, se é isso que você está pedindo. Com um pouco de pesquisa, suponho que você possa escrever sua própria. Uma instância específica possui um tamanho fixo derivado do número de referências e valores primitivos mais os dados da contabilidade da instância. Você simplesmente percorreria o gráfico do objeto. Quanto menos variados os tipos de linha, mais fácil.

Se isso for muito lento ou apenas mais problemas do que vale, sempre haverá boas linhas antiquadas contando a regra de ouro.

esplêndido
fonte
2

Eu escrevi um teste rápido uma vez para estimar rapidamente:

public class Test1 {

    // non-static nested
    class Nested { }

    // static nested
    static class StaticNested { }

    static long getFreeMemory () {
        // waits for free memory measurement to stabilize
        long init = Runtime.getRuntime().freeMemory(), init2;
        int count = 0;
        do {
            System.out.println("waiting..." + init);
            System.gc();
            try { Thread.sleep(250); } catch (Exception x) { }
            init2 = init;
            init = Runtime.getRuntime().freeMemory();
            if (init == init2) ++ count; else count = 0;
        } while (count < 5);
        System.out.println("ok..." + init);
        return init;
    }

    Test1 () throws InterruptedException {

        Object[] s = new Object[10000];
        Object[] n = new Object[10000];
        Object[] t = new Object[10000];

        long init = getFreeMemory();

        //for (int j = 0; j < 10000; ++ j)
        //    s[j] = new Separate();

        long afters = getFreeMemory();

        for (int j = 0; j < 10000; ++ j)
            n[j] = new Nested();

        long aftersn = getFreeMemory();

        for (int j = 0; j < 10000; ++ j)
            t[j] = new StaticNested();

        long aftersnt = getFreeMemory();

        System.out.println("separate:      " + -(afters - init) + " each=" + -(afters - init) / 10000);
        System.out.println("nested:        " + -(aftersn - afters) + " each=" + -(aftersn - afters) / 10000);
        System.out.println("static nested: " + -(aftersnt - aftersn) + " each=" + -(aftersnt - aftersn) / 10000);

    }

    public static void main (String[] args) throws InterruptedException {
        new Test1();
    }

}

O conceito geral é alocar objetos e medir a mudança no espaço livre da pilha. A chave é getFreeMemory()que solicita que o GC seja executado e aguarda a estabilização do tamanho do heap livre relatado . A saída do acima é:

nested:        160000 each=16
static nested: 160000 each=16

É o que esperamos, dado o comportamento do alinhamento e a possível sobrecarga do cabeçalho do bloco de heap.

O método de instrumentação detalhado na resposta aceita aqui é o mais preciso. O método que descrevi é preciso, mas apenas sob condições controladas em que nenhum outro encadeamento está criando / descartando objetos.

Jason C
fonte
2

Basta usar java visual VM.

Tem tudo o que você precisa para analisar e depurar problemas de memória.

Ele também possui um console OQL (Object Query Language) que permite fazer muitas coisas úteis, uma das quais sizeof(o)

ACV
fonte
2

Ao usar o JetBrains IntelliJ, primeiro habilite "Anexar agente de memória" em Arquivo | Configurações | Compilação, Execução, Implantação | Depurador.

Ao depurar, clique com o botão direito do mouse em uma variável de interesse e escolha "Calcular tamanho retido": Calcular tamanho retido

simon04
fonte
1

Minha resposta é baseada no código fornecido por Nick. Esse código mede a quantidade total de bytes que são ocupados pelo objeto serializado. Portanto, isso realmente mede o material de serialização + a pegada de memória de objeto simples (apenas serialize, por exemplo, inte você verá que a quantidade total de bytes serializados não é 4). Portanto, se você deseja que o número de bytes brutos seja usado exatamente para o seu objeto - é necessário modificar um pouco esse código. Igual a:

import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class ObjectSizeCalculator {
    private Object getFirstObjectReference(Object o) {
        String objectType = o.getClass().getTypeName();

        if (objectType.substring(objectType.length()-2).equals("[]")) {
            try {
                if (objectType.equals("java.lang.Object[]"))
                    return ((Object[])o)[0];
                else if (objectType.equals("int[]"))
                    return ((int[])o)[0];
                else
                    throw new RuntimeException("Not Implemented !");
            } catch (IndexOutOfBoundsException e) {
                return null;
            }
        }

        return o;
    } 

    public int getObjectSizeInBytes(Object o) {
        final String STRING_JAVA_TYPE_NAME = "java.lang.String";

        if (o == null)
            return 0;

        String objectType = o.getClass().getTypeName();
        boolean isArray = objectType.substring(objectType.length()-2).equals("[]");

        Object objRef = getFirstObjectReference(o);
        if (objRef != null && !(objRef instanceof Serializable))
            throw new RuntimeException("Object must be serializable for measuring it's memory footprint using this method !");

        try {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(o);
            oos.close();
            byte[] bytes = baos.toByteArray();

            for (int i = bytes.length - 1, j = 0; i != 0; i--, j++) {
                if (objectType != STRING_JAVA_TYPE_NAME) {
                    if (bytes[i] == 112)
                        if (isArray)
                            return j - 4;
                        else
                            return j;
                } else {
                    if (bytes[i] == 0)
                        return j - 1;
                }
            }
        } catch (Exception e) {
            return -1;
        }

        return -1;
    }    

}

Eu testei esta solução com tipos primitivos, String e em algumas classes triviais. Pode não haver casos cobertos também.


UPDATE: Exemplo modificado para suportar o cálculo da pegada de memória de objetos de matriz.

Agnius Vasiliauskas
fonte
0

Você pode gerar um dump de heap (com jmap, por exemplo) e depois analisar a saída para encontrar tamanhos de objeto. Esta é uma solução offline, mas você pode examinar tamanhos rasos e profundos etc.

JZeeb
fonte
0
long heapSizeBefore = Runtime.getRuntime().totalMemory();

// Code for object construction
...
long heapSizeAfter = Runtime.getRuntime().totalMemory();
long size = heapSizeAfter - heapSizeBefore;

size fornece o aumento no uso de memória da jvm devido à criação do objeto e esse normalmente é o tamanho do objeto.

user835199
fonte
e se o GC for executado no meio durante // Código para construção do objeto? Agora pode produzir o resultado correto o tempo todo.
rajugaadu
0

Esta resposta não está relacionada ao tamanho do objeto, mas quando você estiver usando a matriz para acomodar os objetos; quanto tamanho de memória será alocado para o objeto.

Portanto, matrizes, lista ou mapeamento de todas essas coleções não armazenam objetos realmente (somente no momento das primitivas, é necessário o tamanho real da memória do objeto), ele armazenará apenas referências para esses objetos.

Agora o Used heap memory = sizeOfObj + sizeOfRef (* 4 bytes) in collection

  • (4/8 bytes) depende do SO (32/64 bits)

PRIMITIVOS

int   [] intArray    = new int   [1]; will require 4 bytes.
long  [] longArray   = new long  [1]; will require 8 bytes.

OBJETOS

Object[] objectArray = new Object[1]; will require 4 bytes. The object can be any user defined Object.
Long  [] longArray   = new Long  [1]; will require 4 bytes.

Quero dizer que todo o objeto REFERENCE precisa de apenas 4 bytes de memória. Pode ser referência de string OU referência de objeto duplo, mas, dependendo da criação do objeto, a memória necessária variará.

por exemplo) Se eu criar um objeto para a classe abaixo ReferenceMemoryTest, 4 + 4 + 4 = 12 bytes de memória serão criados. A memória pode ser diferente quando você está tentando inicializar as referências.

 class ReferenceMemoryTest {
    public String refStr;
    public Object refObj;
    public Double refDoub; 
}

Portanto, ao criar uma matriz de objeto / referência, todo o seu conteúdo será ocupado com referências NULL. E sabemos que cada referência requer 4 bytes.

E, finalmente, a alocação de memória para o código abaixo é de 20 bytes.

ReferenceMemoryTest ref1 = new ReferenceMemoryTest (); (4 (ref1) + 12 = 16 bytes) ReferenceMemoryTest ref2 = ref1; (4 (ref2) + 16 = 20 bytes)

Kanagavelu Sugumar
fonte
1
Como um número inteiro de 4 bytes e uma referência de objeto de tamanho desconhecido cabem em 4 bytes?
Marquês de Lorne
@EJP Quero dizer que todo o objeto REFERENCE precisa de apenas 4 bytes de memória. Pode ser uma referência de seqüência de caracteres OU dupla referência de objeto, mas, dependendo da criação do objeto, a memória necessária variará.
Kanagavelu Sugumar
0

Suponha que eu declare uma classe chamada Complexcomo:

public class Complex {

    private final long real;
    private final long imaginary;

    // omitted
}

Para ver quanta memória é alocada para instâncias ativas dessa classe:

$ jmap -histo:live <pid> | grep Complex

 num     #instances         #bytes  class name (module)
-------------------------------------------------------
 327:             1             32  Complex
Ali Dehghani
fonte
-5

Para JSONObject, o código abaixo pode ajudá-lo.

`JSONObject.toString().getBytes("UTF-8").length`

retorna tamanho em bytes

Eu verifiquei com meu objeto JSONArray gravando-o em um arquivo. Está dando tamanho do objeto.

solokiran
fonte
isso funcionaria apenas para objetos que são principalmente cadeias de caracteres.
Dexter Legaspi
-6

Duvido que você queira fazê-lo programaticamente, a menos que queira fazê-lo apenas uma vez e armazená-lo para uso futuro. É uma coisa cara de se fazer. Não há operador sizeof () em Java e, mesmo que houvesse, contaria apenas o custo das referências a outros objetos e o tamanho das primitivas.

Uma maneira de fazer isso é serializar a coisa em um arquivo e observar o tamanho do arquivo, assim:

Serializable myObject;
ObjectOutputStream oos = new ObjectOutputStream (new FileOutputStream ("obj.ser"));
oos.write (myObject);
oos.close ();

Obviamente, isso pressupõe que cada objeto é distinto e não contém referências não transitórias a qualquer outra coisa.

Outra estratégia seria pegar cada objeto e examinar seus membros refletindo e somar os tamanhos (booleano e byte = 1 byte, curto e char = 2 bytes, etc.), percorrendo a hierarquia de membros. Mas isso é tedioso e caro e acaba fazendo a mesma coisa que a estratégia de serialização faria.

jodonnell
fonte
3
Seria serializado para um byte [] usando um ByteArrayOutputStream. Seria muito mais rápido do que gravá-lo em um arquivo.
ScArcher2 9/09/08
@KorayTugay A determinação do tamanho de byte de um objeto já é uma operação dispendiosa. Escrevendo cada objeto em disco para determinar o tamanho, só vai torná-lo rastejar ...
HammerNL
1
O formato do objeto serializado é totalmente diferente do formato do objeto na memória heap. Mais notavelmente, um descritor para a classe do objeto (e todas as suas superclasses serializáveis) é gravado no fluxo. Portanto, escrever uma instância simples java.lang.Integerproduz cerca de 80 bytes, onde a representação de heap geralmente é 32 (diferente da representação de fluxo de objetos, a representação de heap depende do tamanho do ponteiro e do alinhamento do objeto). Por outro lado, uma nullreferência serializada requer um byte em vez dos quatro ou oito bytes na memória heap.
Holger