HotSpot JVM, çöp toplama döngüsü nesneyi hareket ettirmeden önce, değeri bir kez hesaplayarak—genellikle başlangıç bellek adresinden—ve bunu nesne başlığındaki işaret kelimesinde önbelleğe alarak Object.hashCode() tutarlılığını garanti eder. Bu işaret kelimesi, hash'in malzeme haline geldiğini belirten bir bit bayrağının yanı sıra ayrılmış bir hash kodu alanını içerir ve böylece sonraki çağrılar önbellekten değeri alır ve yeniden hesaplamaz. Sonuç olarak, G1 veya ZGC gibi toplama yöntemleri nesneyi yeni bir adrese taşırken bile, kimlik hash'i fiziksel işaretçiden bağımsız olduğu ve sabit başlık meta verilerine kaydedildiği için kararlıdır.
Dağıtık bir web uygulaması, yük dengeleme operasyonları sırasında önbellek affinitesi yönlendirmesi için System.identityHashCode()'ya güvenerek birden fazla uygulama düğümü arasında aktif Session nesnelerini takip etmek için IdentityHashMap kullandı. Yoğun trafik sırasında, ZGC düşük gecikme toplama yöntemi, sık sık genç nesil nesneleri taşımakta ve sıkı duraklama sürelerini hedeflenen şekilde korumaktaydı. Kimlik hash'i taşıma sırasında değişseydi, oturum affinitesi bozulacak ve istekler düğümler arasında dağılacaktı, bu da tutarlılık garantilerini ihlal edecekti.
Bir yaklaşım, her Session oluşturulurken UUID örnekleri üretmek ve ayrı bir ConcurrentHashMap<UUID, Session> tutmaktı. Artılar: JVM nesne yaşam döngüsünden ve taşınma mekaniklerinden tamamen bağımsız. Eksiler: Her oturum nesnesi için on altı bayt ek yük ve UUID üretiminden kaynaklanan tahsis baskısı ekler, bu da ani trafik sırasında tahsis oranını doyurabilir.
Ekip, GC taşınmasını önlemek için oturum nesnelerini bellekte JNI kritik referansları kullanarak sabitlemeyi düşündü. Artılar: Sabit bellek adreslerini garanti eder ve dolayısıyla adreslerden türetilen sabit kimlik hash'lerini sağlar. Eksiler: ZGC'de tüm yığın bölgelerini sabitler ve parçalanmaya neden olur, bu da toplama yönteminin eşzamanlı taşınma yeteneklerini bozar ve kabul edilemez duraklama sürelerine yol açar.
Seçilen çözüm, kimlik hash kodlarının sabit kalacağını garanti eden JVM spesifikasyonu ile birlikte HotSpot'un işaret kelimesi önbelleğe alma uygulamasını kullandı. Artılar: Ekstra bellek yükü yok, tahsis maliyeti yok ve ZGC gibi agresif toplayıcılarla tam uyumluluk. Eksiler: JVM uygulama detaylarına güvenilirlik gerektirir, ancak bu spesifikasyonda kodlanmıştır.
Uygulama, IdentityHashMap bütünlüğünü bozmadan, milyonlarca ZGC döngüsü boyunca mükemmel oturum affinitesini sürdürdü ve sabitleme veya yardımcı tanımlayıcılar olmadan alt milisaniye duraklama süreleri sağladı.
System.identityHashCode() her zaman nesnenin mevcut bellek adresini bir tamsayı olarak mı döndürür?
Hayır. Başlangıç hesaplaması bellek adresini entropi olarak kullanabilir, ancak sonuç hemen nesne başlığına kaydedilir ve daha sonra asla değişmez. Bu, döndürülen tamsayının GC hareketinden sonraki nesnenin mevcut konumunu yansıtmadığı anlamına gelir ve geliştiriciler bunu bir işaretçi veya bellek adresi sondası olarak ele almamalıdır.
Kimlik hash kodu negatif olabilir mi ve koleksiyonlar bunu nasıl yönetir?
Evet, her tür otuz iki bit tamsayı değeri geçerlidir, negatif sayılar da dahil. IdentityHashMap negatif hash'leri, Math.abs()'in iki'nin iki tamamlayanı taşması nedeniyle Integer.MIN_VALUE üzerinde başarısız olduğu maskalama işlemleriyle (h ^ (h >>> 16)) & (length-1) gibi işlemler ile yönetir.
Kimlik hash kodunun tüm nesneler arasında benzersiz olması garanti midir?
Hayır. Otuz iki bit tamsayı alanı, potansiyel yığın adres alanından daha küçüktür, bu nedenle çakışmalar mümkündür. HotSpot, değerleri iyi dağıtan Marsaglia'nın xor-shift yöntemini veya adres tabanlı hashing kullanır, ancak benzersizlik garanti edilmez. Bu da IdentityHashMap'in belirsizliği kaldırmak için yalnızca hash kodlarına değil, referans eşitliğine güvenmesini gerektirir.