Bu çöp koleksiyonunu okuduktan sonra, görüşmeci ile güreşmekte sorun yok

Yazar | Code Hai

Kaynak | Code Sea (ID: seaofcode)

Sorumlu Editör | Wu Xingling

Java'nın C / C ++ ile karşılaştırıldığında en önemli özelliği, C / C ++ 'nın en zahmetli bellek yönetimi sorununu çözen ve programcıların programa odaklanmasına olanak tanıyan otomatik çöp toplamanın (bundan sonra GC otomatik çöp toplamaya atıfta bulunmak için kullanılacaktır) tanıtılmasıdır. Kendi başına bellek geri dönüşümü konusunda endişelenmenize gerek yok, bu Java'nın bu kadar popüler olmasının önemli nedenlerinden biridir. GC gerçekten programcıların üretkenliğini serbest bırakır, ancak programcıların varlığını algılaması zordur. Yemekten sonra tabakları masanın üzerine koyun ve çıkın, garson bu tabakları sizin için temizleyecektir, garsonun onları ne zaman toplayacağı ve nasıl toplayacağı umurunuzda değil.

Bazı insanlar, GC temizliği otomatik olarak tamamladığından, GC'yi anlamamakta yanlış bir şey olmadığını söylüyor. Çoğu durumda sorun yoktur, ancak bazı performans ayarı, sorun giderme vb. İçeriyorsa, GC'nin derinlemesine anlaşılması hala gereklidir.Meituan, hizmet yanıt süresini TP90 ve TP99'u azaltmak için JVM ile ilgili GC parametrelerini ayarladıktan sonra. 10ms + sonrasında, hizmet kullanılabilirliği büyük ölçüde iyileştirildi! Bu nedenle, derinlemesine bir GC anlayışı, iyi bir Java programcısı olmak için gerekli bir derstir!

Çöp toplama iki bölüme ayrılmıştır, ilk bölüm öncelikle aşağıdakileri içeren çöp toplama teorisinden bahsedecektir:

  • GC'nin birkaç ana toplama yöntemi: marka kaldırma, markalama ve kopyalama algoritmalarının ilkeleri ve özellikleri ve bunların avantajları ve dezavantajları?
  • Neden Serial, CMS, G1, vb. Gibi çeşitli toplayıcı türleri vardır. Her birinin avantajları ve dezavantajları nelerdir ve neden evrensel çöp toplayıcı yoktur?
  • Yeni nesil neden Cennet, S0 ve S1'in üç alanına ayarlanmalı? Dikkat edilmesi gereken noktalar nelerdir?
  • Yığın dışı bellek GC tarafından kontrol edilmez, peki nasıl serbest bırakılır?
  • Nesne geri dönüştürülebilirse, geri dönüştürülecek mi?
  • SafePoint nedir ve Stop The World nedir?
  • Sonraki bölüm, esas olarak aşağıdakileri içeren çöp toplama uygulamasına odaklanmaktadır:

  • GC günlük formatına nasıl bakılır?
  • OOM'nin meydana geldiği ana senaryolar nelerdir?
  • OOM oluşur, nasıl bulunur ve yaygın olarak kullanılan bellek hata ayıklama araçları nelerdir?
  • Bu makale çöp toplamayı aşağıdaki yönlerden açıklayacaktır:

  • JVM bellek alanı
  • Çöp nasıl belirlenir
  • Referans sayma
  • Erişilebilirlik algoritması
  • Ana çöp toplama yöntemleri
  • Mark süpürme
  • Kopyalama yöntemi
  • İşaretleme
  • Nesil toplama algoritması
  • Çöp toplayıcı karşılaştırması
  • Pek çok metin var, ancak okuyucuların anlamasını kolaylaştırmak için pek çok GC animasyonu eklendi.İzledikten sonra çok fazla kazanım olacağına inanıyorum.

    JVM bellek alanı

    Çöp toplama mekanizmasını anlamak için önce çöp toplamanın esas olarak hangi verileri topladığını ve verilerin esas olarak hangi alanda olduğunu bilmemiz gerekir, bu nedenle JVM'nin bellek alanına birlikte bakalım

    • Sanal makine yığını: yöntem yürütüldüğünde bellek modelini tanımlar. İş parçacığına özeldir.Yaşam döngüsü iş parçacığınınkiyle aynıdır. Her yöntem yürütüldüğünde, esas olarak yürütme yönteminin bir bölümünü kaydeden bir yığın çerçevesi (aşağıya bakın) oluşturulur. Değişken tablo, işlemsel yığın, dinamik bağlantı ve yöntem dönüş adresi ve diğer bilgiler, yöntem yürütüldüğünde, yığın itilir ve yöntem, yöntem yürütüldükten sonra yığından çıkar, pop, veriyi boşaltmaya eşdeğerdir ve yığın ve popun zamanlaması açıktır, bu nedenle bu alan GC gerekmez.
    • Yerel yöntem yığını: İşlev, sanal makine yığınına çok benzer. Temel fark, sanal makine yığınının, sanal makine Java yöntemlerini çalıştırdığında hizmet verirken, yerel yöntem yığını, sanal makine yerel yöntemleri yürüttüğünde hizmet vermesidir. Bu alan GC gerektirmez.
    • Program sayacı: iş parçacığına özgüdür, aşağıdaki bayt kodu içeriği gibi mevcut evre tarafından yürütülen bayt kodunun satır numarası göstergesi olarak kabul edilebilir, her bayt kodunun önünde bir sayı (satır numarası) vardır '' Bunu program sayacında depolanan içerik olarak düşünebiliriz

    Bu numaraları kaydetmenin ne faydası var? Java sanal makinesinin çoklu okuma işleminin sırayla iş parçacıkları arasında geçiş yaparak ve işlemci zamanı tahsis ederek gerçekleştirildiğini biliyoruz. Herhangi bir zamanda, bir işlemci yalnızca bir iş parçacığı yürütecektir. Bu iş parçacığının ayrılan zaman dilimi yürütüldüğünde (iş parçacığı askıya alındı), işlemci yürütmek için başka bir iş parçacığına geçecektir.Parlak olan iş parçacığının bir sonraki çalıştırılışında (iş parçacığını uyandırmak), en son nerede olduğunu nasıl anlarsınız? Program sayacına kaydedilen satır numarası göstergesinden bilinebilir, bu nedenle program sayacının ana işlevi, iş parçacığının çalışma durumunu kaydetmektir, böylece iş parçacığı uyandırıldığında, en son askıya alındığı durumdan çalışmaya devam edebilir. Program sayacının, Java Sanal Makinesi spesifikasyonunda herhangi bir OOM koşulu belirtmeyen tek alan olduğunu ve bu nedenle bu alanın GC gerektirmediğini unutmayın.

    • Yerel bellek: iş parçacığı paylaşımlı alan, Java 8'de yerel bellek, meta boşluk ve doğrudan bellek dahil olmak üzere genellikle yığın dışı bellek dediğimiz şeydir.Java 8'in bellek alanı ile Java 8'den önceki JVM arasındaki farka yukarıdaki şekilde dikkat edin, Java 8'den önce, aslında HotSpot sanal makinede kalıcı üretime atıfta bulunan kalıcı bir nesil kavramı vardı.JVM spesifikasyonu tarafından tanımlanan yöntem alanı işlevini uygulamak için kalıcı nesil kullanır. Esas olarak sınıf bilgilerini, sabitleri, statik değişkenleri ve anlık Derleyici kodu derledikten sonra, bu bölüm yığın içinde uygulanır ve GC tarafından yönetilir.Ancak, kalıcı nesil üst sınırı -XX: MaxPermSize olduğundan, sınıf dinamik olarak oluşturulmuşsa (sınıf bilgilerini kalıcı üretime koyun) veya çok sayıda String.intern'i çalıştırmak için (kalıcı üretimde alan dizesini sabit alana koymak), OOM'ye neden olmak kolaydır.Bazıları kalıcı neslin yeterince büyük ayarlanabileceğini söyler, ancak uygun bir boyut, sınıf sayısı ve sabit sayısı belirlemek zordur. Etki miktarı harika. Bu nedenle, Java 8'de yöntem alanının uygulanması, yerel bellekteki meta boşluğuna taşınır, böylece yöntem alanı JVM tarafından kontrol edilmez ve GC gerçekleştirilmez, böylece performansı iyileştirir (bir GC, bir Durdurmaya neden olur. Word, performansın bir dereceye kadar etkilenmesine neden olur, daha sonra bahsedilecektir) ve kalıcı neslin sınırlı boyutunun neden olduğu OOM anormalliği yoktur (toplam belleğin 1G olduğu varsayılırsa, JVM'ye bellek 100M tahsis edilir, teorik olarak meta alan 2G tahsis edilebilir. -100M = 1.9G, alan boyutu yeterlidir), meta alanda birleşik yönetim için de uygundur. Özetle, bu alan Java 8'den sonra GC gerektirmez.

    Seslendirme: Bir problem hakkında düşündüğünüzde, yığın dışı bellek GC tarafından kontrol edilmez ve GC aracılığıyla serbest bırakılamaz. O zaman hangi biçimde serbest bırakılmalı? Onu sadece yaratamaz ve bırakamazsınız.Bu durumda, hafıza yakında dolabilir. Ayrıntılı açıklama için lütfen makalenin sonundaki referans makalesine bakın.

    • Yığın: İlk birkaç veri alanı GC değildir, bu nedenle sadece yığın kalır Evet, bu GC'nin oluştuğu alandır! Nesne örnekleri ve dizileri öbek üzerinde tahsis edilir ve GC de esas olarak bu iki tür veriyi geri dönüştürür.Bu aynı zamanda daha sonra analiz etmemiz gereken alandır.

    Çöp nasıl belirlenir

    Önceki bölümde, JVM'nin bellek alanını ayrıntılı olarak tanımladık.GC'nin esas olarak yığın içinde oluştuğunu bilerek, GC, nesne örneğinin veya yığındaki verilerin çöp olup olmadığını nasıl belirler veya belirli verilerin çöp olup olmadığını belirlemek için hangi yöntemler vardır? .

    Referans sayma

    Düşünmenin en kolay yolu referans sayma yöntemidir.Referans sayma yöntemi olarak adlandırılır. Basitçe ifade etmek gerekirse, nesneye bir kez referans verilir ve referansların sayısı nesne başlığına eklenir. Referans verilmiyorsa (referans sayısı 0'dır), Daha sonra bu nesne geri dönüştürülebilir.

    Stringref = newString ("Java");

    Yukarıdaki ref1 kodu sağda tanımlanan nesneye atıfta bulunur, bu nedenle referans sayısı 1'dir.

    Yukarıdaki koddan sonra bir ref = null eklerseniz, nesneye başvurulmadığından, referans sayısı 0 olarak ayarlanır ve herhangi bir değişken tarafından başvurulmadığından bu sefer geri dönüştürülür. Animasyon aşağıdaki gibidir.

    Görünüşe göre referans sayma ile ilgili bir sorun yok, ancak büyük bir sorunu çözemez: döngüsel referanslar! Döngüsel referans nedir?

    publicclassTestRC {TestRCinstance; publicTestRC (Stringname) {} publicstaticvoidmain (Stringargs) {// İlk adım Aa = newTestRC ("a"); Bb = newTestRC ("b"); // İkinci adım a.instance = b; b .instance = a; // Üçüncü adım a = null; b = null;}}

    Adım adım bir resim çizin:

    Üçüncü adımda, hem a hem de b null olarak ayarlansa da, işaret ettikleri nesneler birbirlerini gösterdikleri için (referans sayısı 1'dir), geri dönüştürülemezler, çünkü tam olarak dairesel referans problemi çözülemez. Bu nedenle, modern sanal makineler, bir nesnenin geri dönüştürülmesi gerekip gerekmediğini belirlemek için referans sayımı kullanmaz.

    Erişilebilirlik algoritması

    Modern sanal makineler temelde bir nesnenin canlı olup olmadığını belirlemek için bu algoritmayı kullanır.Erişilebilirlik algoritmasının prensibi, GC Kökü adı verilen bir dizi nesneden başlar, işaret ettikleri bir sonraki düğüme ve ardından başlangıç noktası olarak sonraki düğüme götürür. Bu düğüm tarafından gösterilen sonraki düğüme gidin. . . (Bu şekilde, GC Kökü tarafından dizilen bir satıra referans zinciri denir), tüm düğümler geçilene kadar, ilgili nesneler GC Kökünden başlayan herhangi bir referans zincirinde değilse, bu nesneler "çöp" olarak değerlendirilecektir. ", GC tarafından geri dönüştürülecektir.

    Şekilde gösterildiği gibi, erişilebilirlik algoritması yukarıdaki dairesel referans problemini çözmek için kullanılırsa, a ve b'ye GC Kökünden ulaşılamadığından, a ve b geri dönüştürülebilir.

    a, b Nesne geri dönüştürülebilir ise, geri kazanılacak mı? Hayır, nesnenin finalize yöntemi nesneye mücadele etme şansı verir. Nesne erişilemez olduğunda (geri dönüştürülebilir), bir GC oluştuğunda, önce nesnenin olup olmadığını belirleyecektir. Finalize yöntemi çalıştırılır.İşlem yapılmazsa, önce finalize yöntemi çalıştırılır.Bu yöntemde mevcut nesneyi GC Roots ile ilişkilendirebiliriz, böylece finalize yöntemini çalıştırdıktan sonra, GC nesnenin tekrar ulaşılabilir olup olmadığına karar verir. Erişilebilir değilse, o zaman Ulaşılabilirse geri dönüştürülecek, geri dönüştürülmeyecek!

    Not: Sonlandırma yöntemi yalnızca bir kez yürütülecektir.Nesne, sonlandırma yöntemi ilk çalıştırıldığında erişilebilir hale gelirse, geri dönüştürülmez, ancak nesne yeniden GC ise, sonlandırma yöntemi göz ardı edilir ve nesne geri dönüştürülür! Hatırla bunu!

    Peki bu GC Kökleri tam olarak nedir ve hangi nesneler GC Kökleri olarak kullanılabilir? Aşağıdaki kategoriler vardır

    • 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
    Sanal makine yığınında başvurulan nesneler

    Aşağıdaki kodda gösterildiği gibi, a, yığın çerçevesindeki yerel bir değişkendir. A = null olduğunda, a şu anda bir GC Kökü gibi davrandığından, a, new Test () 'in işaret ettiği orijinal örnekten ayrılır, böylece nesne Geri dönüşüm.

    publicclassTest {publicstaticvoidmain (Stringargs) {Testa = newTest (); a = null;}} Yöntem alanında sınıf statik özellikleri tarafından başvurulan nesneler

    Aşağıdaki kodda gösterildiği gibi, yığın çerçevesindeki yerel değişken a = null olduğunda, a ile gösterilen nesnenin GC Kökü (değişken a) ile bağlantısı kesildiği için, a ile gösterilen nesne geri dönüştürülecektir ve s verdiğimiz için Bir değişken referansı atanır, s şu anda sınıfın statik bir özellik referansıdır, bir GC Kökü olarak işlev görür ve işaret ettiği nesne hala canlıdır!

    publicclassTest {publicstaticTests; publicstaticvoidmain (Stringargs) {Testa = newTest (); a.s = newTest (); a = null;}} Yöntem alanında sabitler tarafından başvurulan nesneler

    Aşağıdaki kodda gösterildiği gibi, s sabitiyle gösterilen nesne geri dönüştürülmeyecektir çünkü a ile gösterilen nesne geri dönüştürülmüştür.

    publicclassTest {publicstaticfinalTests = newTest (); publicstaticvoidmain (Stringargs) {Testa = newTest (); a = null;}} Yerel yöntem yığınında JNI tarafından başvurulan nesneler

    Bu, yerel yöntemin ne olduğunu bilmeyen çocuk ayakkabıları için basit bir açıklamadır: Sözde yerel yöntem, Java'da Java olmayan kodu çağırmak için bir arayüzdür. Bu yöntem Java'da uygulanmaz ve C veya Python gibi diğer dillerde de uygulanabilir. Java JNI kullanır. Yerel yöntemi çağırmak için ve yerel yöntem bir kitaplık dosyası biçiminde saklanır (WINDOWS platformunda DLL dosyası, UNIX makinesinde SO dosyası). Yerel kütüphane dosyalarının dahili yöntemlerini çağırarak, JAVA yerel makine ile yakın bağlantıyı gerçekleştirebilir ve sistem düzeyinde arayüz yöntemlerini çağırabilir, yoksa anlamıyor musunuz? Yerel yöntemlerin tanımı ve kullanımına ayrıntılı bir giriş için makalenin sonundaki referansa bakın.

    Bir Java yöntemi çağrılırken, sanal makine bir yığın çerçevesi oluşturur ve bunu Java yığınına iter.Yerel bir yöntemi çağırdığında, sanal makine Java yığınını değiştirmeden tutar ve Java yığın çerçevesine yeni bir çerçeve göndermez. Makine, dinamik olarak bağlanır ve belirtilen yerel yöntemi doğrudan çağırır.

    JNIEXPORTvoidJNICALLJava_com_pecuyu_jnirefdemo_MainActivity_newStringNative (JNIEnv * env, jobjectinstance, jstringjmsg) {... // cached String classjclassjc = (* env) - > FindClass (env, STRING_PATH);

    Yukarıdaki kodda gösterildiği gibi, Java yukarıdaki yerel yöntemi çağırdığında, JC yerel yöntem yığını tarafından yığına itilecektir.JC, yerel yöntem yığınında JNI nesne referansı dediğimiz şeydir, bu nedenle yalnızca bu yerel yöntem çalıştırıldıktan sonra gerçekleştirilecektir. yayınlandı.

    Ana çöp toplama yöntemleri

    Önceki bölümde, erişilebilirlik algoritmasının hangi verilerin çöp olduğunu belirlemek için kullanılabileceğini, dolayısıyla bu çöpün nasıl geri dönüştürüleceğini öğrendik. Esas olarak aşağıdaki yollar var

    • Mark kaldırma algoritması
    • Algoritmayı kopyala
    • İşaretleme

    Mark kaldırma algoritması

    Adımlar basit

  • İlk olarak, erişilebilirlik algoritmasına göre karşılık gelen geri dönüştürülebilir nesneleri işaretleyin (şekildeki sarı kısım).
  • Geri dönüştürülebilir nesneleri geri dönüştürün. İşlem gerçekten çok basit ve verileri taşımaya gerek yok Sorun nedir? Yukarıdaki resme dikkatlice bakın, evet, hafıza parçalanması! Yukarıdaki şekildeki yığın içerisine 4M veya 5M alan tahsis etmek istersek açıkça başarısız olacaktır, nasıl çözülür Yukarıdaki kullanılmayan 2M, 2M, 1M hafızaları bağlayabilirsek bağlanabilir. Sadece 5M boş alana sahip bir alana yapın, nasıl yapılır?
  • Algoritmayı kopyala

    Yığını A ve B olmak üzere iki alana bölün. Alan A, nesnelerin tahsis edilmesinden sorumludur ve alan B tahsis edilmemiştir. Kalan nesneleri işaretlemek için yukarıda belirtilen gösterim yöntemini A alanı için kullanın (aşağıdaki şeklin silinmesine gerek yoktur) ve sonra A alanındaki tüm hayatta kalan nesneler B alanına kopyalanır (hayatta kalan nesneler sırayla yan yana düzenlenir) Son olarak, alan A'daki tüm nesneler boş alan açmak için temizlenir ve bu da bellek parçalanması sorununu çözer.

    Bununla birlikte, kopyalama algoritmasının eksiklikleri açıktır: Örneğin, yığına 500M bellek tahsis edilirse, yalnızca 250M kullanılabilir ve alan hiçbir neden olmaksızın yarı yarıya azalır! Bu kesinlikle kabul edilemez! Ek olarak, hayatta kalan nesneler her geri dönüştürüldüğünde diğer yarısına taşınmalıdır, bu verimsizdir (dizi öğelerini silmeyi ve ardından silinmemiş öğeleri bir uca kaydırmayı düşünebiliriz, verimlilik açıkça endişe vericidir)

    İşaretleme

    İlk iki adım, işaret kaldırma yöntemiyle aynıdır, fark, işaret kaldırma yöntemine bir sıralama işlemi eklemesidir, yani hayatta kalan tüm nesneler, yan yana düzenlenmiş (şekilde gösterildiği gibi) bir uca taşınır ve ardından diğer uç temizlenir. Bu şekilde bellek parçalanması sorunu çözülür.

    Ancak eksiklikler de açıktır: hayatta kalan nesneler, çöp her kaldırıldığında sıklıkla taşınır ve bu çok verimsizdir.

    Nesil toplama algoritması

    Kuşak toplama algoritması, yukarıdaki algoritmaları entegre eder, bu algoritmaların avantajlarını birleştirir ve eksikliklerinden büyük ölçüde kaçınır.Bu nedenle modern sanal makinelerin kullandığı tercih edilen algoritmadır.O kadar da bir algoritma değildir, bir strateji değildir çünkü Yukarıda bahsedilen algoritmaları entegre eder. Neden nesillere göre toplamanız gerekiyor? Nesnelerin dağılımının hangi kuralları olduğunu görelim.

    Şekilde gösterildiği gibi: dikey eksen tahsis edilen baytları temsil eder ve yatay eksen program çalışma süresini temsil eder

    Şekilden, nesnelerin çoğunun kısa ömürlü olduğu ve kısa bir süre içinde kurtarıldığı görülebilir (IBM profesyonel araştırması, genel olarak, Minor GC'den sonra nesnelerin% 98'inin öldüğünü göstermektedir. Geri dönüştürülecek), bu nedenle kuşak toplama algoritması, yığını nesnenin yaşam döngüsüne göre genç nesil ve eski nesil olarak ayırır (Java 8'den önce kalıcı bir nesil vardır), varsayılan oran 1: 2'dir, yeni nesil, Eden alanına bölünür. Survivor alanı (S0 olarak kısaltılır), Survivor alanına (S1 olarak kısaltılır), üçünün oranı 8: 1: 1'dir, böylece yeni ve eski nesillerin özelliklerine göre en uygun çöp toplama algoritmasını seçebiliriz. GC, Young GC (Minor GC olarak da adlandırılır) ve yaşlılıkta oluşan GC, Old GC (Full GC olarak da adlandırılır) olarak adlandırılır.

    Seslendirme: Bir düşünün, yeni nesil neden bu kadar çok bölgeye sahip?

    Peki, nesilsel çöp toplama nasıl çalışıyor?

    Nesil toplama nasıl çalışır?

    1. Yeni nesil nesnelerin tahsisi ve kurtarılması

    Yukarıdaki analizden, çoğu nesnenin kısa bir süre içinde geri dönüştürüleceği ve nesnelerin genellikle Eden alanında tahsis edildiği görülebilir.

    Eden alanı neredeyse dolduğunda, Minor GC'yi tetikleyin

    Daha önce söylediğimiz gibi, çoğu nesne kısa sürede geri dönüştürülecek, bu nedenle Minor GC'den sonra sadece birkaç nesne hayatta kalacak ve S0 alanına taşınacaklar (bu nedenle Eden uzayının boyutu: S0: S1 = 8: 1: 1, Eden alanının S0, S1'den çok daha büyük olmasının nedeni, Eden alanında tetiklenen Küçük GC'nin nesnelerin çoğunu (% 98'e yakın) geri kazanması ve yalnızca az sayıda hayatta kalan nesne bırakmasıdır, bu yüzden onları taşıyın S0 veya S1 fazlasıyla yeterli) Aynı zamanda nesnenin yaşı bir artar (nesnenin yaşı, Küçük GC'nin oluşma sayısıdır) ve son olarak Eden alanındaki tüm nesneler, yer açmak için temizlenir. Animasyon aşağıdaki gibidir.

    Bir sonraki Küçük GC tetiklendiğinde, Eden alanında hayatta kalan nesneler ve S0 (veya S1) 'de hayatta kalan nesneler (S0 veya S1'deki hayatta kalan nesneler, her Küçük GC'den sonra geri alınabilir) S1'e (Eden ve S0'ın hayatta kalan nesne yaşı +1'dir) ve Eden ve S0'ın alanı aynı anda temizlenir.

    Bir sonraki Küçük GC tekrar tetiklenirse, önceki adım tekrarlanacaktır, ancak şimdi Eden'den gelir.S1 alanı hayatta kalan nesneleri S0 alanına kopyalayacaktır. Çöp toplama her seferinde, S0 ve S1 rolleri Eden'den değiştirilir. S0 (veya S1) hayatta kalan nesneyi S1'e (veya S0) taşır. Yani Eden bölgesinin çöp toplamasında çoğaltma algoritmasını kullanıyoruz, çünkü Eden alanında tahsis edilen nesnelerin çoğu Küçük GC'den sonra yok oluyor ve hayatta kalan nesnelerin sadece küçük bir kısmı kalıyor (bu yüzden Eden: S0: S1 varsayılan olarak 8: 1: 1'dir) S0 ve S1 alanları da nispeten küçüktür, bu nedenle çoğaltma algoritmasının neden olduğu sık nesne kopyalamanın neden olduğu ek yük en aza indirilir.

    2. Denek ne zaman yaşlılığa yükseltilecek?

    • Nesnenin yaşı belirlediğimiz eşiğe ulaştığında, şekilde gösterildiği gibi S0'dan (veya S1) yaşlılığa yükseltilecektir: yaş eşiği 15'e ayarlanır, bir sonraki Küçük GC gerçekleştiğinde, S0'da 15'e ulaşan bir nesne vardır. , Belirlediğimiz eşiğe ulaştı ve yaşlılığa terfi etti!
    • Büyük Nesneler Bir nesnenin tahsisi için büyük miktarda bitişik bellek gerektiğinde, nesnenin oluşturulması Eden alanında tahsis edilmeyecektir, ancak doğrudan eski nesilde tahsis edilecektir çünkü Eden alanında büyük bir nesne tahsis edilirse, Küçük GC S0'a taşınacaktır. , S1'in çok fazla ek yükü olacak (nesne daha büyük, kopya daha yavaş olacak ve yer kaplayacak) ve yakında S0 ve S1 alanlarını dolduracak, bu yüzden doğrudan eski nesle geçin.
    • Nesnenin yaşlılığa yükseltileceği bir durum da var, yani S0 (veya S1) alanındaki aynı yaştaki nesnelerin boyutlarının toplamı S0 (veya S1) alanının yarısından büyük olduğunda, yaşı o yaştan büyük veya ona eşit olan nesneler de terfi ettirilecek. Yaşlılığa.

    3. Alan tahsisi garantisi

    MinorGC'nin ortaya çıkmasından önce, sanal makine ilk olarak eski nesildeki mevcut en büyük sürekli alanın yeni nesildeki tüm nesnelerin toplam alanından daha büyük olup olmadığını kontrol eder. Daha büyükse, Minor GC güvenli olduğundan emin olabilir. Şundan büyük değilse sanal makine HandlePromotionFailure ayar değerinin olup olmadığını kontrol eder. Garantinin başarısız olmasına izin verin. İzin verilirse, eski nesildeki maksimum kullanılabilir sürekli alanın eski nesle yükseltilen nesnelerin ortalama boyutundan daha büyük olup olmadığını kontrol etmeye devam edecektir, eğer bundan daha büyükse, bir Minor GC gerçekleştirilecektir, aksi takdirde bir Full GC gerçekleştirilebilir.

    4. Dünyayı Durdurun

    Eski nesil doluysa, Tam GC tetiklenecektir Tam GC, hem genç nesli hem de eski nesli aynı anda geri kazanacaktır (yani, GC tüm yığın), bu da Dünyayı Durdur'a (STW olarak kısaltılmıştır) ve çok fazla performans yüküne neden olacaktır.

    STW nedir? Sözde STW, yani GC sırasında (küçük GC veya Tam GC), yalnızca çöp toplayıcı iş parçacığı çalışıyor ve diğer çalışan iş parçacıkları askıya alındı.

    Seslendirme: Çö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?

    Genel olarak, Tam GC, çalışan iş parçacığının çok uzun süre duraklamasına neden olur (çünkü Tam GC, tüm yığındaki kullanılamayan nesneleri temizler, bu genellikle uzun sürer), sunucu çok fazla istek alırsa, hizmet reddedilir! Bu nedenle, Tam GC'yi en aza indirmeliyiz (Küçük GC de STW'ye neden olur, ancak sadece hafif bir STW tetiklenecektir, çünkü Eden alanındaki nesnelerin çoğu geri dönüştürülmüştür ve yalnızca birkaç hayatta kalan nesne çoğaltma algoritması aracılığıyla S0 veya S1 alanına aktarılacaktır. , Yani nispeten iyi).

    Şimdi anlamalıyız ki, genç nesli Eden, S0, S1 alanına ayarlamanın veya nesne için yaş eşiğini belirlemenin veya genç neslin ve eski neslin alan boyutunu varsayılan olarak 1: 2'ye ayarlamanın, nesnenin eskiye erken girmesini önlemek olduğunu anlamalıyız. Tam GC, olabildiğince geç tetiklenir. Sadece yeni nesilde Eden'i ayarlarsanız ne olacağını düşünün.Sonuç, her Minor GC'den sonra, hayatta kalan nesnelerin eski nesle erken girmesi, daha sonra eski nesil hızla dolması ve Full GC'nin yakında tetiklenmesidir, ancak nesne aslında İki veya üç Küçük GC'den sonra çoğu ölecek, bu nedenle S0 ve S1 tamponlarında sadece birkaç nesne yaşlılığa girecek ve yaşlılığın boyutu o kadar hızlı büyümeyecek, bu da erken doğmayı engelliyor Tam GC'yi tetikleyin.

    Tam GC (veya Minor GC) performansı etkileyeceğinden, GC'yi uygun bir zamanda başlatmalıyız. Bu zamandaki bu noktaya Güvenli Nokta denir. Zaman içindeki bu noktanın seçimi, GC süresini çok uzun yapmak için çok küçük olmamalıdır. Program uzun süre takılı kalırsa, çalışma sırasında yükü çok fazla artırmak için çok sık olmamalıdır. Genel olarak, GC Kök bilgisinin belirlenmesi vb. Gibi zamandaki bu noktada iş parçacığının durumu, JVM'nin GC'yi güvenli bir şekilde başlatabilmesi için belirlenebilir. Güvenli Nokta, esas olarak aşağıdaki belirli konumları ifade eder:

    • Döngünün sonu
    • Yöntem dönmeden önce
    • Yöntemin çağrılmasından sonra
    • İstisnanın atıldığı yer aynı zamanda genç neslin özelliklerinden de kaynaklanmaktadır (çoğu nesne Minor GC'den sonra ölecektir) Minor GC bir kopyalama algoritması kullanır, ancak eski nesilde daha fazla nesne vardır ve daha fazla yer kaplar. Kopyalama algoritmasının kullanımının büyük bir ek yükü olacaktır (kopya algoritmasının, nesne hayatta kalma oranı yüksek olduğunda birden fazla kopyalama işlemi gerçekleştirmesi gerekir ve aynı zamanda alanın yarısını boşa harcaması gerekir) Bu nedenle, eski neslin özelliklerine göre, eski nesilde gerçekleştirilen GC genellikle etiketleme yöntemini kullanır. Geri dönüşüm için.

    Çöp toplayıcı türleri

    Toplama algoritması bellek kurtarma metodolojisiyse, çöp toplayıcı, bellek kurtarmanın spesifik uygulamasıdır. Java sanal makine spesifikasyonu, çöp toplayıcının nasıl uygulanması gerektiğini belirtmez, bu nedenle genel olarak, farklı satıcılar tarafından sağlanan çöp toplayıcının uygulanması ve sanal makinenin farklı versiyonları farklı olabilir.Genel olarak, kullanıcıların uygulamanın özelliklerine göre ayarlamalarına izin verecek parametreler verilir. Çeşitli nesillerde kullanılan toplayıcıları birleştiren başlıca şu çöp toplayıcıları vardır.

    • Yeni nesilde çalışan çöp toplayıcılar: Serial, ParNew, ParallelScavenge
    • Yaşlılıkta çalışan çöp toplayıcıları: CMS, Seri Eski, Paralel Eski
    • Hem yeni hem de eski nesillerde çalışan çöp toplayıcı: G1

    Resimdeki çöp toplayıcının bir bağlantısı varsa, birlikte kullanılabilecekleri anlamına gelir.Şimdi, her bir çöp toplayıcının belirli işlevlerine bir göz atalım.

    Senozoik toplayıcı

    Seri toplayıcı

    Seri toplayıcı yeni nesilde çalışır.Tek iş parçacıklı bir çöp toplayıcıdır.Tek iş parçacıklı, çöp toplamayı tamamlamak için yalnızca bir CPU veya bir toplama iş parçacığı kullanacağı anlamına gelir.Sadece bu değil, yukarıda bahsedilen STW'yi hatırlıyor musunuz? , Çöp toplama işlemi gerçekleştirirken, diğer kullanıcı iş parçacıkları, çöp toplama bitene kadar askıya alınacaktır, yani, GC sırasında, uygulama şu anda kullanılamaz.

    Görünüşe göre tek iş parçacıklı çöp toplayıcı çok pratik değil, ancak bilmemiz gereken herhangi bir teknolojinin kullanımı sahneden ayrılamaz. İstemci modunda, basit ve etkilidir (diğer toplayıcıların tek iş parçacıklı toplayıcıları ile karşılaştırıldığında) ve tek bir CPU'yu sınırlayan ortama uygundur. Diğer bir deyişle, Seri tek iş parçacıklı modun diğer iş parçacıklarıyla etkileşime girmesine gerek yoktur, bu da ek yükü azaltır.GC üzerinde yoğunlaşma, tek iş parçacıklı avantajlarını en üst düzeye çıkarabilir.Ayrıca, kullanıcı masaüstü uygulama senaryolarında, sanal makinelere ayrılan bellek genellikle çok büyük değildir. , Onlarca veya hatta bir veya iki yüz megabayt toplayın (yalnızca yeni nesil bellek, masaüstü uygulamaları temelde daha büyük olmayacaktır), STW süresi 100 milisaniyeden fazla bir süre içinde kontrol edilebilir, sık sık gerçekleşmediği sürece, bu duraklama kabul edilebilir Dolayısıyla, İstemci modunda çalışan sanal makineler için, Seri toplayıcı yeni nesil için varsayılan toplayıcıdır.

    ParNew toplayıcı

    ParNew toplayıcı, Seri toplayıcının çok iş parçacıklı bir sürümüdür.Çoklu iş parçacığı kullanmaya ek olarak, diğer toplama algoritmaları, STW, nesne tahsis kuralları ve geri dönüşüm stratejileri, Seri toplayıcınınkilerle aynıdır. Alt seviyede, bu iki toplayıcı da aynı şeyi paylaşır Daha fazla kod, çöp toplama süreci aşağıdaki gibidir

    ParNew esas olarak Sunucu modunda çalışır.Sunucu daha fazla istek alırsa yanıt süresinin çok önemli olduğunu biliyoruz.Multithreading, çöp toplamayı daha hızlı hale getirebilir, bu da STW süresini azalttığı ve yanıt süresini iyileştirdiği anlamına gelir. Yeni nesil toplayıcı, Sunucu modunda çalışan sanal makineler için ilk tercihtir.Performansla ilgisi olmayan bir başka neden de Seri toplayıcı dışında yalnızca CMS toplayıcıyla çalışabilmesidir.CMS, çığır açan bir çöp toplayıcıdır. Eşzamanlı toplayıcı anlamında, çöp toplama iş parçacığının ve kullanıcı iş parçacığının (temelde) aynı anda çalıştığını fark eder, geleneksel GC toplayıcı kod çerçevesini kullanır ve Serial, ParNew bir kod çerçevesini paylaşır, böylece Bu ikisiyle birlikte çalışın ve daha sonra bahsedilen Parallel Scavenge ve G1 toplayıcıları geleneksel GC toplayıcı kodu çerçevesini kullanmaz, ancak bağımsız olarak uygulanır. Diğer toplayıcılar çerçeve kodunun yalnızca bir bölümünü paylaşır, bu nedenle CMS toplayıcı ile çalışamaz.

    Birden fazla CPU olması durumunda, ParNew'in çok iş parçacıklı geri dönüşüm özelliği nedeniyle, çöp toplamanın daha hızlı olacağına ve ayrıca STW süresini etkili bir şekilde azaltacağına ve uygulama yanıt hızını artırabileceğine şüphe yoktur.

    Paralel Temizleme toplayıcı

    Parallel Scavenge toplayıcı aynı zamanda bir çoğaltma algoritması kullanan, çok iş parçacıklı ve yeni nesilde çalışan bir çöp toplayıcıdır.ParNew toplayıcı ile aynı işleve sahip gibi görünüyor.Bununla ilgili özel bir şey var mı?

    Odak farklıdır. CMS gibi çöp toplayıcılar, çöp toplama sırasında kullanıcı iş parçacıklarının duraklatma süresini olabildiğince kısaltmaya odaklanırken, Parallel Scavenge'ın amacı kontrol edilebilir bir verim elde etmektir (verim = çalıştırma kullanıcı kodu süresi / (kullanıcı kodu süresini çalıştırma) + Çöp toplama süresi)), yani CMS gibi çöp toplayıcıların kullanıcılarla etkileşime giren programlar için daha uygun olduğu anlamına gelir, çünkü duraklama süresi ne kadar kısaysa kullanıcı deneyimi o kadar iyi olurken, Parallel Scavenge toplayıcı verime odaklanır, bu yüzden daha uygundur Çok fazla kullanıcı etkileşimi gerektirmeyen arka plan hesaplamaları ve diğer görevleri yapın.

    Parallel Scavenge toplayıcı, verimi doğru bir şekilde kontrol etmek için iki parametre sağlar. Bunlar, maksimum çöp toplama süresini kontrol eden -XX: MaxGCPauseMillis parametresi ve doğrudan işleme boyutunu ayarlayan -XX: GCTimeRatio'dur (varsayılan% 99)

    Yukarıdaki iki parametreye ek olarak, Parallel Scavenge toplayıcı tarafından sağlanan üçüncü parametre -XX: UseAdaptiveSizePolicy'yi de kullanabilirsiniz.Bu parametreyi açtıktan sonra, genç neslin boyutunu, Eden'in Survivor oranını (SurvivorRatio) ve diğer ayrıntıları manuel olarak belirtmenize gerek yoktur. Temel yığın boyutunu (maksimum yığını ayarlamak için -Xmx) ve maksimum çöp toplama süresini ve verimini ayarlayın, sanal makine mevcut sistem işlemine göre izleme bilgilerini toplayacak ve kümemize mümkün olduğunca ulaşmak için bu parametreleri dinamik olarak ayarlayacaktır. Maksimum çöp toplama süresi veya işlem hacmi iki göstergedir. Uyarlanabilir strateji, Parallel Scavenge ve ParNew! Arasında önemli bir farktır.

    Yaşlılık koleksiyoncusu

    Seri Eski koleksiyoncu

    Yukarıda bildiğimiz gibi, Seri toplayıcı, yeni nesilde çalışan tek iş parçacıklı bir toplayıcıdır.Ancak Serial Old, eski nesilde çalışan tek iş parçacıklı bir toplayıcıdır.Bu koleksiyonerin temel önemi sanal Sunucu modunda kullanılıyorsa, iki ana kullanımı vardır: biri, JDK 1.5 ve önceki sürümlerde Parallel Scavenge ile birlikte kullanılacak, diğeri ise CMS toplayıcı için eşzamanlı toplama sırasında oluşan bir yedekleme planı olarak kullanılacak. Eşzamanlı Mod Arızası olduğunda kullanılır (daha sonra açıklanacaktır), aşağıdaki gibi Seri toplayıcı ile birlikte kullanılır

    Paralel Eski toplayıcı

    Parallel Old, Parallel Scavenge toplayıcısının eski sürümüne göredir. Çoklu okuma ve etiket sıralaması kullanır. İkisinin kombinasyonunun şematik diyagramı aşağıdaki gibidir. İkisinin kombinasyonu, gerçekten "önce iş hacmine" ulaşan çok iş parçacıklı bir toplayıcıdır Amaçları

    CMS toplayıcı

    CMS toplayıcı, en kısa STW süresine ulaşmayı hedefleyen bir toplayıcıdır.Uygulama, hizmetin yanıt hızına büyük önem veriyorsa ve kullanıcılara en iyi deneyimi yaşatmak istiyorsa, CMS toplayıcı çok iyi bir seçimdir!

    Daha önce yaşlılığın esas olarak işaretleme yöntemini kullandığını ve CMS'nin yaşlılıkta çalışmasına rağmen, esas olarak aşağıdaki dört adımı olan işaret kaldırma yöntemini kullandığını söylemiştik.

  • İlk işaret
  • Eşzamanlı işaret
  • Açıklama
  • Eşzamanlı temizleme
  • STW'nin ilk işaretleme ve yeniden işaretleme aşamalarında meydana geleceği ve kullanıcı iş parçacığının askıda kalmasına neden olacağı şekilden görülebilir, ancak ilk işaretleme yalnızca GC Kökleri ile ilişkilendirilebilen nesneleri işaretler ki bu çok hızlıdır Eşzamanlı işaretleme, GC Kök İzleme sürecidir. Yeniden işaretleme, eşzamanlı işaretleme sırasında kullanıcı ipliğinin devam eden işlemine bağlı olarak işaretlemesi değiştirilen nesnenin parçasının işaretleme kaydını düzeltmektir Bu aşamadaki duraklama süresi genellikle başlangıç işaretleme aşamasından biraz daha uzun, ancak eşzamanlı işaretleme süresinden çok daha kısadır.

    Tüm süreçte en uzun zaman alan süreç eş zamanlı işaretleme ve işaretleme temizliğidir.Ancak, kullanıcı iş parçacıkları bu iki aşamada çalışabilir, bu nedenle uygulamanın normal kullanımını etkilemez. Bu nedenle, genel olarak CMS toplayıcısının bellek kurtarma işlemi şu şekilde değerlendirilebilir: Kullanıcı konuları eşzamanlı olarak yürütülür.

    Bununla birlikte, CMS toplayıcı mükemmel olmaktan uzaktır ve aşağıdaki üç dezavantaja sahiptir.

    • CMS toplayıcısının CPU kaynaklarına karşı çok hassas olmasının nedeni de anlaşılabilir. Örneğin, istekleri işlemek için başlangıçta 10 kullanıcı iş parçacığım vardı, ancak şimdi 3'ü geri dönüşüm iş parçacığı olarak ayırmam gerekiyor. Verim% 30 düştü. Varsayılan olarak CMS tarafından başlatılan geri dönüşüm iş parçacığı Sayı (CPU sayısı + 3) / 4'tür, CPU sayısı yalnızca bir veya iki ise, işlem hacmi doğrudan% 50 düşer ve bu açıkça kabul edilemez
    • CMS yüzen çöpü (Yüzen Çöp) işleyemez ve "Eşzamanlı Mod Hatası" meydana gelebilir, bu da başka bir Tam GC'nin üretilmesine neden olabilir.Kullanıcı iş parçacığı eşzamanlı temizleme aşamasında hala çalıştığından, temizlik sırasında sürekli olarak yeni çöp görünür. Çöpün bir kısmı yalnızca bir sonraki GC'de (yani, bulut çöpü) temizlenebilir.Aynı zamanda, kullanıcı iş parçacığı, çöp toplama aşamasında çalışmaya devam etmelidir.Kullanıcı iş parçacığının normal şekilde yürütülmesini sağlamak için yeterli alan ayırmak gerekir, bu da CMS toplama anlamına gelir Diğer toplayıcılar gibi, yaşlılık da yaşlılık dolana kadar kullanılamaz. Yaşlılık alanın% 68'ini kullandığında JDK 1.5 varsayılan olarak etkinleştirilecektir. Elbette bu oran -XX: CMSInitiatingOccupancyFraction ile ayarlanabilir, ancak ayar çok yüksekse CMS'nin çalışması sırasında ayrılan hafızanın programın gereksinimlerini karşılamaması ve Eşzamanlı Mod Arızasının başarısız olmasına neden olması kolaydır.Şu anda, Seri Eski toplayıcı eski nesli yeniden toplamak için etkinleştirilecektir ve Seri Eski toplayıcının tek iş parçacıklı bir koleksiyon olduğunu biliyoruz. Bu, STW'nin daha uzun olmasına neden olacaktır.
    • CMS, işaret süpürme yöntemini kullanır. Yukarıda bahsettiğimiz gibi, bu yöntem büyük miktarda bellek parçalanması oluşturacak ve bu da büyük bellek tahsisinde çok fazla soruna neden olacaktır.Nesneleri ayırmak için yeterince büyük bir sürekli alan bulamazsanız, Uygulamanın performansını etkileyecek olan Tam GC'yi tetikleyecektir. Elbette, CMS toplayıcı FullGC'ye dayanamadığında bellek parçalanmasının birleştirme ve birleştirme sürecini başlatmak için kullanılan -XX: + UseCMSCompactAtFullCollection'ı (varsayılan olarak etkindir) açabiliriz. Bellek birleştirme STW'ye neden olur ve duraklama süresi uzar. Kaç tane tam GC'nin sıkıştırılmadan ve ardından sıkıştırmalı olarak yürütüleceğini ayarlamak için başka bir -XX: CMSFullGCsBeforeCompation parametresini kullanabilirsiniz.
    G1 (Önce Çöp) toplayıcı

    G1 toplayıcı, sunucu odaklı bir çöp toplayıcıdır.Her şeyi kontrol eden çöp toplayıcı olarak adlandırılır.Aşağıdaki özelliklere sahiptir.

    • CMS toplayıcı gibi, uygulama iş parçacıklarıyla aynı anda çalışabilir.
    • Boş alanı daha hızlı düzenleyin.
    • GC duraklatma süresinin daha iyi tahmin edilmesi gerekiyor.
    • CMS gibi çok fazla verim performansından ödün vermez.
    • Daha büyük bir Java Yığınına gerek yok

    CMS ile karşılaştırıldığında, aşağıdaki iki açıdan daha iyi performans gösterir

  • İşlem sırasında hiçbir bellek parçası oluşturulmayacaktır.G1 bir bütün olarak işaret-birleştirme yöntemini benimser ve kısmi (iki bölge) çoğaltma algoritmasına göre uygulanır.Her iki algoritma da bellek parçaları oluşturmaz ve toplama sonrasında düzenlilik sağlar. Kullanılabilir bellek, programın uzun süreli çalışmasına elverişlidir.
  • STW'de öngörülebilir bir duraklama süresi modeli oluşturulmuştur.Kullanıcılar istenen duraklama süresini belirleyebilir ve G1, kullanıcı tarafından ayarlanan duraklatma süresi içinde duraklatma süresini kontrol edecektir.
  • G1 neden öngörülebilir bir duraklama modeli oluşturabilir? Bunun ana nedeni, G1'in yığın alanı tahsisinin geleneksel çöp toplayıcıdan farklı olmasıdır.Geleneksel bellek tahsisi süreklidir, yeni nesillere ve eski nesillere bölünmüştür. Çağda, yeni nesil aşağıdaki gibi Eden, S0, S1'e ayrılmıştır.

    Her bir G1 neslinin depolama adresleri sürekli değildir. Her nesil, aynı boyutta n ayrı bölge kullanır ve her bölge, şekilde gösterildiği gibi sürekli bir sanal bellek adresini işgal eder

    Geleneksel yeni ve eski nesiller ile hayatta kalma alanı arasındaki mekansal farka ek olarak, Bölgede Humongous anlamına gelen ek bir H vardır, bu da bu Bölgelerin büyük nesneler (H-obj) depoladığı anlamına gelir, yani boyut bölgenin yarısından büyük veya yarısına eşittir. G1

    Full GC Region G1 Region Region, STW Region, STW

    G1

  • CMS Region GC

    sonuç olarak

    Client Serial + Serial Old ParNew + CMS G1 JVM

    demoOOM

    referans:

    https://www.jianshu.com/p/35cf0f348275

    Java--jni https://blog.csdn.net/w1992wishes/article/details/80283403

    Java https://mp.weixin.qq.com/s/pR7U1OTwsNSg5fRyWafucA

    Java

    Java Hotspot G1 GC https://tech.meituan.com/2016/09/23/g1.html

    Yerleşik sistemlerde makine öğrenimi neden başarısız oluyor?
    önceki
    Uzun mesafeli toplantı her zaman takılıp kalıyor? 8 "Xiaobai" yöntemi bir bakışta görülebilir
    Sonraki
    Aynı anda çeşitli veritabanları için SQL yürütme planlarını edinin | Kuvvet Projesi
    BlackBerry telefonları durdurulacak; üç büyük operatör: Ücret borcu olan kullanıcılar salgın önleme ve kontrol döneminde durmayacak; Chrome testi, arama sonuçları sayfası URL'lerini kaldırıyor | Geek
    Bir tren istasyonu özel polisi tarafından Fare Yılı Bahar Şenliği: "Daha az insan olmasına rağmen, daha çok endişeleniyorum"
    Önümüzdeki yılın Bahar Şenliği için ayrılmış 5.000 karakterlik elektrikli otomobil kış hileleri
    BYD 5 yıl "genişleme", Han EV / DM veya 256.900 satış harcıyor
    Bunları iyi yapmak salgın riskini en aza indirebilir
    Yasal sistem en üst sırada yer alır ve Takata Hava Yastığı Ocak geri çağırma listesinde "yok"
    Skoda'nın yeni Octavia ile ilgili beş soru, değişimden sonra gücü nasıl artacak?
    İlkbaharın ilk gününde tüm bitkiler tomurcuklanır.
    38 inç kavisli ekran "büyük" ve yeni Escalade sürprizleri bundan çok daha fazlası
    Wuhan, dört tür yeni koroner pnömoni hastasını toplamaya ve tedavi etmeye başladı
    Otomobil hisselerinde 72 saatlik iniş ve çıkışlar: Neden en çok kaybedenler ve kazananlar TA?
    To Top