In Java, qual è il modo migliore per determinare la dimensione di un oggetto?

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

  •  09-06-2019
  •  | 
  •  

Domanda

Ad esempio, supponiamo di avere un'applicazione in grado di leggere un file CSV con pile di righe di dati.Fornisco all'utente un riepilogo del numero di righe in base ai tipi di dati, ma voglio assicurarmi di non leggere troppe righe di dati e causare OutOfMemoryErrorS.Ogni riga si traduce in un oggetto.Esiste un modo semplice per scoprire la dimensione di quell'oggetto a livello di codice?Esiste un riferimento che definisce quanto sono grandi i tipi primitivi e i riferimenti agli oggetti per a VM?

In questo momento, ho un codice che dice read up to 32.000 righe, ma mi piacerebbe anche avere un codice che dica di leggere quante più righe possibili finché non le avrò usate 32 MB di memoria.Magari la domanda è diversa, ma mi piacerebbe comunque saperlo.

È stato utile?

Soluzione

Puoi usare il pacchetto java.lang.instrument

Compila e inserisci questa classe in un 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);
    }
}

Aggiungi quanto segue al tuo MANIFEST.MF:

Premain-Class: ObjectSizeFetcher

Utilizza getObjectSize:

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

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

Invoca con:

java -javaagent:ObjectSizeFetcherAgent.jar C

Altri suggerimenti

Dovresti usare jol, uno strumento sviluppato come parte del progetto OpenJDK.

JOL (Java Object Layout) è il piccolo strumento per analizzare gli schemi di layout degli oggetti nelle JVM.Questi strumenti utilizzano pesantemente Unsafe, JVMTI e Serviceability Agent (SA) per decodificare il layout, l'impronta e i riferimenti effettivi dell'oggetto.Ciò rende JOL molto più accurato rispetto ad altri strumenti che si basano su dump dell'heap, ipotesi di specifiche, ecc.

Per ottenere le dimensioni delle primitive, dei riferimenti e degli elementi dell'array, utilizzare VMSupport.vmDetails().Su Oracle JDK 1.8.0_40 in esecuzione su Windows a 64 bit (utilizzato per tutti gli esempi seguenti), questo metodo restituisce

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]

È possibile ottenere la dimensione superficiale di un'istanza di oggetto utilizzando ClassLayout.parseClass(Foo.class).toPrintable() (facoltativamente passando un'istanza a toPrintable).Questo è solo lo spazio consumato da una singola istanza di quella classe;non include nessun altro oggetto a cui fa riferimento quella classe.Esso fa includere l'overhead della VM per l'intestazione dell'oggetto, l'allineamento dei campi e il riempimento.Per 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

È possibile ottenere una visualizzazione riepilogativa delle dimensioni profonde di un'istanza di oggetto utilizzando GraphLayout.parseInstance(obj).toFootprint().Naturalmente, alcuni oggetti nell'impronta potrebbero essere condivisi (anche referenziati da altri oggetti), quindi si tratta di un'approssimazione eccessiva dello spazio che potrebbe essere recuperato quando quell'oggetto viene sottoposto a garbage collection.Per il risultato di Pattern.compile("^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$") (preso da questa risposta), jol riporta un'impronta totale di 1840 byte, di cui solo 72 sono l'istanza Pattern stessa.

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 invece usi GraphLayout.parseInstance(obj).toPrintable(), jol ti dirà l'indirizzo, la dimensione, il tipo, il valore e il percorso dei riferimenti dei campi a ciascun oggetto a cui si fa riferimento, anche se di solito sono troppi dettagli per essere utili.Per l'esempio del modello in corso, potresti ottenere quanto segue.(Gli indirizzi probabilmente cambieranno tra le esecuzioni.)

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)

Le voci "(qualcos'altro)". descrivi altri oggetti nell'heap che non fanno parte di questo oggetto grafico.

La migliore documentazione jol è il campioni jol nel repository jol.Gli esempi illustrano le operazioni jol comuni e mostrano come utilizzare jol per analizzare gli interni della VM e del Garbage Collector.

Alcuni anni fa Javaworld aveva un articolo sulla determinazione della dimensione degli oggetti Java compositi e potenzialmente annidati, fondamentalmente attraversano la creazione di un'implementazione sizeof() in Java.L'approccio si basa fondamentalmente su un altro lavoro in cui le persone identificano sperimentalmente la dimensione delle primitive e dei tipici oggetti Java e quindi applicano tale conoscenza a un metodo che percorre ricorsivamente un grafico di oggetti per calcolare la dimensione totale.

Sarà sempre un po' meno accurato di un'implementazione C nativa semplicemente a causa delle cose che accadono dietro le quinte di una classe, ma dovrebbe essere un buon indicatore.

In alternativa un progetto SourceForge opportunamente chiamato taglia di che offre una libreria Java5 con un'implementazione sizeof().

PSNon utilizzare l'approccio della serializzazione, non esiste alcuna correlazione tra la dimensione di un oggetto serializzato e la quantità di memoria che consuma quando è attivo.

Innanzitutto "la dimensione di un oggetto" non è un concetto ben definito in Java.Potresti intendere l'oggetto stesso, con solo i suoi membri, l'Oggetto e tutti gli oggetti a cui si riferisce (il grafico di riferimento).Potresti intendere la dimensione in memoria o la dimensione su disco.E la JVM può ottimizzare cose come Strings.

Quindi l'unico modo corretto è chiedere alla JVM, con un buon profiler (io uso Il tuoKit), che probabilmente non è quello che desideri.

Tuttavia, dalla descrizione sopra sembra che ogni riga sarà autonoma e non avrà un grande albero delle dipendenze, quindi il metodo di serializzazione sarà probabilmente una buona approssimazione sulla maggior parte delle JVM.Il modo più semplice per farlo è il seguente:

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

Ricorda che se hai oggetti con riferimenti comuni questo non lo farà fornisce il risultato corretto e la dimensione della serializzazione non sempre corrisponderà alla dimensione in memoria, ma è una buona approssimazione.Il codice sarà un po' più efficiente se inizializzi la dimensione ByteArrayOutputStream su un valore ragionevole.

Ho trovato accidentalmente una classe Java "jdk.nashorn.internal.ir.debug.objectsizeCalculator", già in JDK, che è facile da usare e sembra abbastanza utile per determinare la dimensione di un oggetto.

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

risultati:

164192
48
16
48
416

Se desideri semplicemente sapere quanta memoria viene utilizzata nella tua JVM e quanta è gratuita, potresti provare qualcosa del genere:

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

modificare:Ho pensato che questo potrebbe essere utile poiché l'autore della domanda ha anche affermato che gli piacerebbe avere una logica che gestisca "leggere quante più righe possibile finché non ho utilizzato 32 MB di memoria".

Quando lavoravo su Twitter, ho scritto un'utilità per calcolare la dimensione profonda degli oggetti.Tiene conto di diversi modelli di memoria (32 bit, compressi oops, 64 bit), riempimento, riempimento di sottoclassi e funziona correttamente su strutture e array di dati circolari.Puoi semplicemente compilare questo file .java;non ha dipendenze esterne:

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

Gran parte delle altre risposte forniscono dimensioni poco profonde, ad es.la dimensione di una HashMap senza nessuna chiave o valore, che probabilmente non è quello che desideri.

Il progetto jamm utilizza il pacchetto java.lang.instrumentation sopra ma cammina sull'albero e quindi può darti un utilizzo profondo della memoria.

new MemoryMeter().measureDeep(myHashMap);

https://github.com/jbellis/jamm

Per utilizzare MemoryMeter, avviare la JVM con "-javaagent:/jamm.jar"

Devi camminare sugli oggetti usando la riflessione.Fai attenzione mentre fai:

  • La semplice allocazione di un oggetto comporta un sovraccarico nella JVM.L'importo varia in base alla JVM, quindi potresti rendere questo valore un parametro.Almeno rendilo una costante (8 byte?) e applicalo a qualsiasi cosa allocata.
  • Solo perché byte è teoricamente 1 byte non significa che ne occorre solo uno in memoria.
  • Ci saranno loop nei riferimenti agli oggetti, quindi dovrai mantenere a HashMap o qualcosa del genere utilizzando oggetto-uguale come comparatore per eliminare i loop infiniti.

@jodonnell:Mi piace la semplicità della tua soluzione, ma molti oggetti non sono serializzabili (quindi ciò genererebbe un'eccezione), i campi possono essere transitori e gli oggetti possono sovrascrivere i metodi standard.

Devi misurarlo con uno strumento o stimarlo a mano e dipende dalla JVM che stai utilizzando.

C'è un sovraccarico fisso per oggetto.È specifico per JVM, ma di solito stimo 40 byte.Quindi devi guardare i membri della classe.I riferimenti agli oggetti sono 4 (8) byte in una JVM a 32 bit (64 bit).I tipi primitivi sono:

  • booleano e byte:1 byte
  • carattere e breve:2 byte
  • int e float:4 byte
  • lungo e doppio:8 byte

Gli array seguono le stesse regole;cioè, è un riferimento a un oggetto, quindi richiede 4 (o 8) byte nel tuo oggetto, quindi la sua lunghezza moltiplicata per la dimensione del suo elemento.

Cercando di farlo a livello di codice con le chiamate a Runtime.freeMemory() semplicemente non ti dà molta precisione, a causa delle chiamate asincrone al garbage collector, ecc.La profilazione dell'heap con -Xrunhprof o altri strumenti ti darà i risultati più accurati.

IL java.lang.instrument.Instrumentation fornisce un buon modo per ottenere la dimensione di un oggetto Java, ma richiede la definizione di a premain ed esegui il tuo programma con un agente Java.Questo è molto noioso quando non hai bisogno di alcun agente e quindi devi fornire un agente Jar fittizio alla tua applicazione.

Quindi ho ottenuto una soluzione alternativa utilizzando il file Unsafe classe da sun.misc.Quindi, considerando l'allineamento dell'heap degli oggetti in base all'architettura del processore e calcolando l'offset massimo del campo, è possibile misurare la dimensione di un oggetto Java.Nell'esempio seguente utilizzo una classe ausiliaria UtilUnsafe per ottenere un riferimento a sun.misc.Unsafe oggetto.

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

C'è anche il Misuratore della memoria strumento (precedentemente at Codice Google, ora in poi GitHub), che è semplice e pubblicato in formato commerciale Licenza Apache 2.0, come discusso in a domanda simile.

Anch'esso richiede un argomento della riga di comando per l'interprete Java se si desidera misurare il consumo di byte di memoria, ma per il resto sembra funzionare bene, almeno negli scenari in cui l'ho utilizzato.

Ecco un'utilità che ho realizzato utilizzando alcuni degli esempi collegati per gestire 32 bit, 64 bit e 64 bit con OOP compresso.Utilizza sun.misc.Unsafe.

Utilizza Unsafe.addressSize() per ottenere la dimensione di un puntatore nativo e Unsafe.arrayIndexScale( Object[].class ) per la dimensione di un riferimento Java.

Utilizza l'offset del campo di una classe conosciuta per calcolare la dimensione base di un oggetto.

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

Senza dover fare confusione con la strumentazione e così via, e se non hai bisogno di conoscere la dimensione esatta in byte di un oggetto, potresti seguire il seguente approccio:

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

do your job here

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

In questo modo si legge la memoria utilizzata prima e dopo, e richiamando il GC appena prima di ottenere la memoria utilizzata si abbassa il "rumore" quasi a 0.

Per un risultato più affidabile è possibile eseguire il lavoro n volte, quindi dividere la memoria utilizzata per n, ottenendo la quantità di memoria necessaria per un'esecuzione.Inoltre, puoi eseguire il tutto più volte e fare una media.

Non c'è una chiamata al metodo, se è quello che stai chiedendo.Con una piccola ricerca, suppongo che potresti scriverne uno tuo.Una particolare istanza ha una dimensione fissa derivata dal numero di riferimenti e valori primitivi più i dati contabili dell'istanza.Cammineresti semplicemente sul grafico dell'oggetto.Meno vari sono i tipi di riga, più facile è.

Se è troppo lento o semplicemente più complicato di quanto valga, c'è sempre la buona vecchia regola del conteggio delle righe.

Ho scritto un test rapido una volta per stimare al volo:

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

}

Il concetto generale è allocare oggetti e misurare il cambiamento nello spazio heap libero.L'essere chiave getFreeMemory(), Quale richiede l'esecuzione di GC e attende che la dimensione dell'heap libero segnalata si stabilizzi.L'output di quanto sopra è:

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

Questo è ciò che ci aspettiamo, dato il comportamento di allineamento e il possibile sovraccarico dell'intestazione del blocco heap.

Il metodo di strumentazione dettagliato nella risposta accettata qui è il più accurato.Il metodo che ho descritto è accurato ma solo in condizioni controllate in cui nessun altro thread crea/elimina oggetti.

Basta usare Java Visual VM.

Ha tutto il necessario per profilare ed eseguire il debug dei problemi di memoria.

Dispone anche di una console OQL (Object Query Language) che ti consente di fare molte cose utili, una delle quali è sizeof(o)

La mia risposta si basa sul codice fornito da Nick.Tale codice misura la quantità totale di byte occupati dall'oggetto serializzato.Quindi questo in realtà misura il materiale di serializzazione + l'impronta della memoria dell'oggetto semplice (basta serializzare per esempio int e vedrai che la quantità totale di byte serializzati non lo è 4).Quindi, se vuoi che il numero di byte non elaborato venga utilizzato esattamente per il tuo oggetto, devi modificare un po' quel codice.Così:

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

}

Ho testato questa soluzione con tipi primitivi, String e su alcune classi banali.Potrebbero esserci anche casi non coperti.


AGGIORNAMENTO: Esempio modificato per supportare il calcolo dell'impronta di memoria degli oggetti dell'array.

Potresti generare un dump dell'heap (con jmap, ad esempio) e quindi analizzare l'output per trovare le dimensioni degli oggetti.Questa è una soluzione offline, ma puoi esaminare dimensioni superficiali e profonde, ecc.

long heapSizeBefore = Runtime.getRuntime().totalMemory();

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

size ti dà l'aumento dell'utilizzo della memoria di jvm a causa della creazione dell'oggetto e questa in genere è la dimensione dell'oggetto.

Questa risposta non è correlata alla dimensione dell'oggetto, ma quando si utilizza l'array per accogliere gli oggetti;quanta memoria verrà allocata per l'oggetto.

Quindi gli array, gli elenchi o le mappe di tutte quelle raccolte non memorizzeranno realmente gli oggetti (solo al momento delle primitive, è necessaria la dimensione della memoria degli oggetti reali), memorizzeranno solo i riferimenti per quegli oggetti.

Ora il Used heap memory = sizeOfObj + sizeOfRef (* 4 bytes) in collection

  • (4/8 byte) dipende dal sistema operativo (32/64 bit).

PRIMITIVI

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

OGGETTI

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.

Voglio dire che tutto l'oggetto REFERENCE necessita solo di 4 byte di memoria.Potrebbe essere un riferimento a una stringa o un riferimento a un oggetto doppio, ma a seconda della creazione dell'oggetto la memoria necessaria varierà.

ad esempio) Se creo un oggetto per la classe seguente ReferenceMemoryTest quindi verranno creati 4 + 4 + 4 = 12 byte di memoria.La memoria potrebbe differire quando si tenta di inizializzare i riferimenti.

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

Pertanto, quando si crea un array di oggetti/riferimenti, tutto il suo contenuto sarà occupato da riferimenti NULL.E sappiamo che ogni riferimento richiede 4 byte.

Infine, l'allocazione di memoria per il codice seguente è di 20 byte.

ReferenceMemoryTest ref1 = nuovo ReferenceMemoryTest();(4 (Ref1) + 12 = 16 byte) referenceMemoryTest ref2 = ref1;( 4(rif2) + 16 = 20 byte)

Supponiamo che io dichiari una classe denominata Complex Piace:

public class Complex {

    private final long real;
    private final long imaginary;

    // omitted
}

Per vedere quanta memoria è allocata alle istanze live di questa classe:

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

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

Per JSONObject il codice seguente può aiutarti.

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

restituisce la dimensione in byte

L'ho controllato con il mio oggetto JSONArray scrivendolo su un file.Sta dando la dimensione dell'oggetto.

Dubito che tu voglia farlo a livello di codice a meno che tu non voglia farlo solo una volta e memorizzarlo per un uso futuro.È una cosa costosa da fare.Non esiste un operatore sizeof() in Java e, anche se esistesse, conterebbe solo il costo dei riferimenti ad altri oggetti e la dimensione delle primitive.

Un modo per farlo è serializzare l'oggetto in un file e osservare la dimensione del file, in questo modo:

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

Naturalmente, ciò presuppone che ogni oggetto sia distinto e non contenga riferimenti non transitori a nient'altro.

Un'altra strategia sarebbe quella di prendere ciascun oggetto ed esaminare i suoi membri riflettendo e sommare le dimensioni (booleano e byte = 1 byte, breve e carattere = 2 byte, ecc.), procedendo verso il basso lungo la gerarchia di appartenenza.Ma questo è noioso e costoso e finisce per fare la stessa cosa che farebbe la strategia di serializzazione.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top