Kolega powiedział mi, że tworzenie obiektów Java jest najdroższą operacją, jaką można wykonać. Mogę więc tylko stwierdzić, że mogę utworzyć jak najmniej obiektów.
Wydaje się to nieco pokonywać cel programowania obiektowego. Jeśli nie tworzymy obiektów, piszemy tylko jeden długi styl klasy C w celu optymalizacji?
Odpowiedzi:
Twój kolega nie ma pojęcia o czym mówi.
Twoja najdroższa operacja będzie ich słuchać . Zmarnowali czas na przekręcanie cię na informacje, które są ponad dziesięć lat nieaktualne (od pierwotnej daty opublikowano tę odpowiedź), a także na poświęcanie czasu na pisanie tutaj i poszukiwanie prawdy w Internecie.
Miejmy nadzieję, że po prostu ignorują coś, co słyszeli lub czytali ponad dziesięć lat temu i nie wiedzą nic lepszego. Przyjęłbym również wszystko, co powiedzą, jako podejrzane, powinien to być dobrze znany błąd każdego, kto i tak jest na bieżąco.
Wszystko jest przedmiotem (oprócz
primitives
)Wszystko inne niż prymitywy (
int, long, double
itp.) To Obiekty w Javie. Nie ma możliwości uniknięcia tworzenia obiektów w Javie.Tworzenie obiektów w Javie ze względu na strategie alokacji pamięci jest w większości przypadków szybsze niż C ++ i ze względów praktycznych w porównaniu do wszystkiego innego w JVM można uznać za „wolne” .
Na początku lat dziewięćdziesiątych na początku lat 2000. implementacje JVM miały pewne narzuty wydajności w rzeczywistej alokacji obiektów. Nie było tak od co najmniej 2005 roku.
Jeśli dostroisz się,
-Xms
aby obsługiwać całą pamięć potrzebną do poprawnego działania aplikacji, GC może nigdy nie być zmuszona do uruchomienia i zamiatania większości śmieci w nowoczesnych implementacjach GC, programy krótkotrwałe mogą nigdy nie być GC.Nie próbuje zmaksymalizować wolnego miejsca, które i tak jest czerwonym śledziem, maksymalizuje wydajność środowiska wykonawczego. Jeśli to oznacza, że JVM Heap jest przydzielany prawie w 100% przez cały czas, niech tak będzie. Wolna pamięć sterty JVM i tak niczego nie daje.
Istnieje błędne przekonanie, że GC zwalnia pamięć z powrotem do reszty systemu w użyteczny sposób, jest to całkowicie nieprawda!
Sterta JVM nie rośnie i nie kurczy się, więc na resztę systemu pozytywnie wpływa wolna pamięć w stercie JVM .
-Xms
alokuje WSZYSTKO, co jest określone podczas uruchamiania, a jego heurystyka polega na tym, że tak naprawdę nigdy nie zwalnia żadnej pamięci z powrotem do systemu operacyjnego, aby mogła być współdzielona z innymi procesami systemu operacyjnego, dopóki ta instancja JVM nie zakończy się całkowicie.-Xms=1GB -Xmx=1GB
przydziela 1 GB pamięci RAM, niezależnie od liczby obiektów faktycznie utworzonych w danym momencie. Istnieją pewne ustawienia, które pozwalają na zwolnienie procentu pamięci sterty, ale dla wszystkich praktycznych celów JVM nigdy nie jest w stanie zwolnić wystarczającej ilości tej pamięci, aby to się kiedykolwiek wydarzyłowięc żadne inne procesy nie mogą odzyskać tej pamięci, więc reszta systemu nie korzysta z uwolnienia JVM Heap. Zapytanie RFE zostało „przyjęte” 29 listopada 2006 r., Ale nic z tym nie zrobiono. Takie zachowanie nie jest uważane za problem przez nikogo z autorytetów.Istnieje błędne przekonanie, że tworzenie wielu małych, krótkotrwałych obiektów powoduje, że JVM zatrzymuje się na długi okres czasu, jest to również nieprawda
Obecne algorytmy GC są w rzeczywistości zoptymalizowane do tworzenia wielu wielu małych obiektów, które są krótkotrwałe, to jest w zasadzie 99% heurystyka dla obiektów Java w każdym programie. Próby łączenia obiektów powodują, że JVM działa gorzej w większości przypadków.
Jedynymi obiektami, które wymagają dziś pulowania, są obiekty odnoszące się do skończonych zasobów, które są zewnętrzne w stosunku do JVM; Gniazda, pliki, połączenia z bazą danych itp. Mogą być ponownie użyte. Zwykłe przedmioty nie mogą być łączone w taki sam sposób, jak w językach, które pozwalają na bezpośredni dostęp do miejsc pamięci. Buforowanie obiektów jest inną koncepcją i może być, lub nie, to, co niektórzy naiwnie nazywają pulowaniem , te dwie koncepcje nie są tym samym i nie należy ich łączyć.
Współczesne algorytmy GC nie mają tego problemu, ponieważ nie zwalniają zgodnie z harmonogramem, zwalniają, gdy w danym pokoleniu potrzebna jest wolna pamięć. Jeśli sterta jest wystarczająco duża, wówczas nie następuje dezalokacja na tyle długo, aby spowodować przerwy.
Języki dynamiczne zorientowane obiektowo pokonują C nawet teraz w testach wrażliwych na obliczenia.
źródło
_alloca
zamortyzowana.new
słowa kluczowego w hotspocie. Widziałem ludzi korzystanew ImageIcon(Image)
wpaint()
sposobie obiektów huśtawka, która jest dość drogie i robił bardzo słaby cały UI. Więc to nie jest czarno-biała odpowiedź, zastanów się, zanimnew
gdzieś skorzystasz .Konkluzja: Nie narażaj projektu na działanie, aby tworzyć skróty przy tworzeniu obiektów. Unikaj niepotrzebnego tworzenia obiektów. Jeśli jest to rozsądne, należy zaprojektować, aby uniknąć zbędnych operacji (jakiegokolwiek rodzaju).
W przeciwieństwie do większości odpowiedzi - tak, przydział obiektów NIE wiąże się z kosztami. Jest to niski koszt, ale należy unikać tworzenia niepotrzebnych przedmiotów. Tak samo, jak powinieneś unikać niepotrzebnych elementów w kodzie. Duże wykresy obiektowe spowalniają GC, implikują dłuższe czasy wykonywania, ponieważ prawdopodobnie będziesz mieć więcej wywołań metod, wyzwala więcej braków pamięci podręcznej procesora i zwiększa prawdopodobieństwo zamiany twojego procesu na dysk w przypadkach małej pamięci RAM.
Zanim ktokolwiek narzeka na to, że jest to przypadek skrajny - mam profilowane aplikacje, które przed optymalizacją utworzyły 20 + MB obiektów w celu przetworzenia ~ 50 wierszy danych. Testowanie jest w porządku, dopóki nie skalujesz do stu żądań na minutę i nie tworzysz nagle 2 GB danych na minutę. Jeśli chcesz zrobić 20 reqs / s, tworzysz 400 MB obiektów, a następnie wyrzucasz. 20 reqs / s to niewielka ilość jak na porządny serwer.
źródło
while(something) { byte[] buffer = new byte[10240]; ... readIntoBuffer(buffer); ...
co może być marnotrawnym w porównaniu do npbyte[] buffer = new byte[10240]; while(something) { ... readIntoBuffer(buffer); ...
.W rzeczywistości, ze względu na strategie zarządzania pamięcią, które umożliwia język Java (lub inny język zarządzany), tworzenie obiektów to niewiele więcej niż zwiększanie wskaźnika w bloku pamięci zwanym młodym pokoleniem. Jest znacznie szybszy niż C, gdzie trzeba wykonać wyszukiwanie wolnej pamięci.
Inną częścią kosztu jest zniszczenie obiektu, ale trudno go porównać do C. Koszt kolekcji opiera się na ilości obiektów zapisanych długoterminowo, ale częstotliwość kolekcji zależy od ilości obiektów utworzonych ... w koniec jest wciąż znacznie szybszy niż zarządzanie pamięcią w stylu C.
źródło
Point
obiektu może się zmieścić w 2 rejestrów ogólnego przeznaczenia).Inne plakaty słusznie zauważyły, że tworzenie obiektów jest niezwykle szybkie w Javie i że zwykle nie powinieneś się tym przejmować w żadnej normalnej aplikacji Java.
Istnieje kilka bardzo szczególnych sytuacjach, gdy jest to dobry pomysł, aby uniknąć tworzenia obiektów.
źródło
W tym, co mówi twój kolega, jest jądro prawdy. Szacunkiem sugerować problem z obiektowego tworzenia jest rzeczywiście śmieci kolekcja . W C ++ programista może dokładnie kontrolować sposób zwalniania pamięci. Program może gromadzić rudę tak długo lub tak krótko, jak chce. Co więcej, program C ++ może odrzucić crud, używając innego wątku niż ten, który go stworzył. W ten sposób działający obecnie wątek nigdy nie musi kończyć się w celu oczyszczenia.
Natomiast wirtualna maszyna Java (JVM) okresowo zatrzymuje kod w celu odzyskania nieużywanej pamięci. Większość programistów Java nigdy nie zauważa tej przerwy, ponieważ zwykle jest ona rzadka i bardzo krótka. Im więcej zgrubienia gromadzisz lub im bardziej ograniczona jest JVM, tym częstsze są te przerwy. Do wizualizacji tego procesu można użyć narzędzi takich jak VisualVM .
W najnowszych wersjach Javy algorytm odśmiecania (GC) można dostosować . Zasadą jest, że im krótsze są przerwy, tym droższy jest koszt maszyny wirtualnej (tj. Procesor i pamięć spędzone na koordynowaniu procesu GC).
Kiedy to może mieć znaczenie? Za każdym razem, gdy zależy Ci na spójnym wskaźniku odpowiedzi poniżej milisekund, zależy Ci na GC. Zautomatyzowane systemy transakcyjne napisane w Javie dostrajają JVM, aby zminimalizować przerwy. Firmy, które w innym przypadku napisałyby Javę, zwracają się do C ++ w sytuacjach, gdy systemy muszą być bardzo responsywne przez cały czas.
Dla przypomnienia, ogólnie nie toleruję unikania obiektów! Domyślnie programowanie obiektowe. Dostosuj to podejście tylko wtedy, gdy GC stanie ci na przeszkodzie, i dopiero po próbie dostrojenia JVM, aby zatrzymać na krótszy czas. Dobrą książką na temat dostrajania wydajności Java jest Java Performance autorstwa Charliego Hunta i Binu Johna.
źródło
Jest jeden przypadek, w którym możesz zniechęcić się do tworzenia zbyt wielu obiektów w Javie z powodu narzutu - projektowania pod kątem wydajności na platformie Android
Poza tym powyższe odpowiedzi są prawdziwe.
źródło
GC jest dostrojony do wielu krótkotrwałych obiektów
powiedział, że jeśli możesz w trywialny sposób ograniczyć przydział obiektów, powinieneś
jednym z przykładów byłoby budowanie łańcucha w pętli, naiwnym sposobem byłoby
który tworzy nowy
String
obiekt dla każdej+=
operacji (plus aStringBuilder
i nową podstawową tablicę char)możesz łatwo przepisać to na:
ten wzorzec (niezmienny wynik i lokalna zmienna wartość pośrednia) można również zastosować do innych rzeczy
ale poza tym powinieneś wyciągnąć profiler, aby znaleźć prawdziwe wąskie gardło zamiast ścigać duchy
źródło
StringBuilder
metody jest pre-sizeStringBuilder
tak, że nigdy nie ma do realokacji bazowego tablicę zStringBuilder(int)
konstruktora. To sprawia, że jest to pojedynczy przydział, a nie1+N
przydział.Joshua Bloch (jeden z twórców platform Java) napisał w swojej książce Effective Java w 2001 roku:
źródło
To zależy od konkretnej aplikacji, więc ogólnie trudno jest powiedzieć. Byłbym jednak bardzo zaskoczony, gdyby tworzenie obiektów było faktycznie wąskim gardłem wydajności w aplikacji. Nawet jeśli są powolne, korzyści w stylu kodu prawdopodobnie przewyższą wydajność (chyba że jest to zauważalne dla użytkownika)
W każdym razie nie powinieneś nawet martwić się o te rzeczy, dopóki nie wyprofilujesz swojego kodu, aby określić rzeczywiste wąskie gardła wydajności zamiast zgadywania. Do tego czasu powinieneś robić wszystko, co najlepsze dla czytelności kodu, a nie wydajności.
źródło
Myślę, że twój kolega musiał to powiedzieć z perspektywy tworzenia niepotrzebnych obiektów. Mam na myśli, że jeśli często tworzysz ten sam obiekt, lepiej udostępnić ten obiekt. Nawet w przypadkach, w których tworzenie obiektów jest skomplikowane i zajmuje więcej pamięci, możesz sklonować ten obiekt i uniknąć tworzenia procesu tworzenia złożonych obiektów (ale to zależy od twoich wymagań). Myślę, że stwierdzenie „Tworzenie obiektów jest drogie” powinno być brane pod uwagę w kontekście.
Jeśli chodzi o wymagania dotyczące pamięci JVM, poczekaj na Javę 8, nie musisz nawet określać -Xmx, ustawienia przestrzeni meta zajmą się potrzebą pamięci JVM i będzie rosła automatycznie.
źródło
Tworzenie klasy to coś więcej niż przydzielanie pamięci. Istnieje również inicjalizacja, nie jestem pewien, dlaczego ta część nie jest objęta wszystkimi odpowiedziami. Normalne klasy zawierają pewne zmienne i wykonują jakąś formę inicjalizacji, a to nie jest darmowe. W zależności od klasy klasa może odczytywać pliki lub wykonywać dowolną liczbę powolnych operacji.
Zastanów się więc, co robi konstruktor klasy, zanim zorientujesz się, czy jest darmowy, czy nie.
źródło
Java GC jest w rzeczywistości bardzo zoptymalizowana pod względem szybkiego tworzenia dużej liczby obiektów w trybie „seryjnym”. Z tego, co rozumiem, używają sekwencyjnego alokatora (najszybszego i najprostszego alokatora O (1) dla żądań o zmiennej wielkości) dla tego rodzaju „cyklu impulsów” w przestrzeni pamięci, którą nazywają „przestrzenią Edenu” i tylko wtedy, gdy obiekty pozostają po cyklu GC zostają przeniesieni do miejsca, w którym GC może je zbierać jeden po drugim.
To powiedziawszy, jeśli twoje potrzeby wydajnościowe stają się wystarczająco krytyczne (jak mierzone w rzeczywistych wymaganiach użytkowników), wtedy obiekty niosą narzut, ale nie pomyślałbym o tym tak dużo w zakresie tworzenia / alokacji. Ma to więcej wspólnego z lokalizacją odniesienia i dodatkowym rozmiarem wszystkich obiektów w Javie, niezbędnym do obsługi pojęć takich jak odbicie i dynamiczne wysyłanie (
Float
jest większy niżfloat
, często około 4 razy większy w 64-bitowych wymaganiach wyrównania, oraz tablicaFloat
nie musi koniecznie być przechowywana w sposób ciągły z tego, co rozumiem).Jedną z najbardziej przyciągających uwagę rzeczy, które kiedykolwiek widziałem, opracowaną w Javie, która sprawiła, że uznałem, że jest to zawodnik o dużej wadze w mojej dziedzinie (VFX), to interaktywny, wielowątkowy standardowy moduł śledzący ścieżki (nie korzystający z buforowania napromieniania, BDPT, MLS ani niczego innego) na procesor zapewnia podgląd w czasie rzeczywistym, który dość szybko zbiegał się w obraz pozbawiony szumów. Pracowałem z profesjonalistami w C ++ poświęcając swoją karierę takim rzeczom z fantazyjnymi profilerami w ręku, którzy mieli z tym trudności.
Ale rozejrzałem się po kodzie źródłowym i chociaż używał on wielu obiektów w trywialny sposób, najbardziej krytyczne części znacznika ścieżki (BVH oraz trójkąty i materiały) bardzo wyraźnie i celowo unikały obiektów na rzecz dużych tablic typów pierwotnych ( głównie
float[]
iint[]
), co spowodowało, że zużywał on znacznie mniej pamięci i gwarantował lokalizację przestrzenną, aby przejść z jednejfloat
tablicy do drugiej. Nie sądzę, aby myślenie w ten sposób było zbyt spekulacyjne, jeśli autor użył polubionych typów pudełkowychFloat
tam jego wydajność byłaby dość duża. Ale mówimy o najbardziej absolutnie krytycznej części tego silnika i jestem całkiem pewien, biorąc pod uwagę, jak umiejętnie deweloper zoptymalizował go, że zmierzył to i zastosował tę optymalizację bardzo, bardzo rozsądnie, ponieważ z radością używał obiektów wszędzie indziej trywialny koszt jego imponującego znacznika ścieżki w czasie rzeczywistym.Nawet w dziedzinie tak krytycznej pod względem wydajności, jak moja, nie będziesz pisać skutecznych produktów, jeśli ściskasz się w miejscach, które nie mają znaczenia. Twierdziłbym nawet, że najbardziej krytyczne dla wydajności pola mogą spowodować jeszcze większe zapotrzebowanie na produktywność, ponieważ potrzebujemy dodatkowego czasu, aby dostroić te hotspoty, które są naprawdę ważne, nie marnując czasu na rzeczy, które nie . Podobnie jak w przypadku przykładu śledzenia ścieżki z góry, autor umiejętnie i rozważnie zastosował takie optymalizacje tylko w miejscach, które naprawdę, naprawdę miały znaczenie, i prawdopodobnie z perspektywy czasu po pomiarze i nadal z radością korzystały z obiektów wszędzie.
źródło
float[]
wersetachFloat[]
, gdzie przetwarza milion byłego kolejno może być stosunkowo dość szybciej niż sekundę.Jak powiedzieli ludzie, tworzenie obiektów nie jest dużym kosztem w Javie (ale stawiam na większe niż większość prostych operacji, takich jak dodawanie itp.) I nie powinieneś tego zbytnio unikać.
To powiedziało, że wciąż jest to koszt, a czasami możesz próbować zeskrobać jak najwięcej obiektów. Ale dopiero po tym, jak profilowanie wykazało, że jest to problem.
Oto świetna prezentacja na ten temat: https://www.cs.virginia.edu/kim/publicity/pldi09tutorials/memory-efficient-java-tutorial.pdf
źródło
Zrobiłem szybki mikrobenchmark na ten temat i podałem pełne źródła w github. Doszedłem do wniosku, że to, czy tworzenie obiektów jest drogie, czy nie, nie jest problemem, ale ciągłe tworzenie obiektów z myślą, że GC zajmie się Twoimi sprawami, sprawi, że Twoja aplikacja szybciej uruchomi proces GC. GC jest bardzo kosztownym procesem i najlepiej jest go unikać, gdy tylko jest to możliwe, i nie próbuj naciskać go, aby rozpocząć.
źródło