Czy łączenie obiektów jest przestarzałą techniką?


62

Bardzo dobrze znam koncepcję łączenia obiektów i zawsze staram się z niej korzystać w jak największym stopniu.

Dodatkowo zawsze myślałem, że pula obiektów jest standardową normą, ponieważ zauważyłem, że sama Java, a także inne frameworki używają pula w jak największym stopniu.

Ostatnio jednak przeczytałem coś, co było dla mnie zupełnie nowe (i sprzeczne z intuicją?)

Ta pula faktycznie pogarsza wydajność programu, szczególnie w aplikacjach współbieżnych, i newzamiast tego zaleca się tworzenie instancji obiektów, ponieważ w nowszych maszynach JVM tworzenie instancji obiektu jest naprawdę szybkie.

Przeczytałem to w książce: Java Concurrency in Practice

Teraz zaczynam zastanawiać się, czy coś tu nie rozumiem, ponieważ pierwsza część książki zalecała użycie Executorstego ponownego użycia Threadzamiast tworzenia nowych instancji.

Czy pule obiektów są obecnie przestarzałe?

Odpowiedzi:


72

Jest to przestarzała technika ogólna, ponieważ - jak zauważyłeś - tworzenie i niszczenie krótko żyjących obiektów per se (tj. Alokacja pamięci i GC) jest niezwykle tanie we współczesnych maszynach JVM. Tak więc użycie odręcznej puli obiektów dla obiektów typu mill-of-the-mill jest najprawdopodobniej wolniejsze, bardziej skomplikowane i bardziej podatne na błędy niż zwykłe new. *

Nadal jednak ma zastosowanie w przypadku obiektów specjalnych, których tworzenie jest stosunkowo kosztowne, takich jak połączenia DB / sieciowe, wątki itp.

* Kiedyś musiałem poprawić wydajność przeszukiwalnej aplikacji Java. Dochodzenie ujawniło próbę użycia puli obiektów do przydzielenia milionów obiektów ... a sprytny facet, który ją napisał, użył jednej globalnej blokady, aby zapewnić bezpieczeństwo wątków. Zastąpienie puli zwykłym newsprawiło, że aplikacja była 30 razy szybsza.


1
Jak więc zdecydować, czy utworzenie obiektu jest zbyt drogie?
user10326,

3
Jeśli obiekt zużywa zasoby systemu operacyjnego (wątki, operacje we / wy, pamięć współdzieloną itp.)
Kevin Cline

13
@ user10326, według pomiaru :-) Jeśli tworzenie obiektów zajmuje dużo czasu i / lub są one powiązane z jakimś specjalnym, potencjalnie ograniczonym zasobem innym niż pamięć, możesz rozważyć połączenie.
Péter Török

8
@ user10326, IMO w ponad 95% przypadków, powyższe kryteria ułatwiają wcześniejszą decyzję, czy potrzebujesz puli obiektów. (Co więcej, w prawie wszystkich przypadkach wymagających puli, najprawdopodobniej użyjesz istniejącej biblioteki / frameworka, która prawdopodobnie ma już dla Ciebie zaimplementowaną pulę obiektów.) W pozostałych przypadkach nadal łatwo jest ukryć tworzenie obiektów np. fabrykę, którą później można ponownie wdrożyć w dowolny sposób.
Péter Török

2
Bardzo ważna uwaga autorstwa @Peter Torok: wiele frameworków i bibliotek implementuje dla ciebie pule, ZAWSZE upewnij się, że nie korzystasz już z puli bibliotek przed wdrożeniem własnej.
hromanko

36

Odpowiedź na konkretne pytanie: „Czy łączenie obiektów jest przestarzałą techniką?” jest:

Nie. Pule obiektów są szeroko stosowane w określonych miejscach - pule wątków, pule połączeń z bazami danych itp.

Ogólne tworzenie obiektów nigdy nie było procesem powolnym. Samo gromadzenie zasobów pochłania zasoby - pamięć i moc obliczeniową. Każda optymalizacja jest kompromisem.

Zasada jest następująca:

Przedwczesna optymalizacja jest zła !!!

Ale kiedy dana optymalizacja jest przedwczesna?

Przedwczesna optymalizacja to jakakolwiek optymalizacja przeprowadzona, zanim odkryjesz wąskie gardło poprzez dokładne profilowanie .


2
W rzeczy samej. OP powiedział: „Zawsze staram się go używać tak często, jak to możliwe” - to jest problem, IMO.
nerdytenor,

@ Boris, więc zgodnie z drugim zdaniem nie powinniśmy sprzeciwić się połączeniom i wątkom db puli, dopóki nie odkryjemy ich jako wąskiego gardła poprzez profilowanie?
Pacerier

1
@Pac Niektóre wyniki profilowania nie wymagają ciągłego ponownego pomiaru :-)
David Bullock

9

W sytuacjach, w których chcesz całkowicie uniknąć wyrzucania elementów bezużytecznych, myślę, że pule obiektów są jedyną realną alternatywą. Więc nie, to absolutnie nie jest przestarzała technika.


1
I dodałbym, że dobrym pomysłem jest unikanie GC, gdy obiekty są wystarczająco długie, aby przenieść się do starszej generacji.
Zan Lynx,

8

Pomiar

Zależy to całkowicie od przypadku użycia, wielkości obiektów, JVM, opcji JVM, włączonej GC i całego szeregu innych czynników.

W skrócie: zmierz go wcześniej, a następnie zmierz. Zakładając, że używasz frameworku obiektów (jak z Apache), przełączanie między implementacjami nie powinno być zbyt bolesne.

Dodatkowa wskazówka dotycząca testowania wydajności - pozwól JVM trochę się rozgrzać, uruchom testy na uruchomionej maszynie JVM kilka razy, może zachowywać się inaczej.


3
„pozwól JVM najpierw się rozgrzać” - pamiętam, kiedy jedyną rzeczą, którą trzeba było „rozgrzać”, był monitor. Oy, wszystko nowe jest znowu stare.
kylben

Jedyne, co muszę rozgrzać, to kawa!
Rozczarowany

@Marijn, w jaki sposób „rozgrzewa się”?
Pacerier

Zobacz JMH Framework, aby uzyskać pełne wyjaśnienie ( openjdk.java.net/projects/code-tools/jmh ), ale w zasadzie musisz dać JVM szansę na JIT swojego kodu, uruchomić GC przed testem porównawczym i tak dalej.
Martijn Verburg

8

Ta pula faktycznie pogarsza wydajność programu, szczególnie w aplikacjach współbieżnych, i wskazane jest tworzenie instancji nowych obiektów, ponieważ w nowszych maszynach JVM tworzenie instancji obiektu jest naprawdę szybkie.

Zależy od kontekstu.


1
Doskonała odpowiedź i przekonujący. Dodałbym (być może jako gwiazdkę?), Że twierdzenie „24 bajty” odnosi się do 4 instancji 4-bajtowych liczb zmiennoprzecinkowych (16 bajtów), plus 4 bajty dla odwołania do obiektu oraz 4 bajty dla odniesienia do blokady. To jest dokładnie taki koszt jaki Twój projekt eliminuje.
strickli

5

Nie wiem, czy istnieje zmieniając trend tutaj, ale jego na pewno będzie tak, że to zależy . Jeśli klasa Java zarządza zasobem zewnętrznym, takim jak połączenie RMI lub ładowanie pliku zasobów itp. - z pewnością koszty tworzenia instancji obiektu mogą być nadal wysokie (chociaż zasoby te mogą być już dla Ciebie połączone!). Jako ogólną praktykę zgodziłbym się z książką.


No cóż, teraz nie wiem, ponieważ nawet w tym przypadku opisujesz, które (przed przeczytaniem tego) zdecydowanie użyłbym puli, miałbym również narzut. 1) Nowe konstrukcje do obsługi pulowania 2) Synchronizacja obiektu pobierania / zwalniania z puli 3) utrzymywanie puli itp. Więc myślę teraz, że być może nie ma przypadku użycia, który byłby użyteczny, z wyjątkiem np. buforowania gniazda zamiast otwierania nowego za każdym razem, aby połączyć się z serwerem. narzut związany z tworzeniem opóźnień, a nie tworzenia instancji
10326,

@ user10326 Tak dokładnie. Widzę otwieranie gniazda jako część narzutu tworzenia instancji, jeśli jest to zadanie klasy i musi zostać zainicjowane w konstruktorze, to dotyczy to opóźnień i wpływów we / wy.
Jeremy
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.