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

StackOverflow https://stackoverflow.com/questions/52353

  •  09-06-2019
  •  | 
  •  

Pergunta

Por exemplo, digamos que eu tenha um aplicativo que pode ler um arquivo CSV com pilhas de linhas de dados.Forneço ao usuário um resumo do número de linhas com base nos tipos de dados, mas quero ter certeza de não ler muitas linhas de dados e causar 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 o tamanho dos tipos primitivos e das referências de objetos para um VM?

No momento, tenho um código que diz leia até 32.000 linhas, mas também gostaria de ter um código que diga para ler o máximo de linhas possível até usar 32 MB de memória.Talvez essa seja uma pergunta diferente, mas ainda assim gostaria de saber.

Foi útil?

Solução

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

UsegetObjectSize:

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

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

Invoque com:

java -javaagent:ObjectSizeFetcherAgent.jar C

Outras dicas

Você deveria usar alegre, 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 usam fortemente Unsafe, JVMTI e Serviceability Agent (SA) para decodificar o layout, a pegada e as referências reais do objeto.Isso torna o JOL muito mais preciso do que outras ferramentas que dependem de heap dumps, suposições de especificações, etc.

Para obter os tamanhos de primitivos, referências e elementos de array, 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.Isto faz incluem sobrecarga de VM para o cabeçalho do objeto, alinhamento de campo e preenchimento.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().É claro que alguns objetos na área ocupada podem ser compartilhados (também referenciados por outros objetos), portanto, é uma superaproximação do espaço que poderia ser recuperado quando esse objeto fosse coletado como lixo.Para o resultado de Pattern.compile("^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$") (Tirado de esta resposta), jol relata um espaço total de 1.840 bytes, dos quais apenas 72 são a própria instância do Padrão.

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(), jol informará o endereço, tamanho, tipo, valor e caminho das desreferências de campo para cada objeto referenciado, embora isso geralmente seja muito detalhado para ser útil.Para o exemplo de padrão contínuo, 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 "(outra coisa)" descrever outros objetos no heap que não fazem parte deste gráfico de objetos.

A melhor documentação jol é a amostras alegres no repositório jol.Os exemplos demonstram operações jol comuns e mostram como você pode usar jol para analisar componentes internos da VM e do coletor de lixo.

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

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

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

P.S.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 quando ativo.

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

Então a única forma correta é perguntar à JVM, com um bom profiler (eu uso SeuKit), o que provavelmente não é o que você deseja.

No entanto, pela descrição acima, parece que cada linha será independente e não terá uma grande árvore de dependências; 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 que se você tiver objetos com referências comuns isso não vou forneça 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 com um valor razoável.

Acidentalmente, encontrei uma classe java "jdk.nashorn.internal.ir.debug.objectsizecalCulator", já no 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

Se você quiser saber quanta memória está sendo usada em sua JVM e quanto está livre, você pode tentar algo assim:

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

editar:Achei que isso poderia ser útil, pois o autor da pergunta também afirmou que gostaria de ter uma lógica que lidasse com "ler o máximo de linhas possível até usar 32 MB de memória".

Quando eu trabalhava no Twitter, escrevi um utilitário para calcular o tamanho profundo de objetos.Ele leva em consideração diferentes modelos de memória (32 bits, oops compactados, 64 bits), preenchimento, preenchimento de subclasses, funciona corretamente em estruturas e matrizes de dados circulares.Você pode simplesmente compilar este arquivo .java;não tem dependências externas:

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

Muitas das outras respostas fornecem tamanhos rasos - por ex.o tamanho de um HashMap sem nenhuma chave ou valor, 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 um uso profundo de memória.

new MemoryMeter().measureDeep(myHashMap);

https://github.com/jbellis/jamm

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

Você tem que caminhar pelos objetos usando reflexão.Tenha cuidado ao fazer:

  • Apenas alocar um objeto gera alguma sobrecarga na JVM.O valor varia de acordo com a JVM, portanto você pode transformar esse valor em um parâmetro.Pelo menos torne-o uma constante (8 bytes?) E aplique-o a qualquer coisa alocada.
  • Só porque byte é teoricamente 1 byte não significa que seja necessário apenas um na memória.
  • Haverá loops nas referências de objetos, então você precisará manter um HashMap ou algo assim usando objetos iguais como comparador para eliminar loops infinitos.

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

Você tem que 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 normalmente estimo 40 bytes.Então você tem que olhar para os membros da classe.As referências de objeto têm 4 (8) bytes em uma JVM de 32 bits (64 bits).Os tipos primitivos são:

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

As matrizes seguem as mesmas regras;isto é, é uma referência de objeto que ocupa 4 (ou 8) bytes em seu objeto e, em seguida, seu comprimento é multiplicado pelo tamanho de seu elemento.

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

O java.lang.instrument.Instrumentation classe fornece uma ótima maneira de obter o tamanho de um objeto Java, mas exige que você defina um premain e 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 seu aplicativo.

Então eu consegui uma solução alternativa usando o Unsafe aula do sun.misc.Assim, considerando o alinhamento do heap dos 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 utilizo uma classe auxiliar UtilUnsafe para obter uma referência ao sun.misc.Unsafe objeto.

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

Há também o Medidor de memória ferramenta (anteriormente em Código do Google, agora GitHub), que é simples e publicado sob o código comercial Licença Apache 2.0, conforme discutido em um pergunta semelhante.

Ele também requer um argumento de linha de comando para o interpretador java se você quiser medir o consumo de bytes de memória, mas por outro lado parece funcionar bem, pelo menos nos cenários em que o usei.

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

Ele usa Unsafe.addressSize() para obter o tamanho de um ponteiro nativo e Unsafe.arrayIndexScale( Object[].class ) para 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 );
    }
}

Sem ter que mexer com instrumentação e assim por diante, e se você não precisa saber o tamanho exato em bytes de um objeto, você pode seguir a seguinte abordagem:

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

do your job here

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

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

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

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

Se isso for muito lento ou apenas mais problemático do que vale a pena, sempre há a boa e antiga regra prática de contagem de linhas.

Escrevi um teste rápido uma vez para estimar na hora:

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 alterações no espaço livre de heap.A chave é getFreeMemory(), qual solicita que o GC seja executado e aguarde que o tamanho de heap livre relatado se estabilize.A saída acima é:

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

Qual é o que esperamos, dado o comportamento de alinhamento e 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, onde nenhum outro thread está criando/descartando objetos.

Basta usar Java visual VM.

Ele tem tudo que você precisa para criar perfis 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)

Minha resposta é baseada no código fornecido por Nick.Esse código mede a quantidade total de bytes ocupados pelo objeto serializado.Então, isso realmente mede o material de serialização + o consumo de memória de objeto simples (apenas serializar, por exemplo int e você verá que a quantidade total de bytes serializados não é 4).Portanto, se você deseja que o número bruto de bytes seja usado exatamente para o seu objeto, você precisa 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;
    }    

}

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


ATUALIZAR: Exemplo modificado para suportar o cálculo do consumo de memória de objetos de matriz.

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

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.

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

Portanto, arrays, listas ou mapas de todas essas coleções não irão realmente armazenar objetos (apenas no momento das primitivas, o tamanho real da memória do objeto é necessário), eles armazenarão apenas referências para esses objetos.

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

  • (4/8 bytes) depende do sistema operacional (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 irá variar.

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

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

Portanto, ao criar um array 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)

Suponha que eu declare uma classe chamada Complex como:

public class Complex {

    private final long real;
    private final long imaginary;

    // omitted
}

Para ver quanta memória está alocada para instâncias ativas desta classe:

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

 num     #instances         #bytes  class name (module)
-------------------------------------------------------
 327:             1             32  Complex

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

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

retorna o tamanho em bytes

Verifiquei com meu objeto JSONArray gravando-o em um arquivo.Está dando tamanho ao objeto.

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

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

Claro, isso pressupõe que cada objeto seja distinto e não contenha referências não transitórias a mais nada.

Outra estratégia seria pegar cada objeto e examinar seus membros por reflexão e somar os tamanhos (boolean & byte = 1 byte, short & char = 2 bytes, etc.), descendo na hierarquia de membros.Mas isso é tedioso e caro e acaba fazendo a mesma coisa que a estratégia de serialização faria.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top