Sayısız Java GC makalesi okudunuz, bu 5 soruyu bilmiyor olabilirsiniz

Yazar | Xuanyuan Rüzgar

Editör | Tu Min

Kaynak | Programlama Teknolojisi Evreni

Bu makale GC algoritmasını ve çöp toplayıcıyı tekrar etmeyecek, ancak GC oluştuğunda gözden kaçabilecek birkaç sorundan bahsedecektir. Bu sorunları anlamak için, GC anlayışımızı derinleştirebileceğimize inanıyorum.

Bu makalenin ana içeriği:

  • S1: GC çalışması nasıl başlatıldı?

  • S2: Dünyayı Durdur tüm Java iş parçacıklarını nasıl durdurur?

  • S3: GC Kökleri nasıl bulunur?

  • S4: GC sırasında dört özel referansla nasıl başa çıkılır?

  • S5: Nesne taşındıktan sonra referans nasıl düzeltilir?

S1: GC çalışması nasıl başlatıldı?

Çöp toplama, farklı bölümler için MinorGC ve FullGC'ye bölünmüştür ve farklı bölümler için tetikleme koşulları farklıdır. Genel olarak, GC tetikleyicileri iki türe ayrılır: aktif ve pasif:

  • Aktif: Program, GC'yi başlatmak için System.gc'yi çağırmayı görüntüler (hemen veya hatta değil)

  • Pasif: bellek tahsisi başarısız oldu ve alanın temizlenmesi gerekiyor

Her iki durumda da, GC aynı şekilde başlatılır:

  • Adım 1: Bir VM_Operation işlemini başlatmak için GC'ye ihtiyaç duyan iş parçacığı (bu bir temel sınıftır, farklı çöp toplayıcıları, CMS toplayıcısının VM_GenCollectFullConcurrent başlatması gibi ilgili alt sınıf işlemlerini başlatır)

  • Adım 2: İşlem bir kuyruğa teslim edilir ve JVM'de bu işlem isteklerini kuyrukta özel olarak işleyen bir VMThread iş parçacığı vardır.Bu iş parçacığı, her bir işlemi işlemek için VM_Operation'ın değerlendirme işlevini çağırır.

  • Adım 3: VM_Operation'ın değerlendirme fonksiyonu kendi doit sanal fonksiyonunu çağırır

  • Adım4: Her bir çöp toplayıcıdan türetilen VM_Operation alt sınıfları, C ++ polimorfizminin tipik bir kullanımı olan ilgili çöp toplama işleme çalışmalarını gerçekleştirmek için doit yöntemini geçersiz kılar.

S2: Dünyayı Durdur tüm Java iş parçacıklarını nasıl durdurur?

Herkesin STW'yi duyduğuna inanıyorum. Çöp toplama işlemi yaparken, çalışan tüm Java iş parçacıklarını durdurmak gerekiyor. Bunun nedeni yukarıdaki makaleden bir cümle ödünç almaktır:

Çöp toplama sırasında diğer çalışan iş parçacıkları neden askıya alınır? Başka bir grup insan çöpü atarken çöp topladığınızı hayal edin Çöpler temizlenebilir mi?

Peki bu Java iş parçacıkları nasıl durur?

Her şeyden önce, askıya alma işlemini askıya almak için kesinlikle çöp toplama iş parçacığı değildir, nedenini düşünün?

Durdurma, iş parçacığının herhangi bir yerde durmasına izin vermez, çünkü sonraki GC, yığın alanındaki nesnelerin "taşınmasına" neden olur. Durdurma uygun değilse, bu nesnelerin iş parçacığı uyandıktan sonra çalışması beklenmedik olacaktır. hata.

Uygun durak nerede? Bu, başka bir önemli konsepte yol açar: güvenli bir nokta, güvenli noktaya giren iplik, referans ilişkisinin değişmeyeceği anlamına gelir.

Güvenlik noktası senkronizasyonunun yürütülmesi, yukarıda açıklanan VMThread tarafından başlatılır Güvenlik noktası senkronizasyonu, VM_Operation işlenmeden önce gerçekleştirilir ve güvenlik noktası senkronizasyonu, işlem tamamlandıktan sonra iptal edilir.

void VMThread :: loop { while (true) { ... _cur_vm_operation = _vm_queue- > remove_next; ... // Güvenlik noktası senkronizasyonu başlar SafepointSynchronize :: begin; // Mevcut VM_Operation'ı işleyin eval_operation (_cur_vm_operation); ... // Güvenlik noktası senkronizasyonunun sonu SafepointSynchronize :: begin; ... } ... }

Yukarıdaki VMThread'in iş parçacığında, tüm VMOpration işlemlerinin güvenli noktanın senkronizasyon çalışmasını gerçekleştirmeyeceği, VMOpration durumuna göre işleneceği unutulmamalıdır.Açıklık ve basitlik için, yukarıdaki kodda bu mantıklar ihmal edilmiştir.

Bir Java iş parçacığı farklı durumlarda olabilir HotSpot'ta iş parçacığının durumuna bağlı olarak, güvenli noktaya girme yolu farklıdır. HotSpot kaynak kodunda bunu özellikle açıklayan büyük bir yorum var:

1. Yürütme bayt kodu durumunu yorumlayın

JVM sanal makinesinin yürütme sürecinin basit bir şekilde anlaşılması, bayt kodunu sürekli olarak alan ve ardından bayt koduna karşılık gelen kodu çalıştıran süper büyük bir anahtar durumudur (bu yalnızca basitleştirilmiş bir modeldir). Daha sonra, bayt kodunu ve ilgili kod bloğu bilgilerini kaydetmek için JVM'de bir tablo olmalıdır.Bu tablo DispatchTable olarak adlandırılır ve şuna benzer:

Aslında, JVM içinde, biri normal durumda, diğeri güvenli noktada olmak üzere iki tür tablo vardır.

Güvenlik noktasını girecek kodda, görevlerden biri, yukarıda geçerli olan bayt kodu gönderim tablosunu değiştirmektir:

onarmak:

Değiştirilen bayt kodu gönderim tablosundaki kod DispatchTable, burada genişletilmeyecek olan güvenlik noktası kontrol kodunu ekleyecektir.

2. Yerel kod durumunu yürütün

JNI çağrıları yapan evreler için, SafepointSynchronize :: begin'da özel işlemlere gerek yoktur. Yerel kodu çalıştıran Java iş parçacığı, JNI arayüzünden dönerken kendisini askıya alması gerekip gerekmediğini aktif olarak kontrol edecektir.

3. Derlemenin yürütülmesinden sonra kod durumu

Çoğu modern JVM, bir tür tam zamanında derleme teknolojisi JIT kullanır. Yürütme sürecini hızlandırmak için, sıcak yürütme kodu genellikle yerel makine talimatlarında, ayrıntı düzeyi olarak yöntem işleviyle derlenir.

Basitçe söylemek gerekirse, belirli bir işlevin tekrar tekrar çalıştırıldığı veya işlevdeki belirli bir kod bloğunun çok sayıda döngüye sahip olduğu ve artık ara bayt kodu yorumlama ve çalıştırma yoluyla değil, doğrudan yerel koda derlenmesine karar verildi.

Bu durumda, artık ara bayt kodu aracılığıyla yürütülmez ve tabii ki bayt kodu gönderim tablosu alınmayacaktır.Bu nedenle, ilk durumda bayt kodu gönderim tablosunu değiştirmenin yolu, iş parçacığının bu kodu yürütmesidir. Artık çalışmıyor. o zaman ne yapmalıyız?

HotSpot'ta, iş parçacığının güvenli bir noktaya girmesine izin vermek için etkin kesinti adı verilen bir yöntem benimsenmiştir. Özellikle, JVM'de bir bellek sayfası vardır. İş parçacığı çalışırken bu sayfaya zaman zaman bir göz atacaktır (okuyacaktır). Bu normaldir. Bu şartlar altında her şey normal. GC yürütülmeden önce, JVM'deki hizmetçi VMthread, bu bellek sayfasının erişim özniteliğini önceden okunamaz hale getirecektir. Şu anda, diğer çalışan iş parçacıkları bu sayfayı tekrar okuyacak ve bu da bir bellek erişim istisnasını tetikleyecek ve önceden kurulan JVM istisnayı yakalayacaktır. Şu anda, işlemci her iş parçacığının yürütme akışını devralabilir, bazı GC öncesi hazırlıkları yapabilir ve ardından iş parçacığını askıya almak için engelleyebilir.

// Tüm konuları bir kayıt noktasına ileri sar // ve hepsini askıya al void SafepointSynchronize :: begin { ... os :: make_polling_page_unreadable; ... }

Os :: make_polling_page_unreadable çağrılması, yoklama sayfasını okunamaz hale getirir.Bu işlev, farklı işletim sistemi platformlarına göre farklı uygulamalara sahiptir. Yaygın Linux ve Windows'u örnek olarak alın.

Linux:

void os :: make_polling_page_unreadable (void) { eğer (! guard_memory ((char *) _ polling_page, Linux :: page_size)) { ölümcül ("Oylama sayfası devre dışı bırakılamadı"); } } bool os :: guard_memory (char * adr, size_t boyut) { dönüş linux_mprotect (adres, boyut, PROT_NONE); } statik bool linux_mprotect (char * addr, size_t size, int prot) { char * bottom = (char *) align_down ((intptr_t) adres, os :: Linux :: page_size); assert (addr == alt, "akıl sağlığı kontrolü"); size = align_up (işaretçi_delta (adres, alt, 1) + boyut, os :: Linux :: sayfa_boyutu); return :: mprotect (alt, boyut, koruma) == 0; }

Son olarak, sistem düzeyinde API'yi çağırın: mprotect, bellek sayfasının öznitelik ayarını tamamlar.Linux C / C ++ programlamasına aşina olan arkadaşlar yabancı olmamalıdır.

Pencereler:

void os :: make_polling_page_unreadable (void) { DWORD old_status; eğer (! VirtualProtect ((char *) _ polling_page, os :: vm_page_size, PAGE_NOACCESS, old_status)) { ölümcül ("Oylama sayfası devre dışı bırakılamadı"); } }

Son olarak sistem düzeyinde API'yi çağırın: VirtualProtect bellek sayfasının özellik ayarını tamamlar, Windows C / C ++ programlamasına aşina olan arkadaşlar yabancı olmamalıdır.

Bu özel sayfa nerede? Çalışma zamanı / os sınıfında bulunan statik bir üye değişkeni.

4. Engellenmiş durum

IO, kilit senkronizasyonu vb. Nedeniyle bloke edilen konular, GC tamamlanana kadar bloke kalacak ve uyanmayacaktır.

5. Sanal makinede veya durum değiştirmede

Bir Java iş parçacığı, Java bayt kodunu yorumlamak ve yürütmek için zamanın çoğunu harcar ve bazı senaryolarda, JVM'nin kendisi bunu yürütme hakkına sahiptir. İş parçacığı bu özel anlarda olduğunda, JVM, iş parçacığının durumunu değiştirirken güvenli noktanın durumunu da aktif olarak kontrol edecektir.

S3: GC Kökleri nasıl bulunur?

GC Roots kimlerdir?

GC sırasında, ulaşılabilirlik analizi algoritması genellikle değerli nesneleri bulmak, kopyalamak ve saklamak için kullanılır ve izlenebilirlik zincirinde olmayan kalan nesneler temizlenir ve ortadan kaldırılır. Erişilebilirlik analizi algoritmasının başlangıç noktası, GC Kökleri adı verilen bir grup şeydir, öyleyse GC Kökleri nedir? Neredeler?

  • Sanal makine yığınında referans verilen nesneler (yığın çerçevesindeki yerel değişken tablosu)

  • Yöntem alanında sınıf statik özellikleri tarafından başvurulan nesneler

  • Yöntem alanında sabitler tarafından başvurulan nesneler

  • Yerel yöntem yığınında JNI (genel olarak Yerel yöntemler) tarafından başvurulan nesneler

Artık kim olduklarını ve nerede olduklarını biliyorum. Ama bunları GC sırasında nasıl bulabilirim? Örnek olarak ilk yığında atıfta bulunulan nesneyi ele alalım: JVM'de, her dönüşte düzinelerce iş parçacığı çalışıyor ve her iş parçacığına yerleştirilmiş işlev yığını çerçeveleri düzinelerce katmandan düzinelerce yüzlerce katmana kadar değişiyor. Konulardaki tüm referanslar bulundu ve bunun zaman alıcı ve emek yoğun bir proje olacağını tahmin edebiliyorum. Ve bilmelisiniz ki, GC yürütüldüğünde, Dünyayı Durdurun Zaman değerlidir. Çöp toplamanın neden olduğu işlem yanıtının kesintisini azaltmak için GC'yi mümkün olan en kısa sürede tamamlamak gerekir.Sonra, nesne referans zincirini izlememiz, nesneyi kopyalamamız, vb. Bu nedenle, GC Köklerinin geçiş yapması için fazla zaman kalmaz.

HotSpot da dahil olmak üzere modern Java sanal makineleri, zaman için alan kullanma stratejisini benimsemiştir. Temel fikir basittir: GC Köklerinin konum bilgilerini önceden kaydedin.GC sırasında, haritaya göre bunları arayın ve hızlıca bulun.

Öyleyse soru şu ki, bu konum bilgisi nerede var? Ne tür bir veri yapısıdır? İş parçacığı sürekli çalışıyor ve referans ilişkisi sürekli değişiyor Bu bilgiler nasıl güncellenir?

OopMap'in lideri

Bu soruları cevaplamadan önce şimdilik GC Roots'u unutalım ve başka bir soru düşünelim:

JVM iş parçacığı Java yığınını taradığında, 64 bitlik bir 0x0007ff3080345600 numarası bulur.JVM, bunun Java yığınındaki bir nesneye yönelik bir adres (yani bir başvuru) olup olmadığını veya yalnızca uzun bir değişken mi olduğunu nasıl anlar?

Hepimizin bildiği gibi, Java dilinde C / C ++ ile karşılaştırıldığında en büyük değişikliklerden biri can sıkıcı işaretçilerden, özgür programcılardan kurtulmak ve artık belleği yönetmek için işaretleyicilere ihtiyaç duymamaktır. Ancak aslında kurtulmak yüzeysel bir kurtulmaktır.Ne de olsa JVM C ++ ile yazılmıştır.Java'nın pek fazla işaretçisi yoktur.Bir dereceye kadar Java'nın her yerinde işaretçiler vardır. Ancak Java'da başka bir ifadeyi değiştirdik: referans.

Eklenmesi gereken, bazı eski JVM uygulamalarında referansın kendisinin, nesne adres tablosundaki bir indeks değeri olan bir tutamaç değeri olmasıdır. Modern JVM referansları artık bu yöntemi kullanmaz, ancak doğrudan işaretçiler kullanır. Bu soru ile ilgili olarak S6'da: Nesne taşındıktan sonra referans nasıl düzeltilir? Daha fazla detaylandıracak.

Şimdi soruya geri dönersek, JVM neden 64 bitlik bir verinin referans mı yoksa uzun değişken mi olduğunu bilmesi gerekiyor? Cevap, hafızayı nasıl geri kazanacağını bilmiyorsa?

Bu, başka bir terim grubuna yol açar: muhafazakar GC ve doğru GC.

  • Muhafazakar GC: Sanal makine yukarıda belirtilen sorunları net bir şekilde ayırt edemez ve yığındaki hangilerinin referans olduğunu bilemez. Muhafazakar bir tutum benimser. Bir veri bir nesne işaretçisi gibi görünüyorsa (örneğin, bu sayı yığın alanına işaret ediyor, bu konum sadece sahip Bir nesne başlığı), ardından bu durumda bir referans olarak ele alın. Böylelikle referans olmayabilecek şeyler de referans olarak kabul edilir.Daha gerçekçi olmak gerekirse tembel bir politikadır.Bu durumda kaygan bir balığın çöp toplanmaması olasıdır (nedenini düşünün?)

  • Doğru GC: Konservatif GC ile karşılaştırıldığında, bu 64 bitlik bir sayının uzun mu yoksa nesne referansı mı olduğunu açıkça bilmektir. Modern ticari JVM'ler bu daha gelişmiş yöntemi benimser.Bu JVM, yığındaki her adres biriminde neyin yüklü olduğunu ve nesnenin yapısını açıkça bilebilir ve onu yanlışlıkla öldürmez veya gözden kaçırmaz.

Peki, doğru GC nasıl bu kadar net bir şekilde biliyor? Cevap, JVM'nin veri bilgilerini belleğe kaydetmesidir, HotSpot'ta bu verilere OopMap adı verilir.

Önceki bölümdeki son soruyu cevaplamak için, GC Roots'un konum bilgisi de OopMap'te.

OopMap neye benziyor?

OopMap verileri nasıl oluşturulur?

HotSpot kaynak kodunda OopMap ile ilgili verileri oluşturma kodu çeşitli yerlere dağılmıştır.Kaynak dizinde yeni OopMap anahtar sözcüğünü arayarak bunları bulabilirsiniz.Ön okuma yoluyla, işlevin geri dönüşünü, anormal atlamayı ve döngü atlamasını görebilirsiniz. Transferler gibi yerlerde bulunurlar ve bu anlarda JVM, sonraki GC kullanımı için OopMap ile ilgili bilgileri kaydedecektir.

S4: GC sırasında dört özel referansla nasıl başa çıkılır?

GC ile ilgili herhangi bir makale bize şunları söyleyecektir: GC Köklerinden referans alınmayan nesneleri bulmak için erişilebilirlik algoritmalarını kullanın. Ancak buradaki referans o kadar basit değil.

Genellikle, bahsettiğimiz Java referansları, bazı referanslara ek olarak güçlü referanslardır:

  • Güçlü referans: varsayılan olarak doğrudan yeni nesneye işaret eder

  • Yumuşak referans: SoftReference

  • Zayıf referans: WeakReference

  • Hayalet referans: Hayalet referans olarak da adlandırılan PhantomReference

Aşağıda, yukarıda bahsedilen referanslara kısa bir giriştir, varsayılan güçlü referanslardan bahsedilmemiştir:

Yumuşak referans

Esnek referanslar, bazı yararlı ancak gerekli olmayan nesneleri açıklamak için kullanılır. Yazılım referanslarıyla ilişkili nesneler için, bu nesneler, sistem bir bellek taşması istisnasına sahip olmadan önce ikinci bir kurtarma için kurtarma aralığında listelenir. Bu koleksiyon için yeterli bellek yoksa, bir bellek taşması istisnası atılır. "Java Sanal Makinesi Derinlemesine Anlama" bölümünden alıntı

Özet şu şekildedir: Bir nesne A'nın hala ona referans veren yalnızca bir SoftReference nesnesi varsa, normal koşullar altında, bellek yeterli olduğunda A temizlenmeyecektir. Ama hafıza sıkıysa, özür dilerim, sizi A'yı temizlemeye götürmeliyim. Bu aynı zamanda "yumuşak" yumuşak atıfın düzenlemesidir.

Zayıf referans

Zayıf referanslar aynı zamanda gerekli olmayan nesneleri tanımlamak için de kullanılır.Güçleri yumuşak referanslardan daha zayıftır.Zayıf referanslarla ilişkilendirilen nesneler Çöp toplama sırasında, bu nesne yalnızca zayıf referanslarla ilişkilendirilirse (onunla ilişkili güçlü bir referans yoktur), Daha sonra bu nesne geri dönüştürülecek. "Java Sanal Makinesi Derinlemesine Anlama" bölümünden alıntı

Zayıf referanslar, yumuşak referanslardan daha zayıftır, o kadar zayıftır ki, yeterli hafıza olduğunda bile, A nesnesine yalnızca bir WeakReference nesnesi tarafından referans veriliyorsa, üzgünüm, ama sizi almam gerekiyor. Zayıf referansların "zayıf" olmasının nedeni de budur.

Phantom referansı

Bir nesnenin bir hayali referansa sahip olup olmadığı, yaşam süresini hiç etkilemeyecektir ve bir nesnenin bir örneğini bir hayali referans aracılığıyla elde etmek imkansızdır. Bir nesne için sanal bir referans ilişkisi kurmanın tek amacı, nesne toplayıcı tarafından geri alındığında bir sistem bildirimi almaktır. "Java Sanal Makinesi Derinlemesine Anlama" bölümünden alıntı

Bu, yukarıdaki zayıf referanstan daha zayıftır ve hatta bir dereceye kadar bir referans değildir, çünkü yukarıdaki ikisinin aksine, orijinal referans get yöntemi ile elde edilebilir ve get yönteminin üzerine yazılır ve döndürülür:

public class PhantomReference < T > Referans'ı genişletir < T > { public T get { dönüş; } }

Son referans

Yukarıdaki dördüne ek olarak, sonlandırıcı yöntemini kapsayan sınıf nesnesi temizlenmeden önce sonlandırıcı yönteminin yürütülmesini desteklemek için kullanılan FinalReference adlı özel bir referans vardır.

HotSpot kaynak kodunda yukarıdaki referansların tanımları aşağıdaki gibidir:

Temizleme stratejisi

Peki, JVM, GC gerçekleştirirken bu özel referans türlerini nasıl farklı şekilde ele alır?

HotSpot'ta, ne tür bir çöp toplayıcı olursa olsun, tüm referansları GC Kökleri üzerinden geçtikten sonra, nesne temizlemeyi gerçekleştirmeden önce, temizlenmesi gereken referansları işlemek için ReferenceProcessor :: process_discovered_references işlevini çağırır.Bu, bu işlev aracılığıyla yapılır. İsim de görülebilir.

Bu işlevi çağırmadan önce bir adım daha vardır: işleme ilkesini ayarlamak için ReferenceProcessor :: setup_policy'yi çağırmak.

Fonksiyon mantığı çok basittir. _Always_clear_soft_ref_policy veya _default_soft_ref_policy kullanılıp kullanılmayacağını belirlemek için always_clear bool parametresini kullanın.

Adından da anlaşılacağı gibi, biri her zaman yumuşak referansları temizlemek, diğeri ise varsayılan strateji. Bakalım bu iki stratejinin ne olduğunu görelim:

İlki, AlwaysClearPolicy olan her zaman net bir politikadır

Sonra varsayılan politika vardır: Geçerli işlem sunucu modundaysa, LRUMaxHeapPolicy'yi seçin, aksi takdirde istemci modunda LRUCurrentHeapPolicy'yi seçin.

ReferencePolicy bir temel sınıftır, should_clear_reference temel sanal işlevi karşılık gelen referansı temizleyip temizlemeyeceğine karar vermek için kullanılır. HotSpot, referans işleme stratejileri için dört alt sınıf sağlar:

  • NeverClearPolicy: Asla temizleme

  • AlwaysClearPolicy: Her zaman temiz

  • LRUCurrentHeapPolicy: Yakın zamanda kullanılmadıysa temizleyin (mevcut kalan yığın alanına göre en son zamanı değerlendirin)

  • LRUMaxHeapPolicy: Yakın zamanda kullanılmadıysa temizleyin (kalan maksimum yığın alanına bağlı olarak en son zamanı tahmin edin)

Öyleyse, setup_policy işleme politikasını belirlediğinde always_clear doğru mu yanlış mıdır? Çünkü bu, yumuşak referans işleme stratejisinin sonraki seçiminin LRUCurrentHeapPolicy / LRUMaxHeapPolicy veya AlwaysClearPolicy olup olmadığını doğrudan belirler.

Bu noktayla ilgili olarak, HotSpot kaynak kodunda, farklı çöp toplayıcılar biraz farklı işler, ancak genel olarak, always_clear parametresi çoğu senaryoda yanlıştır.Sadece birden fazla bellek ayırma girişiminin tümü başarısızlıkla sonuçlandığında dener. Daha fazla alan boşaltmak için yumuşak referansları temizlemek için bunu true olarak ayarlayın.

Lütfen yukarıdaki stratejileri aklınızda bulundurun, strateji seçimi yumuşak referansların sonraki işlemlerini etkileyecektir.

Özel referanslar için işleme mantığının analizi

Process_discoverd_references işlevine geri dönün, bu işlevin içeriğine bir göz atın:

Yumuşak, Zayıf, Son ve Hayali dört özel referansın işlenmesini tamamlamak için işlevin process_discovered_reflist'i çağırdığı değişkenlerin isimlerini ve yorumlarını görmek zor değildir.

Bu işlev aşağıdaki şekilde bildirilmiştir:

İkinci parametre politikasına ve üçüncü parametre clear_referent'e odaklanın. Yukarıdaki fonksiyon çağrısında aktarılan parametrelere tekrar bakın:

Başvuru türü policyclear_referentSoftReference boş değil trueWeakReference trueFinalReferencefalsePhantomReference true

Dört referansın farklı kaderini farklı parametreler belirleyecektir.

Process_discovered_reflist'e daha fazla baktığımızda, bu işlevin içindeki referansların işlenmesi 3 aşamaya bölünmüştür. Bunlara birer birer bakalım. İlki, ilk aşamadır:

İlk aşama: yumuşak referansların işlenmesi

Yorumlardan, ilk aşamanın yalnızca yazılım referansları için olduğu ve yukarıdaki tabloyla birlikte SoftReference olduğu, yalnızca yazılım referansları işlenirken politika parametresinin boş olmadığı görülebilir.

İşlemeyi fiilen gerçekleştiren işlem_faz1 işlevinde, tüm yazılım referansları taranır.Artık canlı olmayan nesneler için, yukarıda belirtilen stratejideki process_discovered_references işlevi, referansın temizlenecek listeden tutulması veya kaldırılması gerekip gerekmediğini belirlemek için kullanılır.

Aşama 2: Hayatta kalan nesneleri ortadan kaldırın

Bu aşamadaki ana çalışma, temizlenecek listeden hala hayatta olan nesneye yönelik tüm referansları (ona işaret eden başka güçlü referanslar da vardır) kaldırmaktır:

Üçüncü aşama: nesneye kalan referansı kesin

Üçüncü aşamada, dışarıdan geçirilen clear_referent parametresine göre referansın temizlenecek veya saklanacak listeden çıkarılıp çıkarılmayacağı belirlenir.

Yukarıdaki tabloya tekrar baktığımızda, Zayıf, Yumuşak ve Hayalet'in üç tip referansı için, clear_referent parametresi doğrudur, bu, son aşamada, tutulması gereken her şeyin korunduğu ve geri kalanının tamamen ortadan kaldırılması gerektiği anlamına gelir. Yani bu fonksiyonda, kalan referanslardaki referans alanı ayarlanır.Bu noktada, nesne ile bu özel referanslar arasındaki son bağlantı da kesilir ve sonraki GC'de kaçmak zor olacaktır.

Son referanslar için bu parametre yanlıştır ve üçüncü aşama onu nesneden ayırmayacaktır. Bağlantının kesilmesinin zamanlaması, sonlandırıcı yöntemi uygulandıktan sonradır. Bu nedenle, bu GC turunda, sonlandırıcı yöntemini kapsayan bir sınıf nesnesi geçici olarak ömrünü kurtaracaktır.

özet

Bunu görünce, herkesin biraz dağınık olduğu tahmin ediliyor, pek çok referans türü ve bu kadar çok işlem aşaması, kafaların hepsi aktarılıyor. Korkmayın, Xuanyuan Jun ilk okuduğunda durum aynıydı, şimdi bu makaleyi yazsa bile, kaynak kodunu defalarca tatması ve araştırma ve sertifikasyondan sonra çözmesi gerekiyor.

Ardından, her aşamada her bir referans türünün durumunu sıralıyoruz:

Yumuşak referans

  • İlk aşama: Artık hayatta olmayan nesneler için, stratejiye göre temizlenecekleri listeden kaldırılıp kaldırılmayacağını belirleyin.

  • İkinci aşama: Hala hayatta olan nesnenin referansı temizlenecek listeden kaldırılır

  • Üçüncü aşama: Birinci aşamanın temizleme stratejisi, yumuşak referansları temizlemeye karar verirse, üçüncü aşamada nesneyle son bağlantıyı kesmek için kalan yumuşak referanslar boş bırakılacaktır; ilk aşamanın temizleme stratejisi yumuşak referansları temizlememeye karar verirse, Ardından üçüncü aşamaya, temizlenecek liste boştur ve yumuşak referans korunur.

  • Sonuç: Yalnızca yumuşak bir referansla işaret edilen bir nesne temizlendiğinde, temizleme stratejisine bağlıdır Kök neden, mevcut yığın alanı kullanımına bağlıdır.

Zayıf referans

  • İlk aşama: İşlem yok, ilk aşamada yalnızca yazılım referansları işleniyor

  • İkinci aşama: Hala hayatta olan nesnenin referansı temizlenecek listeden kaldırılır

  • Üçüncü aşama: Nesnelerin kalan zayıf referansları artık canlı değildir, zayıf referanslar boş bırakılır ve nesne ile son bağlantı kesilir

  • Sonuç: Yalnızca zayıf bir referansla işaret edilen bir nesne, ilk GC'de temizlenecektir.

Phantom referansı

  • İlk aşama: İşlem yok, ilk aşamada yalnızca yazılım referansları işleniyor

  • İkinci aşama: Hala hayatta olan nesnenin referansı temizlenecek listeden kaldırılır

  • Üçüncü aşama: nesnelere yapılan diğer hayali referanslar artık canlı değildir, zayıf referanslar boş bırakılır ve nesneyle son bağlantı kesilir

  • Sonuç: Bir fantom referans tarafından işaret edilen bir nesne, ilk GC'de temizlenecektir.

S5: Nesne taşındıktan sonra referans nasıl düzeltilir?

Şimdiye kadar hepimiz, çöp toplama sürecine nesnenin "geçişi" eşlik edecek ve nesne "taşındığında" önceki tüm referanslar (yığındaki referanslar, öbekteki nesnelerin üye değişken referansları, vb.) Başarısız olur. Programımızın GC'den sonra hala hatasız çalışabilmesinin nedeni, JVM'nin bunun arkasında çok iş yapmış olması, böylece programımızın kısa bir STW gibi görünmesi ve uyandıktan sonra hiçbir şey olmamış gibi görünmesidir. Aynı, ne yapmalıyım.

Doğal olarak şu soruyu da düşünebiliriz: Nesne taşındıktan sonra referans nasıl düzeltilir?

Bu soruyu cevaplamadan önce, Java'da referansların nesneleri nasıl "işaret ettiğine" bir göz atalım. JVM geliştirme tarihinde iki çözüm ortaya çıktı:

Seçenek 1: Kulp

Referansın kendisi doğrudan nesneye işaret etmez, nesnenin adresi bir tabloda saklanır ve referansın kendisi, bu tablodaki tablo girişinin yalnızca indeks değeridir. "Java Sanal Makinesi Kapsamlı Anlayışı" kitabından bir alıntı:

Bu tür bir düşünce aslında pek çok yerde kullanılmaktadır.Windows platformunda geliştirme yapan arkadaşlara yabancı değildir.Windows pencereleri veya çekirdek nesneleri (Mutex, Event vb.) Olsun, bunlar çekirdekte tanımlanır ve yönetilir.Güvenlik için hayır Çekirdek nesnesinin adresini doğrudan açığa çıkaran uygulama katmanı, yalnızca bir tutamaç değeri alabilir ve bu tutamaç aracılığıyla etkileşime girebilir.

Linux platformunun dosya tanımlayıcısı da bu fikrin somutlaşmış halidir. Modern işletim sistemleri tarafından kullanılan sanal bellek adresi bile aynıdır Bellek adresi fiziksel belleğin adresi değildir, ancak adres kod çözme tablosu tarafından dönüştürülmesi gerekir.

Bu yöntemin faydaları açıktır Nesne taşındıktan sonra, tüm referansların kendilerinin düzeltilmesine gerek yoktur, sadece bu tablodaki karşılık gelen nesne adresinin düzeltilmesi gerekir.

Dezavantajlar da açıktır: Nesneye erişim bir "çeviri dönüşümü" gerektirir ve performans tehlikeye atılır.

Seçenek 2: Doğrudan işaretçi

İkinci çözüm, doğrudan işaretçi yöntemidir, fark yaratacak bir aracı yoktur, referansın kendisi bir göstericidir. "Java Sanal Makinesini Derinlemesine Anlamak" kitabındaki resimden tekrar alıntı yapmama izin verin:

İlk yöntemle karşılaştırıldığında, ikisinin avantajları ve dezavantajları değiş tokuş edilir.

Avantajlar: Nesnelere daha doğrudan erişim ve daha hızlı performans. Dezavantajlar: Nesne taşındıktan sonra referansı onarmak zahmetlidir.

HotSpot tarafından temsil edilen modern ticari JVM, nesne erişim konumlandırması için doğrudan işaretçi yöntemini seçmiştir.

Bu şekilde, mevcut tüm referans değerlerinin değiştirilmesi gerekir ve iş yükü küçük değildir.

Neyse ki, bu makalenin üçüncü bölümünde, S3: GC Kökleri nasıl bulunur? Makalede tanıtılan OopMap, kurtarıcının kimliğini bir kez daha oynadı.

OopMap'te depolanan bilgiler JVM'ye referansların nerede olduğunu söyleyebilir.Bu anahtar bilgi yalnızca çöp toplama için GC Köklerini bulmak için değil, aynı zamanda referansları düzeltmek için önemli bir kılavuzdur.

referans

  • RednaxelaFX: Yığındaki işaretçileri / referansları bulun

Sonuna yaz

Umarım bu makaleyi okuduktan sonra, yalnızca GC'nin kendisinin ne olduğunu bilmekle kalmayacak, aynı zamanda GC'nin sahne önünde ve arkasındaki çalışmalarını daha iyi anlayacaksınız, böylece röportaj yapanla GC hakkında konuştuğunuzda konuşabilir ve gülersiniz ~ daha fazla Birkaç tur savaşın

Tabii ki, yazarın sınırlı teknik seviyesi nedeniyle, 10.000 karakter uzunluğunda denemeler yazmak zahmetlidir.Makalede yazılı veya teknik tartışmada hatalar varsa, lütfen zamanında düzeltmeler için bunu belirttiğinizden emin olun. Teşekkürler.

Taş çekiç! Python bu yıl zorlanacak mı? Programcı: Ben deliyim
önceki
Veri analisti ve algoritma mühendisi, Python'da doğan programcılar nasıl seçim yapar?
Sonraki
Üç büyük operatör, yeni ve eski kullanıcı paketlerinde farklı haklar sorununu çözecek; Luo Yonghao, Douyin ile işbirliği yapıyor; Git 2.26 yayınlandı | Geek Headlines
İnsan-Bilgisayar Etkileşimi İnsan Hayatını Nasıl Değiştirir | Milyonlarca İnsan Yapay Zekayı Öğreniyor
Bir SIM kart nedeniyle Bitcoin Cash'te 30 milyon dolar mı kaybettiniz?
Zuckerberg tarafından yok edilen defter, Facebook'un tüm başarılarını ve başarısızlıklarını gizliyor
Sishui İlçesi Halk Kongresi Parti Şubesi "Bir Marka Şovu Stili Oluşturmak, Yoksulluğu Kalp ve Sevgiyle Azaltmaya Yardımcı Olmak"
"Şaheser" yalnızlık içinde gelişir
Wuhan'daki Fransız gönüllülerle diyalog: Korumayı artırmak için Çin tıbbı içmek ve bir kase sıcak kuru erişte yemek yeterli
Jingzhou, Hubei'den 900'den fazla çalışan Dongguan'a geldi ve belediye parti komitesi sekreteri ve belediye başkanı onları yerinde aldı.
İlk Hubei göçmen işçileri, işe dönmek için Shenzhen'e geri döndüler, özel tren geldi! 169 çalışanın noktadan noktaya teslimat
Özel G4368 treni: Hubei'den Guangdong'a ilk yeniden işleme treni
Avustralya'da salgının tırmanması: Süpermarketlerde 14 gün boyunca fast food yedim ve 20.000 denizaşırı tıp öğrencisi savaşa gidebilir
Salgın durumunun 60 günlük gözlemi: Şangay ve Shandong sık sık bilgilendirilir ve Guangdong dışındaki ithal vakaların yörüngesi detaylandırılır.
To Top