Calculați dimensiunea obiectului în Java [duplicate] (Programare, Java, Memorie, Gestionarea Memoriei, Structuri De Date)

Tyler Petrochko a intrebat.

Vreau să înregistrez câtă memorie (în bytes, sper) ocupă un obiect pentru un proiect (compar dimensiunile structurilor de date) și se pare că nu există o metodă de a face acest lucru în Java. Se presupune că C/C++ are sizeOf() metodă, dar aceasta este inexistentă în Java. Am încercat să înregistrez memoria liberă în JVM cu Runtime.getRuntime().freeMemory() înainte și după crearea obiectului și apoi să înregistrez diferența, dar aceasta ar da doar 0 sau 131304, și nimic între ele, indiferent de numărul de elemente din structură. Vă rog să mă ajutați!

Comentarii

  • stackoverflow.com/questions/52353/… –  > Por Sergey Grinev.
  • Poate doriți să încercați totalMemory() și apoi freeMemory() și să le scădeți în schimb. –  > Por Matt.
  • Vă recomand să aruncați o privire la Jbellis JAMM. (github.com/jbellis/jamm) Se bazează pe agentul de instrumentare menționat mai sus, dar încapsulează câteva utilități utile pentru a măsura în profunzime amprenta unui întreg obiect-grafic. Mie mi se pare simplu și destul de mișto. –  > Por Claudio.
3 răspunsuri
Hunter McMillen

Puteți utiliza java.lang.instrumentation pachet.

Acesta are o metodă care poate fi utilizată pentru a obține aproximarea specifică implementării a dimensiunii obiectului, precum și a cheltuielilor generale asociate cu obiectul.

Răspunsul pe care Sergey l-a legat are un exemplu excelent, pe care îl voi reposta aici, dar la care ar fi trebuit să vă uitați deja din comentariul său:

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

Utilizați getObjectSize:

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

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

Sursa

Comentarii

  • Spune: „Singura modalitate de a accesa o instanță a interfeței Instrumentation este ca JVM să fie lansat într-un mod care să indice clasa agentului – a se vedea specificația pachetului. Instanța Instrumentation este trecută în metoda premain a clasei agent. Odată ce un agent dobândește instanța de instrumentare, acesta poate apela oricând metode ale instanței. ” <– cum se face acest lucru (în Eclipse)? –  > Por Tyler Petrochko.
  • Nu trebuie să specific cum se lansează JVM-ul? Am încercat și îmi tot dă NullPointerException –  > Por Tyler Petrochko.
  • Editează-ți postul de mai sus cu ceea ce ai încercat. –  > Por Hunter McMillen.
  • 131

  • Nu știu cum de acest răspuns a primit atât de multe upvotes. Ignoră complet faptul că este necesar să compilați o clasă de agent pentru a o trece la JVM ca argument. Dacă nu se face acest lucru, instrumentarea va deveni nulă și va declanșa o NullPointerException. Iată răspunsul corect: stackoverflow.com/questions/52353/… –  > Por b1nary.atr0phy.
  • Trebuie să fie ceva care nu este în regulă cu această abordare atunci când încerc să găsesc dimensiunea sesiunii mele web: raportează 40, în timp ce ObjectGraphMeasurer (github.com/DimitrisAndreou/memory-measurer).) raportează „Footprint{Objects=34, References=52, Primitives=[float, int x 19, char x 257, long x 2, byte x 68557]}”. ObjectGraphMeasurer este în mod clar câștigător. –  > Por Yuci.
Louis Wasserman

Uitați-vă în https://github.com/DimitrisAndreou/memory-measurer Guava îl folosește în mod intern, iar ObjectGraphMeasurer este deosebit de simplu de utilizat din start, fără argumente speciale în linia de comandă. 

import objectexplorer.ObjectGraphMeasurer;

public class Measurer {

  public static void main(String[] args) {
    Set<Integer> hashset = new HashSet<Integer>();
    Random random = new Random();
    int n = 10000;
    for (int i = 1; i <= n; i++) {
      hashset.add(random.nextInt());
    }
    System.out.println(ObjectGraphMeasurer.measure(hashset));
  }
}

Comentarii

  • Acest lucru a fost destul de util. Chiar și celălalt a fost foarte ușor de făcut să funcționeze. Ați mai folosit acest lucru înainte? –  > Por Venki.
  • Am mai folosit Memory-measurer, da. (Contribuționez la Guava și am menționat că îl folosim intern. 😉 ) –  > Por Louis Wasserman.
  • Bănuiesc că cei mai frecvenți utilizatori de SO vin aici pentru a tergiversa – așa că, bineînțeles, ne aflăm aici tot timpul, și răspundem instantaneu la comentarii. –  > Por Louis Wasserman.
  • Acest lucru variază în funcție de JVM-ul dumneavoastră. Dacă doriți să măsurați octeți, atunci ar trebui să utilizați MemoryMeasurer din același proiect; urmați instrucțiunile de acolo. –  > Por Louis Wasserman.
  • @rodi Linear în rezultat, dar cu un factor constant destul de mare din cauza reflexiei. –  > Por Louis Wasserman.
Miguel Gamboa

The java.lang.instrument.Instrumentation oferă o modalitate plăcută de a obține dimensiunea unui obiect Java, dar necesită definirea unei clase premain și să vă rulați programul cu un agent Java. Acest lucru este foarte plictisitor atunci când nu aveți nevoie de niciun agent și atunci trebuie să furnizați un agent Jar fictiv aplicației dumneavoastră.

Așadar, am găsit o soluție alternativă folosind Unsafe din clasa sun.misc. Astfel, luând în considerare alinierea heap a obiectelor în funcție de arhitectura procesorului și calculând decalajul maxim al câmpului, puteți măsura dimensiunea unui obiect Java. În exemplul de mai jos am folosit o clasă auxiliară UtilUnsafe pentru a obține o referință la sun.misc.Unsafe obiect.

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

Comentarii

  • Nu sunt sigur de acest lucru: ((int)maxOffset/WORD) + 1)*WORD, dar cred că funcționează, deoarece sunt destul de sigur că variabilele pe 64 de biți nu trec niciodată peste o limită de 8 octeți într-o VM pe 64 de biți. În rest, un cod bun, deși ați putea, desigur, să scăpați de întregul efort de creare a hărții. Am făcut propria mea versiune care gestionează și array-uri de obiecte aici: tinybrain.de/1011517 (ar mai fi nevoie să adăugați array-uri primitive). –  > Por Stefan Reich.