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


617

Ho un'applicazione che legge 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 desidero assicurarmi di non leggere troppe righe di dati e causare OutOfMemoryErrors. Ogni riga si traduce in un oggetto. C'è 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 un VM?

In questo momento, ho un codice che dice di leggere fino a 32.000 righe , ma vorrei anche avere un codice che dice di leggere quante più righe possibili fino a quando non ho usato 32 MB di memoria. Forse questa è una domanda diversa, ma mi piacerebbe comunque saperlo.


Ho aggiunto il mio agente con configurazioni MVN e spiegato come qui: stackoverflow.com/a/36102269/711855
juanmf

Risposte:


460

È possibile utilizzare il pacchetto java.lang.instrument

Compila e metti 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

Usa getObjectSize:

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

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

Invocare con:

java -javaagent:ObjectSizeFetcherAgent.jar C

2
@Stefan Bel suggerimento! Potete per favore dire, quale sarà la dimensione del byte[0], byte[1], byte[5], int[0], int[1], int[2]utilizzando l'approccio che hai descritto? Sarebbe bello se i risultati includessero un sovraccarico per la lunghezza dell'array e l'allineamento della memoria.
dma_k

8
Ho provato questo e ho ottenuto risultati strani e inutili. Le stringhe erano sempre 32, indipendentemente dalle dimensioni. Pensavo che questa fosse forse la dimensione del puntatore, ma per un'altra classe immutabile che ho creato, ne ho 24. Funziona bene per i primitivi ma poi non hai davvero bisogno di un programma per dirti quanto è grande un carattere.
Brel,

6
@Brel questa soluzione è solo "un'approssimazione della quantità di memoria consumata dall'oggetto specificato", come specificato nella documentazione. Suppongo inoltre che gli autori abbiano deciso di impostare la dimensione di una stringa su 32 byte (solo il puntatore?) A causa del pool di stringhe Java, il che rende difficile dire se un'istanza di stringa è condivisa (memorizzata nel pool) o locale e unico per una classe.
Andrei I,

11
Come posso utilizzare ObjectSizeFetcher, se non esportare jar? Ho un progetto java di prova in eclissi.
Yura Shinkarev,

3
@brel Il motivo per cui una stringa è di soli 32 byte indipendentemente dalla lunghezza effettiva è perché la parte di lunghezza variabile di una stringa è memorizzata in un char [], che è il proprio oggetto. Per ottenere la dimensione reale di un oggetto, è necessario aggiungere la dimensione di se stesso e la dimensione di ciascun oggetto a cui fa riferimento.
tombrown52,

117

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

JOL (Java Object Layout) è la minuscola cassetta degli attrezzi per analizzare gli schemi di layout degli oggetti nelle JVM. Questi strumenti utilizzano Unsafe, JVMTI e Serviceability Agent (SA) pesantemente per decodificare il layout dell'oggetto reale, l'impronta e i riferimenti. Ciò rende JOL molto più preciso di altri strumenti che si basano su dump di heap, ipotesi di specifica, ecc.

Per ottenere le dimensioni di primitive, riferimenti ed 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 ridotta 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 altri oggetti a cui fa riferimento quella classe. Essa non comprende VM overhead per l'intestazione oggetto, allineamento campo e imbottitura. 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 vista di riepilogo delle dimensioni profonde di un'istanza di oggetto utilizzando GraphLayout.parseInstance(obj).toFootprint(). Naturalmente, alcuni oggetti nell'impronta potrebbero essere condivisi (a cui fa riferimento anche da altri oggetti), quindi si tratta di una sovrapprossimazione dello spazio che potrebbe essere recuperata quando quell'oggetto viene raccolto. 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 footprint 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 lo usi GraphLayout.parseInstance(obj).toPrintable(), jol ti dirà l'indirizzo, la dimensione, il tipo, il valore e il percorso delle dereferenze di campo a ciascun oggetto referenziato, anche se di solito sono troppi dettagli per essere utili. Per l'esempio di modello in corso, potresti ottenere quanto segue. (Probabilmente gli indirizzi 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)" descrivono altri oggetti nell'heap che non fanno parte di questo grafico di oggetti .

La migliore documentazione jol sono i campioni jol nel repository jol. Gli esempi mostrano operazioni jol comuni e mostrano come è possibile utilizzare jol per analizzare interni di VM e Garbage Collector.


18
Questa risposta dovrebbe avere più voti. Sicuramente un'ottima opzione da verificare. EDIT: verificato che questo è stato aggiunto quest'anno mentre la domanda è stata posta nel '08. Probabilmente l'opzione migliore e più semplice per fare ciò che OP ha chiesto al momento.
affitta il

4
L'autore dello strumento ha scritto un post sul blog su Jol .
Mike,

2
Per determinare la dimensione dell'oggetto "obj" utilizzare: org.openjdk.jol.info.GraphLayout.parseInstance (obj) .totalSize ();
vigore

Si noti che vmDetailsè ora VM.current().details().
Miha_x64,

Dai un'occhiata GraphLayout.parseInstance(instance).toFootprint()Ho trovato più utile capire le dimensioni degli oggetti
Mugen

82

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

i risultati:

164192
48
16
48
416

3
Lo stesso qui, stavo provando le altre soluzioni proposte sopra e mi sono imbattuto in ObjectSizeCalculator. Credo che nessuno ne abbia mai parlato prima da quando è stato recentemente introdotto sul JDK 8 come parte del progetto Nashorn . Tuttavia non ho trovato alcuna documentazione ufficiale su questa classe sul web.
Henrique Gontijo,

Non sembra considerare le lunghezze delle stringhe. Si tratta solo di dimensioni in pila?
jontejj,

1
Ho un hashmap, in cui com.carrotsearch.RamUsageEstimator restituisce circa la metà di ObjectSizeCalculator. Quale è vero? - Quale è più affidabile?
badera,

9
Si noti che ObjectSizeCalculatorè supportato solo su HotSpot VM
kellanburket il

74

Alcuni anni fa Javaworld aveva pubblicato un articolo sulla determinazione della dimensione di oggetti Java compositi e potenzialmente nidificati , che in pratica descrivono la creazione di un'implementazione sizeof () in Java. L'approccio si basa fondamentalmente su altri lavori in cui le persone hanno identificato sperimentalmente la dimensione delle primitive e degli oggetti Java tipici e quindi applicano tale conoscenza a un metodo che ricorre in modo ricorsivo al grafico di un oggetto per calcolare la dimensione totale.

Sarà sempre in qualche modo meno preciso di un'implementazione in 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 chiamato appropriatamente sizeof che offre una libreria Java5 con un'implementazione sizeof ().

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


6
L'utilità sizeof è probabilmente il modo più veloce. Fondamentalmente è quello che ha detto Stefan, ma già confezionato in un barattolo pronto per l'uso.
Alexandre L Telles,

62

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 fa riferimento (il grafico di riferimento). Potresti indicare la dimensione in memoria o la dimensione sul disco. E alla JVM è consentito ottimizzare cose come le stringhe.

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

Tuttavia, dalla descrizione sopra sembra che ogni riga sia autonoma e non abbia 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 darà il risultato corretto e la dimensione della serializzazione non corrisponderà sempre alla dimensione in memoria, ma è una buona approssimazione. Il codice sarà un po 'più efficiente se si inizializza la dimensione ByteArrayOutputStream su un valore sensibile.


2
Mi piace questo approccio. Quanto lontano in termini di dimensioni dell'oggetto sei stato fuori.
Berlin Brown,

1
Molto semplice ed efficace Altri metodi sono troppo disordinati (specialmente all'interno di Eclipse RCP). Grazie.
marcolopes,

19
La serializzazione non terrà traccia delle variabili transitorie e il metodo di serializzazione predefinito scrive le stringhe in UTF-8, quindi qualsiasi carattere ANSI prenderà solo un byte. Se hai molte stringhe, le tue dimensioni saranno così lontane da essere inutili.
TMN,

1
anche se questo potrebbe non dare le dimensioni esatte, per le mie esigenze avevo solo bisogno di un confronto tra 2 oggetti e SizeOf non si inizializzava da un'app web. Grazie!
Isaac,

1
Buona raccomandazione di YourKit . Altre alternative sono VirtualVM e jvmmonitor
angelcervera,

38

Se vuoi solo sapere quanta memoria viene utilizzata nella tua JVM e quanto è gratuita, puoi 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();

modifica: ho pensato che ciò potesse essere utile in quanto anche l'autore della domanda ha affermato che vorrebbe avere una logica che gestisca "leggi quante più righe possibile finché non avrò usato 32 MB di memoria".


24
Questa non è una buona soluzione, poiché non si sa mai quando accadrà una raccolta rifiuti o quanta memoria aggiuntiva verrà allocata all'heap in una sola volta.
Nick Fortescue,

5
Questo è vero, e non intendo che questo affronti la domanda principale di questo post, ma potrebbe aiutarlo a sapere a livello di codice quando si sta avvicinando un po 'alla dimensione massima dell'heap.
matt b,

1
Un altro problema di questa soluzione è quando ci si trova in un ambiente multi-thread (come in un server Web). È possibile che altri thread fossero in esecuzione e consumassero memoria. Con questa approssimazione stai calcolando la memoria utilizzata in tutta la macchina virtuale.
angelcervera,

1
Un altro svantaggio è che freeMemory restituisce un'approssimazione. Prova a creare un oggetto javax.crypto.Cipher. La differenza tra due chiamate a freeMemory (per stimare la dimensione di un codice) non è costante!
Eugen,

1
Credo che tu possa forzare una raccolta dei rifiuti, quindi puoi fare alcune cose in questo approccio.
matanster,

24

Quando lavoravo su Twitter, ho scritto un'utilità per il calcolo delle dimensioni degli oggetti profondi. Tiene conto di diversi modelli di memoria (32-bit, oops compressi, 64-bit), padding, padding di sottoclassi, funziona correttamente su strutture e matrici 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


1
Szia! Vorrei solo urlare la tua presentazione : le diapositive 15-20 sono fantastiche per aiutarti a dare un'idea istintiva del costo di varie decisioni sulla struttura dei dati. Grazie per averlo pubblicato!
Luke Usherwood,

16
"non ha dipendenze esterne" - da quando guava non è una dipendenza esterna?
l4mpi,


Guave è una dipendenza esterna.
Mert Serimer,

18

Molte delle altre risposte forniscono dimensioni poco profonde, ad esempio le dimensioni di una HashMap senza nessuna delle chiavi o dei valori, che probabilmente non è quello che desideri.

Il progetto jamm usa il pacchetto java.lang.instrumentation sopra ma cammina sull'albero e quindi può darti l'uso della memoria profonda.

new MemoryMeter().measureDeep(myHashMap);

https://github.com/jbellis/jamm

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


11

Devi camminare gli oggetti usando la riflessione. Stai attento mentre fai:

  • La semplice allocazione di un oggetto ha un certo sovraccarico nella JVM. L'importo varia in base a JVM, pertanto è possibile impostare questo valore come parametro. Almeno renderlo una costante (8 byte?) E applicare a tutto ciò che è allocato.
  • Solo perché byteteoricamente 1 byte non significa che ne richiede solo uno in memoria.
  • Ci saranno dei loop nei riferimenti agli oggetti, quindi dovrai tenere un HashMapo qualche altro usando object-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 ignorare i metodi standard.


Le dimensioni di varie primitive non sono definite nella specifica Java? (§2.4.1)
erickson,

4
Non nel senso di "quanta memoria occupa", qual è la domanda. Solo nel senso di come funzionano. Ad esempio, byte, caratteri e short occupano un'intera parola nello stack Java, anche se funzionano con arrotondamenti ecc.
Jason Cohen,

1
Sembra simile alla misurazione delle dimensioni, come mostrato da Heinz nella sua Newsletter n. 78: javaspecialists.eu/archive/Issue078.html . L'ho usato. Il suo approccio funziona.
Peter Kofler,

8

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

C'è un overhead fisso per oggetto. È specifico per JVM, ma di solito stima 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
  • char e short: 2 byte
  • int e float: 4 byte
  • lungo e doppio: 8 byte

Le matrici seguono le stesse regole; vale a dire, è un riferimento all'oggetto in modo che occupi 4 (o 8) byte nel tuo oggetto, e quindi la sua lunghezza moltiplicata per la dimensione del suo elemento.

Cercare di farlo in modo programmatico con le chiamate Runtime.freeMemory()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.


@erickson Non sarei sicuro di sizeof (booleano) == 1 guardando questo thread ( stackoverflow.com/questions/1907318/… ). Puoi per favore commentare questo?
dma_k

2
@dma_k, Java in realtà non ha veri booleani. La dimensione del valore booleano è 4 byte esterni alle matrici e 1 byte all'interno boolean[]. In realtà tutti i tipi primitivi non doppi / lunghi sono 4 byte. Questi ultimi sono 8 (la risposta erroneamente li pone anche come 4)
bestsss

@bestsss: per essere più precisi, l'allocazione minima della memoria dipende dalla piattaforma e dall'implementazione di JVM. Anche gli oggetti sull'heap sono allineati, quindi dopo aver riassunto tutte le dimensioni è necessario arrotondare per eccesso.
dma_k,

6

La java.lang.instrument.Instrumentationclasse fornisce un modo piacevole per ottenere le dimensioni di un oggetto Java, ma richiede di definire a premained eseguire il 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 usando la Unsafeclasse dal sun.misc. Pertanto, considerando l'allineamento dell'heap degli oggetti in base all'architettura del processore e calcolando l'offset del campo massimo, è possibile misurare la dimensione di un oggetto Java. Nell'esempio seguente uso una classe ausiliaria UtilUnsafeper ottenere un riferimento sun.misc.Unsafeall'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() { }
}

Approccio interessante, ma ciò non suppone che la memoria dell'oggetto e dei suoi campi non sia frammentata?
nicoulaj,

Sì e non conosco alcuna implementazione JVM che rende tale frammentazione.
Miguel Gamboa,

Non capisco. La frammentazione non è un'opzione :) Prendiamo l'esempio dell'oggetto C che è memorizzato come un campo di oggetti A e B. Non sposta tutto in A o B?
nicoulaj,

Scusa, ma non capisco neanche il tuo punto di vista. Secondo la mia interpretazione, in Java gli oggetti non possono essere memorizzati all'interno di altri oggetti, come accade con le strutture C o Tipi di valore in .Net. Quindi quando dici: "oggetto C che è memorizzato come un campo di oggetti A e B" significa che gli oggetti A e B hanno campi che memorizzano riferimenti (puntatori) all'oggetto C. Quindi le dimensioni di A e B sono uguali a l'offset di quel campo più la dimensione di un riferimento (puntatore) all'oggetto C. E la dimensione di un riferimento è la dimensione di una parola.
Miguel Gamboa,

Oh, ok, stiamo parlando di dimensioni ridotte. Colpa mia.
nicoulaj,

6

Esiste anche lo strumento Memory Measurer (precedentemente in Google Code , ora su GitHub ), che è semplice e pubblicato sotto la licenza commerciale Apache 2.0 , come discusso in una domanda simile .

Inoltre, richiede un argomento da riga di comando all'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 usato.


4

Senza dover fare confusione con la strumentazione e così via, e se non hai bisogno di conoscere la dimensione esatta del 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 leggi la memoria usata prima e dopo, e chiamando il GC appena prima di ottenere la memoria usata riduci 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 l'esecuzione. Ancora di più, puoi eseguire il tutto più volte e fare una media.


5
Non ti System.gc()avvisa solo che vuoi GC? Non è garantito che il GC sia chiamato affatto.
Raildex,

@veramente bello. Questo non è sicuro perché potresti non fare ciò che GC fa o influire sulla memoria tra le tue linee. Quindi "tra" due metodi freeMemory GC può liberare più spazio che non consideri, quindi il tuo oggetto apparirà più piccolo
Mert Serimer

@MertSerimer "non sicuro" è per me un livello completamente diverso: al massimo non è così preciso, come ho anche affermato. Inoltre, non puoi guidare il GC (come affermato da Raildex), ma anche in questo caso ho suggerito di inserirlo in un ciclo. Questo è solo un sistema rapido, sporco e approssimativo che funziona se il risultato non deve essere molto affidabile, come affermato.
reallynice

Ci sono molti problemi con questo, ma ti dà una buona swag.
markthegrea,

3

Ecco un'utilità che ho creato usando 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 le dimensioni di un puntatore nativo e Unsafe.arrayIndexScale( Object[].class )per le dimensioni di un riferimento Java.

Utilizza l'offset di campo di una classe nota per calcolare la dimensione di 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 );
    }
}

Hai testato questa classe con valori? Ho provato, ma per me valori errati !!!.
Débora il

1
I valori che mi ha dato per un oggetto semplice erano circa corretti, ma disattivati ​​di un fattore 10 per un elenco contenente 1mio oggetti. Comunque, ottimo lavoro!
Michael Böckling,

Interessante. L'ho provato usando JDK7u67, su Windows 7 x64 e Linux 2.6.16 / x86_64, usando ciascuna delle modalità di indirizzo a 32 bit / 64 bit / oop. L'ho confrontato con i dump della memoria analizzati in Eclipse Memory Analyzer 1.3.x. Quale configurazione stai usando? Hai un esempio specifico che potrei provare?
dlaudams,

La scelta migliore che posso fare. Non posso usarlo Instrumentationperché non avvio Tomcat, ObjectSizeCalculatorperché non sono sicuro del tipo di VM (HotSpot) e dei JOLbean di primavera bacouse. Uso questo e aggiungo un secondo parametro per ignorare i singletons, vale a dire il codice AbstractRefreshableApplicationContext.getBeanFactory().getSingletonMutex()refactor e quello internalSizeOfper ignorare Class ed Enum
Perlos

Per confrontare i risultati, utilizzare ObjectSizeCalculator (calcolare l'intero server da 1 GB a 10 secondi). JOL causa MemError (6 GB non essere invidiato) e non ottengo gli stessi risultati, probabilmente perché enum.
Perlos,

3

Stavo cercando un calcolo di runtime di una dimensione dell'oggetto che soddisfacesse i seguenti requisiti:

  • Disponibile in fase di esecuzione senza necessità di includere la strumentazione.
  • Funziona con Java 9+ senza accesso a Unsafe.
  • Si basa solo sulla classe. Non una dimensione profonda di quella che prende in considerazione la lunghezza delle stringhe, la lunghezza dell'array, ecc.

Quanto segue si basa sul codice di base dell'articolo originale degli specialisti Java ( https://www.javaspecialists.eu/archive/Issue078.html ) e alcuni bit della versione non sicura in un'altra risposta a questa domanda.

Spero che qualcuno lo trovi utile.

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

}


2

Non c'è una chiamata di metodo, se è quello che stai chiedendo. Con un po 'di ricerca, suppongo che tu possa scrivere il tuo. Un'istanza particolare ha una dimensione fissa derivata dal numero di riferimenti e valori primitivi più i dati di contabilità dell'istanza. Cammineresti semplicemente sul grafico dell'oggetto. Meno vari sono i tipi di riga, più facile.

Se è troppo lento o solo più problemi di quanti ne valga la pena, c'è sempre una buona vecchia fila che conta la regola dei pollici.


2

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 è di allocare oggetti e misurare il cambiamento nello spazio di heap libero. Essendo la chiave getFreeMemory(), che richiede l'esecuzione di GC e attende la stabilizzazione della dimensione dell'heap disponibile riportata . L'output di quanto sopra è:

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

Questo è quello 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 sta creando / scartando oggetti.


2

Usa solo Java Visual VM.

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

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


2

Quando si utilizza JetBrains IntelliJ, abilitare innanzitutto "Collega agente di memoria" in File | Impostazioni | Build, Execution, Deployment | Debugger.

Durante il debug, fai clic con il pulsante destro del mouse su una variabile di interesse e scegli "Calcola dimensione mantenuta": Calcola dimensione mantenuta


1

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 roba di serializzazione + footprint di memoria di oggetti semplici (basta serializzare ad esempio inte vedrai che la quantità totale di byte serializzati non lo è 4). Quindi, se vuoi ottenere un numero di byte non elaborato 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 banali classi. Potrebbero anche non esserci casi coperti.


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


0

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


0
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 di oggetti e che in genere è la dimensione dell'oggetto.


cosa succede se GC viene eseguito al centro durante // Code per la costruzione di oggetti? Ora potrebbe dare sempre risultati corretti.
Rajugaadu,

0

Questa risposta non è correlata alla dimensione dell'oggetto, ma quando si utilizza l'array per ospitare gli oggetti; la quantità di memoria che verrà allocata per l'oggetto.

Quindi le matrici, gli elenchi o le mappe di tutte quelle raccolte non memorizzeranno realmente gli oggetti (solo al momento delle primitive, è necessaria la dimensione della memoria dell'oggetto reale), 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. Può essere riferimento alla stringa O riferimento a doppio oggetto, ma dipende dalla creazione dell'oggetto la memoria necessaria varierà.

es.) Se creo un oggetto per la classe sottostante, ReferenceMemoryTestverranno 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; 
}

Quindi quando si creano array di oggetti / riferimenti, tutto il suo contenuto sarà occupato con riferimenti NULL. E sappiamo che ogni riferimento richiede 4 byte.

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

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


1
In che modo un numero intero a 4 byte e un riferimento a un oggetto di dimensione sconosciuta possono rientrare in 4 byte?
Marchese di Lorne,

@EJP Intendo dire che tutto l'oggetto REFERENCE necessita solo di 4 byte di memoria. Può essere riferimento alla stringa O riferimento a doppio oggetto, ma dipende dalla creazione dell'oggetto la memoria necessaria varierà.
Kanagavelu Sugumar

0

Supponiamo che dichiaro una classe chiamata Complexcome:

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

-5

Per JSONObject il seguente codice può aiutarti.

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

restituisce la dimensione in byte

L'ho controllato con il mio oggetto JSONArray scrivendolo in un file. Sta dando le dimensioni dell'oggetto.


questo funzionerebbe solo per oggetti che sono per lo più stringhe.
Dexter Legaspi,

-6

Dubito che tu voglia farlo a livello di programmazione a meno che tu non voglia semplicemente farlo una volta e conservarlo per un uso futuro. È una cosa costosa da fare. Non c'è un operatore sizeof () in Java, e anche se ci fosse, conterebbe solo il costo dei riferimenti ad altri oggetti e la dimensione delle primitive.

Un modo in cui potresti farlo è serializzare l'oggetto su un file e osservare le dimensioni 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 ogni oggetto ed esaminare i suoi membri per riflessione e sommare le dimensioni (booleano e byte = 1 byte, corto e char = 2 byte, ecc.), Scendendo lungo la gerarchia di appartenenza. Ma è noioso e costoso e finisce per fare la stessa cosa che farebbe la strategia di serializzazione.


3
Lo serializzerei su un byte [] usando un ByteArrayOutputStream. Sarebbe molto più veloce che scriverlo su un file.
ScArcher2,

@KorayTugay Determinare la dimensione in byte di un oggetto è già un'operazione costosa. Scrivere ogni oggetto su disco per determinare la dimensione, lo farà semplicemente strisciare ...
HammerNL

1
Il formato dell'oggetto serializzato è completamente diverso dal formato dell'oggetto nella memoria dell'heap. In particolare, un descrittore per la classe dell'oggetto (e tutte le sue superclassi serializzabili) viene scritto nel flusso. Quindi scrivere una semplice istanza di java.lang.Integerproduce circa 80 byte, in cui la rappresentazione di heap di solito è 32 (a differenza della rappresentazione del flusso di oggetti, la rappresentazione di heap dipende dalle dimensioni del puntatore e dall'allineamento degli oggetti). Al contrario, un nullriferimento serializzato richiede un byte anziché i quattro o otto byte nella memoria dell'heap.
Holger,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.