JavaprogramowanieStarszy programista Java

Jak **HotSpot JVM** zapewnia, że **Object.hashCode()** zwraca spójne wartości po tym, jak śmieciarz przenosi obiekt w inne miejsce w pamięci, mimo że początkowy skrót to wynik oryginalnej lokalizacji pamięci obiektu?

Zdaj rozmowy kwalifikacyjne z asystentem AI Hintsage

Odpowiedź na pytanie.

HotSpot JVM gwarantuje spójność Object.hashCode() podczas przenoszenia obiektów poprzez obliczenie wartości raz — zazwyczaj z początkowego adresu pamięci — i zapisanie jej w polu nagłówka obiektu przed jakimkolwiek cyklem zbierania śmieci, który mógłby przenieść obiekt. To pole nagłówka zawiera dedykowane pole dla skrótu oraz znacznik bitowy wskazujący, że skrót został zmaterializowany, co zapewnia, że kolejne wywołania zwracają zbuforowaną wartość, zamiast ją przeliczać. W związku z tym, nawet gdy śmieciarze tacy jak G1 czy ZGC przenoszą obiekt pod nowy adres, skrót identyfikacyjny pozostaje stabilny, ponieważ jest odłączony od wskaźnika fizycznego i przechowywany w niemutowalnych danych nagłówka.

Sytuacja z życia wzięta

Rozproszona aplikacja internetowa korzystała z IdentityHashMap do śledzenia aktywnych obiektów Session na wielu węzłach aplikacji, polegając na System.identityHashCode() do ustalania routingu affinowego cache podczas operacji równoważenia obciążenia. Podczas dużego ruchu, niskolatencyjny zbieracz ZGC przeprowadzał częste równoczesne przeniesienia obiektów młodej generacji, aby utrzymać wąskie cele czasowe pauzy. Gdyby skrót identyfikacyjny się zmienił po przeniesieniu, to affinowość sesji zostałaby zerwana, co doprowadziłoby do rozlewania się żądań między węzłami i naruszenia gwarancji spójności.

Jednym z rozważanych podejść było generowanie instancji UUID dla każdej Sesji w momencie jej tworzenia i utrzymywanie osobnego ConcurrentHashMap<UUID, Session>. Zalety: Całkowita niezależność od cyklu życia obiektów JVM i mechaniki przenoszenia. Wady: Dodaje szesnaście bajtów obciążenia na obiekt sesji i wprowadza presję alokacyjną z generowania UUID, co potencjalnie saturuje wskaźnik alokacji podczas szczytowego ruchu.

Zespół rozważał również przypinanie obiektów sesji w pamięci za pomocą krytycznych referencji JNI, aby zapobiec przenoszeniu przez GC. Zalety: Gwarantuje stabilne adresy pamięci, a więc stabilne skróty identyfikacyjne uzyskiwane z adresów. Wady: Przypina całe regiony pamięci w ZGC, powodując fragmentację i niwecząc możliwości równoczesnego przenoszenia śmieciarza, co prowadzi do nieakceptowalnych czasów pauzy.

Wybrane rozwiązanie wykorzystało gwarancję specyfikacji JVM, że kody skrótów identyfikacyjnych pozostają stałe, połączoną z wdrożeniem buforowania mark word HotSpot. Zalety: Zerowe dodatkowe obciążenie pamięci, brak kosztów alokacji i pełna kompatybilność z agresywnymi zbieraczami takimi jak ZGC. Wady: Wymaga zaufania do szczegółów implementacji JVM, chociaż są one zapisane w specyfikacji.

Aplikacja utrzymywała doskonałą affinowość sesji przez miliony cykli ZGC bez przypinania lub dodatkowych identyfikatorów, osiągając czasy pauzy poniżej milisekundy przy zachowaniu integralności IdentityHashMap.

Co często umyka kandydatom

Czy System.identityHashCode() zawsze zwraca aktualny adres obiektu w pamięci jako liczbę całkowitą?

Nie. Chociaż początkowe obliczenie może używać adresu pamięci jako entropii, wynik jest natychmiast zapisywany w nagłówku obiektu i nigdy później się nie zmienia. Oznacza to, że zwrócona liczba całkowita nie odzwierciedla aktualnej lokalizacji obiektu po przeniesieniu przez GC, a deweloperzy nie powinni traktować jej jako wskaźnik lub badanie adresu w pamięci.

Czy kod skrótu identyfikacyjnego może być ujemny, i jak sobie z tym radzą kolekcje?

Tak, każda wartość całkowita o długości trzydziestu dwóch bitów jest ważna, w tym liczby ujemne. IdentityHashMap radzi sobie z ujemnymi skrótami poprzez operacje maskujące, takie jak (h ^ (h >>> 16)) & (length-1), unikając Math.abs(), które nie działa na Integer.MIN_VALUE z powodu przepełnienia uzupełnienia do dwóch.

Czy kod skrótu identyfikacyjnego jest gwarantowany jako unikalny dla wszystkich obiektów?

Nie. Przestrzeń liczbowych trzydziestu dwóch bitów jest mniejsza niż potencjalna przestrzeń adresów sterty, więc kolizje są możliwe. HotSpot wykorzystuje schemat Marsaglii xor-shift lub haszowanie oparte na adresach, które dobrze rozkłada wartości, ale unikalność nie jest gwarantowana, przez co IdentityHashMap opiera się na równościach referencyjnych, a nie tylko na kodach skrótów.