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ć.
Odpowiedzi:
Możesz użyć pakietu java.lang.instrument
Skompiluj i umieść tę klasę w pliku JAR:
Dodaj następujące elementy do
MANIFEST.MF
:Użyj getObjectSize:
Wywołaj z:
źródło
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.Powinieneś użyć jol , narzędzia opracowanego w ramach projektu OpenJDK.
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śćMożesz uzyskać płytki rozmiar instancji obiektu za pomocą
ClassLayout.parseClass(Foo.class).toPrintable()
(opcjonalnie przekazując instancję dotoPrintable
). 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ę. Dlajava.util.regex.Pattern
: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 wynikuPattern.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.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).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.
źródło
vmDetails
terazVM.current().details()
.GraphLayout.parseInstance(instance).toFootprint()
, że bardziej przydatne jest zrozumienie rozmiarów obiektówPrzypadkowo 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.
wyniki:
źródło
ObjectSizeCalculator
jest obsługiwany tylko w HotSpot VMKilka 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.
źródło
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:
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ść.
źródło
Jeśli chcesz tylko wiedzieć, ile pamięci jest używane w JVM i ile jest wolne, możesz spróbować czegoś takiego:
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”.
źródło
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
źródło
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.
https://github.com/jbellis/jamm
źródło
Musisz chodzić po obiektach za pomocą odbicia. Bądź ostrożny:
byte
teoretycznie jest 1 bajtem, nie oznacza, że zajmuje tylko jeden bajt.HashMap
cią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.
źródło
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:
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.źródło
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)java.lang.instrument.Instrumentation
Klasa 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
Unsafe
klasy zsun.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,UtilUnsafe
aby uzyskać odwołanie dosun.misc.Unsafe
obiektu.źródło
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.
źródło
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:
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ą.
źródło
System.gc()
tylko powiadamia, że chcesz GC? Nie ma gwarancji, że w ogóle zostanie wywołane GC.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żywa
sun.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.
źródło
Instrumentation
ponieważ nie uruchamiam tomcat,ObjectSizeCalculator
ponieważ nie jestem pewien typu VM (HotSpot) iJOL
bacouse sajgonki. Używam tego i dodaję drugi parametr do ignorowania viz singletonówAbstractRefreshableApplicationContext.getBeanFactory().getSingletonMutex()
iinternalSizeOf
kodu refaktora, aby zignorować Class and EnumSzukałem środowiska wykonawczego obliczenia rozmiaru obiektu, który spełniałby następujące wymagania:
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.
}
źródło
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.
źródło
Raz napisałem szybki test do oszacowania w locie:
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: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.
źródło
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)
źródło
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,
int
a zobaczysz, że całkowita ilość serializowanych bajtów nie jest4
). Jeśli więc chcesz uzyskać nieprzetworzony numer bajtu używany dokładnie dla Twojego obiektu - musisz nieco zmodyfikować ten kod. Tak jak: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.
źródło
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”:
źródło
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.
źródło
rozmiar daje zwiększenie wykorzystania pamięci Jvm z powodu tworzenia obiektu, i zwykle jest to rozmiar obiektu.
źródło
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
PRIMITIVES
OBIEKTY
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,
ReferenceMemoryTest
wówczas zostaną utworzone 4 + 4 + 4 = 12 bajtów pamięci. Pamięć może się różnić podczas próby zainicjowania odwołań.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)
źródło
Załóżmy, że deklaruję klasę o nazwie
Complex
:Aby zobaczyć, ile pamięci jest przydzielone do instancji na żywo tej klasy:
źródło
W przypadku JSONObject poniższy kod może ci pomóc.
zwraca rozmiar w bajtach
Sprawdziłem to za pomocą mojego obiektu JSONArray, pisząc go do pliku. Daje rozmiar obiektu.
źródło
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:
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.
źródło
java.lang.Integer
daje 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 serializowanenull
odwołanie wymaga jednego bajtu zamiast czterech lub ośmiu bajtów w pamięci sterty.