Wydajność Javy
Szczegółowe
porady dotyczące programowania i strojenia aplikacji w Javie
Istnieją
dwie strategie rozwiązywania problemów wydajnościowych
aplikacji w Javie. Z jednej strony można wykorzystać potężne komputery
i przydzielić JVM ogromne zasoby pamięci, z drugiej - w czasach
ekspansji rozwiązań opartych na chmurach obliczeniowych nowe znaczenie
zyskują małe, jednoprocesorowe komputery. Firmy takie jak Oracle czy
Amazon udostępniają tanie serwery, na których można
uruchamiać proste aplikacje. Łatwo się przekonać, jak ważne jest
właściwe zarządzanie niewielką ilością pamięci w tego rodzaju
środowiskach. Każdy, kto programuje w Javie, powinien dokładnie
wiedzieć, jak maszyna JVM wykonuje kod i jak należy ją dostrajać, aby
osiągała możliwie największą wydajność.
W
tej książce opisano wiele funkcjonalności, narzędzi i procedur, dzięki
którym można poprawić efektywność kodu napisanego w Javie 8
i 11 LTS. Główny nacisk położono na zagadnienia istotne dla
środowisk produkcyjnych, ale przedstawiono również ciekawe
nowe technologie, takie jak kompilacja z wyprzedzeniem i
eksperymentalne kolektory. Znalazło się tu także omówienie
nowości w mechanizmie porządkowania pamięci i rejestratorze Java Flight
Recorder, zaprezentowano kwestie funkcjonowania Javy w środowiskach
kontenerowych, udoskonalone narzędzie JMH, kompilatory JIT,
współdzielone klasy danych, narzędzia do monitorowania
wydajności i wiele innych. Książkę doceni każdy inżynier zajmujący się
JVM, który chce poradzić sobie z nietypowym działaniem
systemu, wyciekami pamięci i problemami z jej porządkowaniem.
Najciekawsze zagadnienia:
- platformy i kompilatory
Javy a wydajność aplikacji
- porządkowanie pamięci
- zasady testowania
wydajności aplikacji
- pakiet JDK i narzędzia do
monitorowania aplikacji
- dostrajanie kolektora i
interfejsów Java API
- wydajność aplikacji
korzystających z baz danych
Wstęp
9
1.
Wprowadzenie
15
Ogólny zarys książki 16
Platformy i konwencje 16
Platformy Javy 16
Platformy sprzętowe 18
Pełny obraz wydajności 21
Pisz lepsze algorytmy 21
Pisz mniej kodu 22
Śmiało, przedwcześnie optymalizuj 22
Rozglądaj się wokoło: baza danych jest zawsze słabym punktem 24
Optymalizuj pod kątem typowego użycia 25
Podsumowanie 25
2.
Testowanie
wydajności 27
Testy rzeczywistej aplikacji 27
Mikrotesty porównawcze 27
Makrotesty porównawcze 31
Mezotesty porównawcze 32
Testy przepustowości, operacji wsadowych i czasu odpowiedzi aplikacji 34
Czas wykonania operacji wsadowej 34
Przepustowość 35
Czas odpowiedzi 36
Analiza zmienności wyników 38
Zasada wczesnego i częstego testowania 42
Przykłady testów porównawczych 45
Java Microbenchmark Harness 45
Przykładowe kody 52
Podsumowanie 55
3.
Narzędzia wydajnościowe 57
Monitorowanie i analiza wydajności systemu operacyjnego 57
Wykorzystanie procesora 58
Kolejka procesora 61
Wykorzystanie dysku 62
Wykorzystanie sieci 64
Narzędzia do monitorowania Javy 65
Podstawowe informacje o maszynie wirtualnej 66
Informacje o wątkach 69
Informacje o klasach 69
Bieżąca analiza mechanizmu porządkowania pamięci 69
Przetwarzanie zrzutu sterty 70
Profilowanie maszyny JVM 70
Profilery próbujące 70
Profilery instrumentalizujące 74
Metody blokujące i szeregi czasowe wątków 75
Natywne profilery 77
Java Flight Recorder 77
Java Mission Control 79
Ogólne informacje o JFR 79
Włączenie funkcjonalności JFR 86
Wybieranie zdarzeń 89
Podsumowanie 91
4.
Kompilator
JIT 93
Ogólne informacje o kompilatorze 93
Kompilacja HotSpot 95
Kompilacja etapowa 96
Popularne flagi kompilatora 97
Strojenie pamięci podręcznej kodu 97
Monitorowanie procesu kompilacji 99
Poziomy kompilacji etapowej 102
Deoptymalizacja 103
Zaawansowane flagi kompilatora 106
Wartości progowe 106
Wątki kompilatora 107
Rozwijanie metod 109
Analiza ucieczki 110
Kod procesora 111
Kompromisy kompilacji etapowej 112
Maszyna GraalVM 114
Prekompilacja 115
Kompilacja z wyprzedzeniem 115
Natywna kompilacja w maszynie GraalVM 118
Podsumowanie 119
5.
Wprowadzenie do porządkowania pamięci 121
Ogólne informacje o porządkowaniu pamięci 121
Porządkowanie generacji obiektów 123
Algorytmy porządkowania pamięci 125
Wybór kolektora 127
Podstawy strojenia kolektora 135
Wielkość sterty 135
Dobór wielkości generacji 138
Dobór wielkości metaprzestrzeni 140
Sterowanie wielowątkowością 142
Narzędzia do monitorowania porządkowania pamięci 143
Włączanie dzienników kolektorów w pakiecie JDK 8
143
Włączanie dzienników kolektorów w pakiecie JDK 11
144
Podsumowanie 147
6.
Algorytmy
porządkowania pamięci 149
Kolektor równoległy 149
Adaptywne i statyczne skalowanie sterty 152
Kolektor G1 156
Strojenie kolektora G1 165
Kolektor CMS 168
Strojenie kolektora w celu uniknięcia awarii trybu
współbieżnego porządkowania pamięci 173
Zaawansowane strojenie kolektorów 176
Okres dojrzewania obiektów i obszar obiektów
ocalonych 176
Alokowanie dużych obiektów 180
Flaga AggressiveHeap 186
Pełna kontrola wielkości sterty 187
Kolektory eksperymentalne 189
Współbieżne scalanie sterty: kolektory ZGC i Shenandoah 189
Bez porządkowania: kolektor Epsilon 191
Podsumowanie 192
7.
Dobre
praktyki używania sterty 195
Analiza sterty 195
Histogram sterty 196
Zrzut sterty 197
Problem przepełnienia pamięci 201
Zmniejszenie zajętości pamięci 205
Zmniejszanie wielkości obiektów 206
Leniwe inicjowanie obiektu 208
Obiekty niemutowalne i kanoniczne 212
Zarządzanie cyklem życia obiektów 214
Obiekty wielokrotnego użytku 214
Odwołania miękkie, słabe i inne 219
Skompresowane wskaźniki 232
Podsumowanie 233
8.
Dobre praktyki używania natywnej pamięci
235
Obciążenie pamięci 235
Monitorowanie obciążenia pamięci 236
Minimalizacja obciążenia pamięci 237
Monitorowanie ilości pamięci natywnej 238
Natywna pamięć i współdzielone biblioteki 241
Strojenie maszyny JVM pod kątem systemu operacyjnego 244
Duże strony pamięci 245
Podsumowanie 249
9.
Synchronizacja
wątków i wydajność aplikacji
251
Wątki i sprzęt 251
Pule wątków i klasa ThreadPoolExecutors 252
Dobór maksymalnej liczby wątków 252
Dobór minimalnej liczby wątków 256
Wielkość kolejki zadań 258
Klasa ThreadPoolExecutor 258
Klasa ForkJoinPool 260
Wykradanie pracy 265
Automatyczne zrównoleglanie operacji 266
Synchronizacja wątków 268
Cena synchronizacji 269
Zapobieganie synchronizacji 272
Fałszywe współdzielenie danych 274
Strojenie wątków maszyny JVM 278
Strojenie wielkości stosów wątków 278
Preferencje blokowania 279
Priorytety wątków 280
Monitorowanie wątków i blokad 280
Monitorowanie wątków 280
Monitorowanie zablokowanych wątków 280
Podsumowanie 284
10.
Serwery
Java 285
Przegląd operacji NIO w Javie 285
Kontenery serwerowe 287
Strojenie puli wątków serwera 287
Asynchroniczne serwery REST 289
Asynchroniczne zapytania wychodzące 291
Asynchroniczne zapytania HTTP 291
Przetwarzanie danych JSON 298
Przetwarzanie danych 298
Obiekty JSON 299
Parsowanie danych JSON 301
Podsumowanie 302
11.
Wydajność
baz danych - dobre praktyki
303
Przykładowa baza danych 303
Interfejs JDBC 304
Sterowniki JDBC 304
Pule połączeń JDBC 306
Preparowane zapytania i pule zapytań 307
Transakcje 309
Przetwarzanie zestawu wyników 316
Platforma JPA 318
Optymalizacja zapisu danych w platformie JPA 318
Optymalizacja odczytu danych w platformie JPA 319
Bufor platformy JPA 323
Spring Data 329
Podsumowanie 330
12.
Java SE API - porady 331
Ciągi znaków 331
Kompaktowe ciągi znaków 331
Duplikowanie i internowanie ciągów 332
Łączenie ciągów znaków 338
Buforowanie operacji wejścia/wyjścia 341
Ładowanie klas 343
Współdzielenie danych klas 343
Liczby losowe 346
Interfejs JNI 349
Wyjątki 351
Dzienniki 354
Kolekcje 356
Kolekcje synchroniczne i asynchroniczne 356
Wielkość kolekcji 357
Kolekcje i wykorzystanie pamięci 358
Funkcje lambda i klasy anonimowe 360
Wydajność strumieni i filtrów 362
Leniwe przetwarzanie danych 362
Serializacja obiektów 364
Pola przejściowe 364
Przesłanianie domyślnej serializacji 365
Kompresja danych 367
Śledzenie duplikatów obiektów 369
Podsumowanie 371
Dodatek. Flagi maszyny JVM 373
384
strony, oprawa miękka