W Javie jaki jest najlepszy sposób na określenie rozmiaru obiektu?

616

Mam aplikację, która odczytuje plik CSV ze stosami wierszy danych. Daję użytkownikowi podsumowanie liczby wierszy na podstawie typów danych, ale chcę się upewnić, że nie czytam zbyt wielu wierszy danych i nie powodują OutOfMemoryError. Każdy wiersz przekłada się na obiekt. Czy istnieje prosty sposób na programowe ustalenie wielkości tego obiektu? Czy istnieje odniesienie, które określa, jak duże są typy pierwotne i odwołania do obiektów dla VM?

W tej chwili mam kod z informacją o odczytaniu do 32 000 wierszy , ale chciałbym też mieć kod z informacją, aby odczytać jak najwięcej wierszy, dopóki nie użyję 32 MB pamięci. Może to inne pytanie, ale nadal chciałbym wiedzieć.

Jay R.
źródło
Dodałem mojego agenta z konfiguracjami mvn i wyjaśniłem, jak tutaj: stackoverflow.com/a/36102269/711855
juanmf

Odpowiedzi:

461

Możesz użyć pakietu java.lang.instrument

Skompiluj i umieść tę klasę w pliku 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);
    }
}

Dodaj następujące elementy do MANIFEST.MF:

Premain-Class: ObjectSizeFetcher

Użyj getObjectSize:

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

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

Wywołaj z:

java -javaagent:ObjectSizeFetcherAgent.jar C
Stefan Karlsson
źródło
2
@Stefan Ładna wskazówka! Czy możesz powiedzieć, jaka będzie wielkość byte[0], byte[1], byte[5], int[0], int[1], int[2]stosując podejście opisałeś? Byłoby miło, gdyby wyniki obejmowały narzut związany z długością wyrównania tablicy i pamięci.
dma_k
8
Próbowałem tego i uzyskałem dziwne i nieprzydatne wyniki. Ciągi zawsze miały 32, niezależnie od wielkości. Pomyślałem, że to może rozmiar wskaźnika, ale dla innej niezmiennej klasy, którą stworzyłem, mam 24. Działa dobrze dla prymitywów, ale tak naprawdę nie potrzebujesz programu, który powiedziałby ci, jak duży jest znak.
Brel
6
@Brel to rozwiązanie jest jedynie „przybliżeniem ilości miejsca zajmowanego przez określony obiekt”, jak określono w dokumentacji. Przypuszczam również, że autorzy postanowili ustawić rozmiar ciągu na 32 bajty (tylko wskaźnik?) Z powodu puli napisów w Javie, co utrudnia stwierdzenie, czy instancja String jest współużytkowana (przechowywana w puli) czy lokalny i unikalny dla klasy.
Andriej I,
11
Jak mogę użyć ObjectSizeFetcher, jeśli nie eksportuję jar? Mam testowy projekt Java w środowisku Eclipse.
Yura Shinkarev,
3
@brel Powodem, dla którego Łańcuch ma tylko 32 bajty, niezależnie od faktycznej długości, jest to, że część łańcucha o zmiennej długości jest przechowywana w char [], który jest jego własnym obiektem. Aby uzyskać prawdziwy rozmiar obiektu, musisz dodać sam rozmiar i rozmiar każdego obiektu, do którego się odwołuje.
tombrown52
116

Powinieneś użyć jol , narzędzia opracowanego w ramach projektu OpenJDK.

JOL (Java Object Layout) to niewielki zestaw narzędzi do analizy schematów układu obiektów w JVM. Narzędzia te intensywnie używają programów Unsafe, JVMTI i Serviceability Agent (SA) do dekodowania faktycznego układu obiektu, powierzchni i odniesień. To sprawia, że ​​JOL jest znacznie dokładniejszy niż inne narzędzia oparte na zrzutach sterty, założeniach specyfikacji itp.

Aby uzyskać rozmiary prymitywów, referencji i elementów tablicy, użyj VMSupport.vmDetails(). W przypadku Oracle JDK 1.8.0_40 działającego w 64-bitowym systemie Windows (używanym we wszystkich poniższych przykładach) ta metoda zwraca wartość

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]

Możesz uzyskać płytki rozmiar instancji obiektu za pomocą ClassLayout.parseClass(Foo.class).toPrintable()(opcjonalnie przekazując instancję do toPrintable). Jest to tylko przestrzeń zajmowana przez pojedyncze wystąpienie tej klasy; nie zawiera żadnych innych obiektów, do których odwołuje się ta klasa. To nie obejmują VM narzut na nagłówku obiektu, wyrównanie pola i wyściółkę. Dla 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

Możesz uzyskać widok podsumowania głębokiego rozmiaru instancji obiektu za pomocą GraphLayout.parseInstance(obj).toFootprint(). Oczywiście niektóre obiekty w śladzie mogą być współdzielone (do których odwołują się również inne obiekty), więc jest to nadmierne przybliżenie przestrzeni, którą można odzyskać, gdy obiekt ten zostanie wyrzucony. Dla wyniku Pattern.compile("^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$")(wziętego z tej odpowiedzi ) jol zgłasza całkowity ślad 1840 bajtów, z czego tylko 72 to sama instancja Wzorca.

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)

Jeśli zamiast tego użyjesz GraphLayout.parseInstance(obj).toPrintable(), jol poda adres, rozmiar, typ, wartość i ścieżkę dereferencji pola do każdego obiektu, do którego istnieje odwołanie, choć zwykle jest to zbyt wiele szczegółów, aby było przydatne. W trwającym przykładzie wzoru możesz uzyskać następujące informacje. (Adresy prawdopodobnie będą się zmieniać między seriami).

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)

Pozycje „(coś innego)” opisują inne obiekty na stercie, które nie są częścią tego wykresu obiektów .

Najlepsza dokumentacja jol to próbki jol w repozytorium jol. Przykłady demonstrują typowe operacje jol i pokazują, jak można użyć jol do analizy elementów wewnętrznych maszyny wirtualnej i śmieciarza.

Jeffrey Bosboom
źródło
18
Ta odpowiedź powinna mieć więcej głosów pozytywnych. Zdecydowanie bardzo dobra opcja do sprawdzenia. EDYCJA: Sprawdziłem, czy zostało to dodane w tym roku, gdy pytanie zostało zadane w '08. Prawdopodobnie najlepsza i najłatwiejsza opcja zrobienia tego, o co OP w tej chwili prosił.
wynajmuje
4
Autor narzędzia napisał post na blogu o Jolie .
Mike
2
Aby określić rozmiar obiektu „obj”, użyj: org.openjdk.jol.info.GraphLayout.parseInstance (obj) .totalSize ();
wigor
Zauważ, że vmDetailsteraz VM.current().details().
Miha_x64
Przekonaj się GraphLayout.parseInstance(instance).toFootprint(), że bardziej przydatne jest zrozumienie rozmiarów obiektów
Mugen
81

Przypadkowo znalazłem klasę Java „jdk.nashorn.internal.ir.debug.ObjectSizeCalculator”, już w jdk, która jest łatwa w użyciu i wydaje się dość przydatna do określania wielkości obiektu.

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

wyniki:

164192
48
16
48
416
Tomek
źródło
3
Tak samo tutaj, próbowałem innych rozwiązań zaproponowanych powyżej i natknąłem się na ObjectSizeCalculator. Uważam, że nikt wcześniej o tym nie wspominał, ponieważ został niedawno wprowadzony do JDK 8 w ramach projektu Nashorn . Jednak nie znalazłem żadnej oficjalnej dokumentacji na temat tej klasy w Internecie.
Henrique Gontijo
Wydaje się, że nie bierze pod uwagę długości łańcucha. Czy chodzi tylko o rozmiar na stosie?
jontejj
1
Mam skrót, w którym com.carrotsearch.RamUsageEstimator zwraca około połowy ObjectSizeCalculator. Który jest prawdziwy? - Który jest bardziej niezawodny?
badera
9
Należy pamiętać, że ObjectSizeCalculatorjest obsługiwany tylko w HotSpot VM
kellanburket
74

Kilka lat temu Javaworld miał artykuł na temat określania wielkości złożonych i potencjalnie zagnieżdżonych obiektów Java , w zasadzie przechodzą przez tworzenie implementacji sizeof () w Javie. Podejście to zasadniczo opiera się na innych pracach, w których ludzie eksperymentalnie zidentyfikowali rozmiar prymitywów i typowych obiektów Java, a następnie zastosowali tę wiedzę do metody, która rekursywnie przechodzi wykres obiektu, aby zliczyć całkowity rozmiar.

Zawsze będzie on nieco mniej dokładny niż natywna implementacja C po prostu z powodu rzeczy, które dzieją się za kulisami klasy, ale powinien być dobrym wskaźnikiem.

Alternatywnie projekt SourceForge odpowiednio nazwany sizeof, który oferuje bibliotekę Java5 z implementacją sizeof ().

PS Nie używaj metody serializacji, nie ma korelacji między rozmiarem obiektu serializowanego a ilością pamięci zużywanej podczas życia.

Boris Terzic
źródło
6
Narzędzie sizeof jest prawdopodobnie najszybszym sposobem. To w zasadzie to, co powiedział Stefan, ale już zapakowane w słoik gotowy do użycia.
Alexandre L Telles
62

Po pierwsze, „rozmiar obiektu” nie jest dobrze zdefiniowaną koncepcją w Javie. Mógłbyś oznaczać sam obiekt, tylko jego elementy, Obiekt i wszystkie obiekty, do których się odnosi (wykres odniesienia). Mógłbyś oznaczać rozmiar w pamięci lub rozmiar na dysku. A JVM może optymalizować takie rzeczy jak Strings.

Więc jedynym prawidłowym sposobem jest zapytanie JVM z dobrym profilerem (używam YourKit ), co prawdopodobnie nie jest tym, czego chcesz.

Jednak z powyższego opisu wygląda na to, że każdy wiersz będzie samowystarczalny i nie będzie miał dużego drzewa zależności, więc metoda serializacji będzie prawdopodobnie dobrym przybliżeniem dla większości maszyn JVM. Najłatwiej to zrobić w następujący sposób:

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

Pamiętaj, że jeśli masz obiekty o wspólnych odniesieniach , nie da to poprawnego wyniku, a rozmiar serializacji nie zawsze będzie pasował do wielkości w pamięci, ale jest to dobre przybliżenie. Kod będzie nieco bardziej wydajny, jeśli zainicjujesz rozmiar ByteArrayOutputStream na rozsądną wartość.

Nick Fortescue
źródło
2
Lubię to podejście. Jak daleko byłeś pod względem wielkości obiektu.
Berlin Brown,
1
Bardzo prosty i skuteczny. Inne metody są zbyt nieuporządkowane (szczególnie w Eclipse RCP). Dzięki.
marcolopes,
19
Serializacja nie śledzi zmiennych przejściowych, a domyślna metoda serializacji zapisuje ciągi znaków w UTF-8, więc wszelkie znaki ANSI zajmą tylko jeden bajt. Jeśli masz wiele łańcuchów, twój rozmiar będzie tak daleko, że będzie bezużyteczny.
TMN
1
chociaż może to nie dać dokładnego rozmiaru, dla moich potrzeb potrzebowałem tylko porównania między 2 obiektami a SizeOf nie zainicjuje się z aplikacji internetowej. Dzięki!
Izaak
1
Dobra rekomendacja YourKit . Inne alternatywy to VirtualVM i jvmmonitor
angelcervera
38

Jeśli chcesz tylko wiedzieć, ile pamięci jest używane w JVM i ile jest wolne, możesz spróbować czegoś takiego:

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

edytuj: Pomyślałem, że może to być pomocne, ponieważ autor pytania stwierdził również, że chciałby mieć logikę, która obsługuje „odczytywanie jak największej liczby wierszy, dopóki nie użyję 32 MB pamięci”.

matowy b
źródło
24
Nie jest to dobre rozwiązanie, ponieważ nigdy nie wiadomo, kiedy nastąpi wyrzucanie elementów bezużytecznych, ani ile dodatkowej pamięci zostanie przydzielonych na stos jednocześnie.
Nick Fortescue
5
To prawda i nie zamierzam tego zajmować się głównym pytaniem tego postu, ale może pomóc mu programowo wiedzieć, kiedy zbliża się do osiągnięcia maksymalnego rozmiaru sterty.
matt b
1
Innym problemem tego rozwiązania jest sytuacja w środowisku wielowątkowym (np. Na serwerze WWW). Możliwe, że inne wątki były w trakcie wykonywania i zajmowały pamięć. Przy takim przybliżeniu obliczasz zużytą pamięć na całej maszynie wirtualnej.
angelcervera
1
Kolejną wadą jest to, że freeMemory zwraca przybliżenie. Spróbuj utworzyć obiekt javax.crypto.Cipher. Różnica między dwoma wywołaniami freeMemory (w celu oszacowania rozmiaru szyfru) nie jest stała!
Eugen
1
Wierzę, że można zmusić do zbierania śmieci, więc można zrobić kilka rzeczy w tym podejściu.
Matanster
24

Kiedy pracowałem na Twitterze, napisałem narzędzie do obliczania głębokiego rozmiaru obiektu. Uwzględnia różne modele pamięci (32-bitowe, skompresowane oops, 64-bitowe), dopełnianie, dopełnianie podklasy, działa poprawnie na okrągłych strukturach danych i tablicach. Możesz po prostu skompilować ten jeden plik .java; nie ma żadnych zewnętrznych zależności:

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

Attila Szegedi
źródło
1
Szia! Chciałbym również wykrzyczeć twoją prezentację : slajdy 15-20 świetnie nadają się do instynktownego wyczucia kosztów różnych decyzji dotyczących struktury danych. Dzięki za opublikowanie tego!
Luke Usherwood
16
„nie ma zewnętrznych zależności” - od kiedy guawa nie jest zewnętrzną zależnością?
l4mpi
2
wygląda bardzo podobnie do github.com/JetBrains/jdk8u_nashorn/blob/master/src/jdk/nashorn/… ? : O
Francesco
Guave jest zależnością zewnętrzną.
Mert Serimer,
18

Wiele innych odpowiedzi zapewnia płytki rozmiar - np. Rozmiar HashMap bez żadnego klucza lub wartości, co prawdopodobnie nie jest tym, czego chcesz.

Projekt jamm używa powyższego pakietu java.lang.instrumentation, ale podchodzi do drzewa, dzięki czemu może korzystać z głębokiej pamięci.

new MemoryMeter().measureDeep(myHashMap);

https://github.com/jbellis/jamm

Aby użyć MemoryMeter, uruchom JVM za pomocą „-javaagent: /jamm.jar”

bogaty
źródło
11

Musisz chodzić po obiektach za pomocą odbicia. Bądź ostrożny:

  • Samo przydzielenie obiektu ma pewne obciążenie w JVM. Ilość zależy od JVM, więc możesz ustawić tę wartość jako parametr. Przynajmniej uczyń go stałym (8 bajtów?) I zastosuj do dowolnego przydzielonego elementu.
  • To, że byteteoretycznie jest 1 bajtem, nie oznacza, że ​​zajmuje tylko jeden bajt.
  • W odwołaniach do obiektów będą występować pętle, więc będziesz musiał zachować HashMapciągłe używanie obiektu-równości jako komparatora, aby wyeliminować nieskończone pętle.

@jodonnell: Podoba mi się prostota twojego rozwiązania, ale wiele obiektów nie nadaje się do szeregowania (więc rzucałoby to wyjątek), pola mogą być przejściowe, a obiekty mogą zastąpić standardowe metody.

Jason Cohen
źródło
Czy rozmiary różnych prymitywów nie są zdefiniowane w specyfikacji Java? (§2.4.1)
erickson,
4
Nie w sensie „ile pamięci zajmuje”, to jest pytanie. Tylko w sensie, w jaki sposób działają. Na przykład bajty, znaki i skróty zajmują całe słowo na stosie Java, nawet jeśli działają z zaokrąglaniem itp.
Jason Cohen
1
To brzmi podobnie do mierzenia rozmiaru, jak pokazał Heinz w swoim biuletynie nr 78: javaspecialists.eu/archive/Issue078.html . Użyłem tego. Jego podejście działa.
Peter Kofler
8

Musisz zmierzyć go za pomocą narzędzia lub oszacować ręcznie, i zależy to od używanej maszyny JVM.

Istnieje pewien stały narzut na obiekt. Jest to specyficzne dla JVM, ale zwykle szacuję 40 bajtów. Następnie musisz spojrzeć na członków klasy. Odwołania do obiektów to 4 (8) bajtów w 32-bitowej (64-bitowej) maszynie JVM. Typy pierwotne to:

  • wartość logiczna i bajt: 1 bajt
  • char i short: 2 bajty
  • int i float: 4 bajty
  • długi i podwójny: 8 bajtów

Tablice podlegają tym samym zasadom; to jest odwołanie do obiektu, które zajmuje 4 (lub 8) bajtów w twoim obiekcie, a następnie jego długość pomnożona przez rozmiar jego elementu.

Próba zrobienia tego programowo za pomocą wywołań po Runtime.freeMemory()prostu nie daje dużej dokładności, z powodu asynchronicznych wywołań do modułu wyrzucania elementów bezużytecznych, itp. Profilowanie sterty za pomocą -Xrunhprof lub innych narzędzi da najdokładniejsze wyniki.

erickson
źródło
@erickson Nie byłbym pewien co do sizeof (boolean) == 1 patrząc na ten wątek ( stackoverflow.com/questions/1907318/… ). Czy możesz to skomentować?
dma_k
2
@dma_k, Java faktycznie nie ma prawdziwych boolanów. Rozmiar boolean wynosi 4 bajty poza tablicami i 1 bajt wewnątrz boolean[]. Właściwie wszystkie typy pierwotne inne niż podwójne / długie mają 4 bajty. Te ostatnie to 8 (odpowiedź błędnie podaje je również jako 4)
bestsss
@bestsss: Mówiąc ściślej, minimalny przydział pamięci zależy od platformy i implementacji JVM. Również obiekty na stosie są wyrównane, więc po zsumowaniu wszystkich rozmiarów należy zaokrąglić w górę.
dma_k
6

java.lang.instrument.InstrumentationKlasa zapewnia miły sposób, aby uzyskać rozmiaru obiektu Javy, ale wymaga zdefiniowaniepremain i uruchomić program ze środkiem java. Jest to bardzo nudne, gdy nie potrzebujesz żadnego agenta, a następnie musisz dostarczyć do aplikacji fałszywego agenta Jar.

Więc mam alternatywne rozwiązanie, używając Unsafeklasy z sun.misc. Tak więc, biorąc pod uwagę wyrównanie sterty obiektów zgodnie z architekturą procesora i obliczając maksymalne przesunięcie pola, można zmierzyć rozmiar obiektu Java. W poniższym przykładzie używam klasy pomocniczej, UtilUnsafeaby uzyskać odwołanie do sun.misc.Unsafeobiektu.

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

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

    static {
        Object theUnsafe = null;
        Exception exception = null;
        try {
            Class<?> uc = Class.forName("sun.misc.Unsafe");
            Field f = uc.getDeclaredField("theUnsafe");
            f.setAccessible(true);
            theUnsafe = f.get(uc);
        } catch (Exception e) { exception = e; }
        UNSAFE = (sun.misc.Unsafe) theUnsafe;
        if (UNSAFE == null) throw new Error("Could not obtain access to sun.misc.Unsafe", exception);
    }
    private UtilUnsafe() { }
}
Miguel Gamboa
źródło
Ciekawe podejście, ale czy nie oznacza to, że pamięć obiektu i jego pól nie jest podzielona?
nicoulaj
Tak i nie znam żadnej implementacji JVM, która powoduje taką fragmentację.
Miguel Gamboa
Nie rozumiem. Fragmentacja nie jest opcją :) Weźmy przykład obiektu C, który jest przechowywany jako pole obiektów A i B. Czy to nie przesuwa całej rzeczy w A lub B?
nicoulaj,
Przepraszam, ale nie rozumiem ani waszego punktu widzenia. Według mojej interpretacji, obiekty Java nie mogą być przechowywane w innych obiektach, tak jak dzieje się to w przypadku struktur C lub typów wartości w .Net. Kiedy więc powiesz: „obiekt C, który jest przechowywany jako pole obiektów A i B”, oznacza to, że obiekty A i B mają pola przechowujące odniesienia (wskaźniki) do obiektu C. Wtedy rozmiar A i B jest równy przesunięcie tego pola plus rozmiar referencji (wskaźnika) do obiektu C. A wielkość referencji to rozmiar jednego słowa.
Miguel Gamboa
Och, OK, mówimy o płytkim rozmiarze. Mój błąd.
nicoulaj,
6

Istnieje również narzędzie Memory Measurer (wcześniej Google Code , teraz GitHub ), które jest proste i opublikowane na komercyjnej licencji Apache 2.0 , jak omówiono w podobnym pytaniu .

To także wymaga argumentu wiersza poleceń dla interpretera Java, jeśli chcesz zmierzyć zużycie bajtu pamięci, ale poza tym wydaje się działać dobrze, przynajmniej w scenariuszach, w których go użyłem.

PNS
źródło
4

Bez konieczności manipulowania instrumentacją i tak dalej, a jeśli nie musisz znać dokładnego bajtu rozmiaru obiektu, możesz zastosować następujące podejście:

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

do your job here

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

W ten sposób odczytujesz zużytą pamięć przed i po, a wywołując GC tuż przed uzyskaniem zużytej pamięci, obniżasz „szum” prawie do zera.

Aby uzyskać bardziej wiarygodny wynik, możesz uruchomić zadanie n razy, a następnie podzielić zużytą pamięć przez n, uzyskując ilość pamięci wymaganą przez jedno uruchomienie. Co więcej, możesz uruchomić to wszystko więcej razy i zrobić średnią.

bardzo miłe
źródło
5
Nie System.gc()tylko powiadamia, że ​​chcesz GC? Nie ma gwarancji, że w ogóle zostanie wywołane GC.
Raildex
@bardzo miłe. Nie jest to bezpieczne, ponieważ możesz nigdy nie robić tego, co robi GC lub wpływa na pamięć między liniami. Tak więc „pomiędzy” dwiema metodami freeMemory GC może zwolnić więcej miejsca, którego nie bierzesz pod uwagę, więc twój obiekt będzie wyglądał na mniejszy
Mert Serimer,
@MertSerimer „nie jest bezpieczny” jest dla mnie na zupełnie innym poziomie: co najwyżej nie jest tak dokładne, jak powiedziałem. Nie można także prowadzić GC (jak stwierdził Raildex), ale w tym przypadku również zasugerowałem, aby wstawić to w cykl. To tylko szybki, brudny i przybliżony system, który działa, jeśli wynik nie musi być bardzo niezawodny, jak stwierdzono.
naprawdęnice
Jest z tym wiele problemów, ale daje ci dobrą łup.
markthegrea
3

Oto narzędzie, które utworzyłem za pomocą niektórych połączonych przykładów do obsługi 32-bitowych, 64-bitowych i 64-bitowych ze skompresowanym OOP. To używasun.misc.Unsafe .

Służy Unsafe.addressSize()do uzyskania rozmiaru natywnego wskaźnika iUnsafe.arrayIndexScale( Object[].class ) rozmiaru odniesienia Java.

Wykorzystuje przesunięcie pola znanej klasy, aby obliczyć podstawowy rozmiar obiektu.

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

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

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

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

    private static final Unsafe UNSAFE;

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

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


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

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

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

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


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

            return padSize( size );
        }
    }


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


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

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


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

        return shallowSizeOf( object );
    }
}
dlaudams
źródło
Czy przetestowałeś tę klasę z wartościami? Próbowałem, ale dla mnie nieprawidłowe wartości !!!.
Débora,
1
Wartości, które podał mi dla prostego obiektu, były w przybliżeniu poprawne, ale pomniejszone o współczynnik 10 dla listy zawierającej 1 milion obiektów. Mimo to bardzo dobra robota!
Michael Böckling,
Ciekawy. Przetestowałem to przy użyciu JDK7u67, na Windows 7 x64 i Linux 2.6.16 / x86_64, przy użyciu każdego z trybów adresu 32bit / 64bit / oop. Porównałem to do zrzutów pamięci analizowanych w Eclipse Memory Analyzer 1.3.x. Jakiej konfiguracji używasz? Czy masz konkretny przykład, który mógłbym wypróbować?
dlaudams
Najlepszy wybór, jaki mogę zrobić. Nie mogę używać, Instrumentationponieważ nie uruchamiam tomcat, ObjectSizeCalculatorponieważ nie jestem pewien typu VM (HotSpot) i JOLbacouse sajgonki. Używam tego i dodaję drugi parametr do ignorowania viz singletonów AbstractRefreshableApplicationContext.getBeanFactory().getSingletonMutex()i internalSizeOfkodu refaktora, aby zignorować Class and Enum
Perlos
Aby porównać wyniki, użyj ObjectSizeCalculator (Oblicz cały serwer od 1 GB do 10 s). JOL powoduje MemError (6 GB nie może być wyuczony) i nie otrzymuję takich samych wyników, prawdopodobnie dlatego, że wylicza.
Perlos
3

Szukałem środowiska wykonawczego obliczenia rozmiaru obiektu, który spełniałby następujące wymagania:

  • Dostępne w środowisku wykonawczym bez potrzeby dołączania oprzyrządowania.
  • Działa z Javą 9+ bez dostępu do Niebezpiecznych.
  • Opiera się tylko na klasie. Nie jest to głęboki rozmiar, który uwzględnia długości łańcuchów, długości tablic itp.

Poniższe informacje oparte są na podstawowym kodzie oryginalnego artykułu specjalistów Java ( https://www.javaspecialists.eu/archive/Issue078.html ) oraz kilku bitach z wersji niebezpiecznej w innej odpowiedzi na to pytanie.

Mam nadzieję, że ktoś uzna to za przydatne.

public class JavaSize {

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

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

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

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

        clazz = clazz.getSuperclass();

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

    result += HEADER_SIZE;

    return result;
}

}

David Ryan
źródło
2

Nie ma wywołania metody, jeśli o to prosisz. Przy odrobinie badań przypuszczam, że możesz napisać własną. Określona instancja ma stały rozmiar wynikający z liczby odwołań i pierwotnych wartości oraz danych księgowych instancji. Po prostu chodziłbyś po wykresie obiektowym. Im mniej zróżnicowane typy wierszy, tym łatwiej.

Jeśli jest to zbyt powolne lub po prostu więcej kłopotów niż jest to warte, zawsze istnieją dobre, staromodne zasady liczenia wierszy.

sblundy
źródło
2

Raz napisałem szybki test do oszacowania w locie:

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

}

Ogólna koncepcja polega na przydzielaniu obiektów i mierzeniu zmian w wolnej przestrzeni sterty. Kluczem jest to getFreeMemory(), że żądanie GC działa i czeka na ustabilizowanie się zgłoszonego rozmiaru wolnej sterty . Wynikiem powyższego jest:

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

Tego się spodziewamy, biorąc pod uwagę zachowanie wyrównania i możliwy narzut nagłówka bloku sterty.

Metoda instrumentacji opisana w przyjętej odpowiedzi tutaj jest najdokładniejsza. Metoda, którą opisałem, jest dokładna, ale tylko w kontrolowanych warunkach, w których żadne inne wątki nie tworzą / odrzucają obiektów.

Jason C.
źródło
2

Wystarczy użyć wirtualnej maszyny wirtualnej Java.

Ma wszystko, czego potrzebujesz do profilowania i debugowania problemów z pamięcią.

Ma również konsolę OQL (Object Query Language), która pozwala robić wiele przydatnych rzeczy, z których jedną jest sizeof(o)

ACV
źródło
1

Moja odpowiedź oparta jest na kodzie dostarczonym przez Nicka. Ten kod mierzy całkowitą liczbę bajtów zajmowanych przez obiekt serializowany. Tak więc to faktycznie mierzy dane serializacji + ślad pamięci zwykłego obiektu (na przykład po prostu serializuj, inta zobaczysz, że całkowita ilość serializowanych bajtów nie jest 4). Jeśli więc chcesz uzyskać nieprzetworzony numer bajtu używany dokładnie dla Twojego obiektu - musisz nieco zmodyfikować ten kod. Tak jak:

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

}

Przetestowałem to rozwiązanie z prymitywnymi typami, String i na niektórych trywialnych klasach. Mogą również nie być uwzględnione przypadki.


AKTUALIZACJA: Przykład zmodyfikowany w celu obsługi obliczania powierzchni pamięci obiektów tablicowych.

Agnius Vasiliauskas
źródło
1

Korzystając z JetBrains IntelliJ, najpierw włącz opcję „Dołącz agenta pamięci” w pliku | Ustawienia | Kompilacja, wykonanie, wdrożenie | Debuger

Podczas debugowania kliknij prawym przyciskiem myszy zmienną będącą przedmiotem zainteresowania i wybierz „Oblicz zachowany rozmiar”: Oblicz zachowany rozmiar

simon04
źródło
0

Możesz wygenerować zrzut sterty (na przykład za pomocą jmap), a następnie przeanalizować dane wyjściowe, aby znaleźć rozmiary obiektów. Jest to rozwiązanie offline, ale możesz badać płytki i głęboki rozmiar itp.

JZeeb
źródło
0
long heapSizeBefore = Runtime.getRuntime().totalMemory();

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

rozmiar daje zwiększenie wykorzystania pamięci Jvm z powodu tworzenia obiektu, i zwykle jest to rozmiar obiektu.

użytkownik835199
źródło
co jeśli GC działa w środku podczas // kodu do budowy obiektów? Może teraz cały czas przynosić poprawne wyniki.
rajugaadu
0

Ta odpowiedź nie jest związana z rozmiarem obiektu, ale gdy używasz tablicy do dostosowania obiektów; ile miejsca w pamięci przydzieli dla obiektu.

Tak więc tablice, lista lub mapa wszystkich tych kolekcji nie będą tak naprawdę przechowywać obiektów (tylko w czasie operacji podstawowych potrzebna jest prawdziwa wielkość pamięci obiektu), będą przechowywać tylko odniesienia do tych obiektów.

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

  • (4/8 bajtów) zależy od (32/64 bit) systemu operacyjnego

PRIMITIVES

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

OBIEKTY

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.

Mam na myśli, że cały obiekt REFERENCE potrzebuje tylko 4 bajtów pamięci. Może to być odwołanie do ciągu LUB odwołanie do podwójnego obiektu, ale w zależności od utworzenia obiektu wymagana pamięć będzie się różnić.

Np. Jeśli utworzę obiekt dla niższej klasy, ReferenceMemoryTestwówczas zostaną utworzone 4 + 4 + 4 = 12 bajtów pamięci. Pamięć może się różnić podczas próby zainicjowania odwołań.

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

Kiedy więc tworzymy tablicę obiektów / referencji, cała jej zawartość będzie zajmowana referencjami NULL. I wiemy, że każde odniesienie wymaga 4 bajtów.

I wreszcie, przydział pamięci dla poniższego kodu wynosi 20 bajtów.

ReferenceMemoryTest ref1 = new ReferenceMemoryTest (); (4 (ref1) + 12 = 16 bajtów) ReferenceMemoryTest ref2 = ref1; (4 (ref2) + 16 = 20 bajtów)

Kanagavelu Sugumar
źródło
1
W jaki sposób 4-bajtowa liczba całkowita i odwołanie do obiektu o nieznanym rozmiarze mieszczą się w 4 bajtach?
Markiz Lorne
@EJP Mam na myśli, że cały obiekt REFERENCE potrzebuje tylko 4 bajtów pamięci. Może to być odwołanie do ciągu LUB odwołanie do podwójnego obiektu, ale w zależności od utworzenia obiektu wymagana pamięć będzie się różnić.
Kanagavelu Sugumar
0

Załóżmy, że deklaruję klasę o nazwie Complex:

public class Complex {

    private final long real;
    private final long imaginary;

    // omitted
}

Aby zobaczyć, ile pamięci jest przydzielone do instancji na żywo tej klasy:

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

 num     #instances         #bytes  class name (module)
-------------------------------------------------------
 327:             1             32  Complex
Ali Dehghani
źródło
-4

W przypadku JSONObject poniższy kod może ci pomóc.

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

zwraca rozmiar w bajtach

Sprawdziłem to za pomocą mojego obiektu JSONArray, pisząc go do pliku. Daje rozmiar obiektu.

solokiran
źródło
działałoby to tylko w przypadku obiektów, które w większości są łańcuchami.
Dexter Legaspi,
-6

Wątpię, czy chcesz to zrobić programowo, chyba że chcesz to zrobić tylko raz i zachować do przyszłego użytku. To kosztowna rzecz do zrobienia. W Javie nie ma operatora sizeof (), a nawet gdyby tak było, liczyłby tylko koszt referencji do innych obiektów i rozmiar prymitywów.

Jednym ze sposobów, w jaki możesz to zrobić, jest serializowanie pliku do pliku i sprawdzenie rozmiaru pliku, jak poniżej:

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

Oczywiście zakłada to, że każdy obiekt jest odrębny i nie zawiera przejściowych odniesień do niczego innego.

Inną strategią byłoby wzięcie każdego obiektu i zbadanie jego elementów przez odbicie i zsumowanie rozmiarów (boolean i bajt = 1 bajt, krótki i char = 2 bajty itp.), Idąc w dół hierarchii członkostwa. Ale jest to żmudne i kosztowne i ostatecznie robi to samo, co zrobiłaby strategia serializacji.

jodonnell
źródło
3
Serializowałbym go do bajtu [] za pomocą ByteArrayOutputStream. Byłoby to o wiele szybsze niż zapisanie go do pliku.
ScArcher2
@KorayTugay Określenie wielkości bajtu obiektu jest już kosztowną operacją. Zapisywanie każdego obiektu na dysk w celu ustalenia rozmiaru spowoduje, że zacznie się czołgać ...
HammerNL,
1
Zserializowany format obiektu jest zupełnie inny niż format obiektu w pamięci sterty. Co najważniejsze, deskryptor klasy obiektu (i wszystkich jego nadklas możliwych do serializacji) jest zapisywany w strumieniu. Tak więc napisanie prostej instancji java.lang.Integerdaje około 80 bajtów, przy czym reprezentacja stosu zwykle wynosi 32 (w przeciwieństwie do reprezentacji strumienia obiektów reprezentacja stosu zależy od wielkości wskaźnika i wyrównania obiektu). Z kolei serializowane nullodwołanie wymaga jednego bajtu zamiast czterech lub ośmiu bajtów w pamięci sterty.
Holger,