Belleği Gmail ölçeğinde etkili şekilde yönetme

John McCutchan
John McCutchan
Loreena Lee
Loreena Lee

Giriş

JavaScript, otomatik bellek yönetimi için atık toplamayı kullansa da uygulamalardaki etkili bellek yönetiminin yerine geçmez. JavaScript uygulamaları, bellek sızıntıları ve şişkinlik gibi bellekle ilgili yerel uygulamalarla aynı sorunları yaşarlar, ancak atık toplama duraklamalarıyla da ilgilenmeleri gerekir. Gmail gibi büyük ölçekli uygulamalarda, daha küçük uygulamalarda da aynı sorunlar yaşanır. Gmail ekibinin, bellek sorunlarını tanımlamak, ayırmak ve düzeltmek amacıyla Chrome Geliştirici Araçları'nı nasıl kullandığını öğrenmek için okumaya devam edin.

Google I/O 2013 Oturumu

Bu materyali Google I/O 2013'te sunduk. Aşağıdaki videoyu izleyin:

Gmail, bir sorunumuz var...

Gmail ekibi ciddi bir sorunla karşılaşıyordu. Kaynak açısından kısıtlı dizüstü ve masaüstü bilgisayarlarda birden fazla gigabayt bellek tüketen Gmail sekmelerine ilişkin anekdotlar giderek daha sık duyuluyor ve genellikle tarayıcının tamamının devre dışı bırakılmasıyla sonuçlanıyordu. %100'de sabitlenen CPU'lar, yanıt vermeyen uygulamalar ve Chrome'daki üzgün sekmeleri ("Bu Artık İflah Olmaz") ile ilgili hikayeler. Ekip, düzeltmek bir yana, sorunu teşhis etmeye nasıl başlayacağı konusunda bile bir şey kaybediyordu. Sorunun ne kadar yaygın olduğunu bilmiyorlardı ve mevcut araçların ölçeği büyük uygulamalara ulaşamıyordu. Chrome ekipleriyle güçlerini birleştiren ekip, birlikte bellek sorunlarını öncelik sırasına koymak için yeni teknikler geliştirdi, mevcut araçları iyileştirdi ve sahadan bellek verilerinin toplanmasını sağladı. Ancak araçlara geçmeden önce JavaScript bellek yönetiminin temellerine değinelim.

Bellek Yönetimiyle İlgili Temel Bilgiler

JavaScript'te belleği etkili bir şekilde yönetebilmeniz için temel bilgileri anlamanız gerekir. Bu bölümde temel türler ile nesne grafiği ele alınıp genel olarak bellek şişmesi ve JavaScript'teki bellek sızıntısı tanımlarını görebilirsiniz. JavaScript'te bellek grafik olarak kavramlandırılabilir. Bu nedenle Grafik teorisi, JavaScript bellek yönetiminde ve Yığın Profil Aracı'nda önemli bir rol oynar.

Temel Türler

JavaScript'in üç temel türü vardır:

  1. Sayı (ör. 4, 3.14159)
  2. Boole (doğru veya yanlış)
  3. Dize ("Hello World")

Bu temel öğe türleri, başka hiçbir değere başvuramaz. Nesne grafiğinde bu değerler her zaman yaprak veya sonlandırma düğümleridir, yani hiçbir zaman dışarı giden bir kenara sahip olmazlar.

Tek bir kapsayıcı türü vardır: Nesne. JavaScript'te Nesne bir ilişkisel dizidir. Boş olmayan bir nesne, kenarları diğer değerlere (düğümlere) giden bir iç düğümdür.

Diziler Hakkında

JavaScript'te bir Dizi aslında sayısal tuşlara sahip bir nesnedir. JavaScript çalışma zamanları Dizi Benzer Nesneleri optimize edeceği ve bunları arka planda dizi olarak temsil edeceği için bu basit bir işlemdir.

Terminoloji

  1. Değer - Temel tür, Nesne, Dizi vb.
  2. Değişken - Bir değere başvuran bir ad.
  3. Özellik - Bir Nesnede, bir değere başvuran bir ad.

Nesne Grafiği

JavaScript'teki tüm değerler nesne grafiğinin bir parçasıdır. Grafik, köklerle (ör. window nesnesi) başlar. GC köklerinin ömrünü yönetmek sizin kontrolünüzde değildir çünkü bunlar tarayıcı tarafından oluşturulur ve sayfa kaldırıldığında silinir. Genel değişkenler aslında pencerede bulunan özelliklerdir.

Nesne grafiği

Bir Değer Ne Zaman Çöpe Dönüştürülür?

Bir değer, kökten değere giden bir yol olmadığında değer gereksiz hale gelir. Diğer bir deyişle, köklerden başlayıp yığın çerçevesinde canlı olan tüm nesne özellikleri ve değişkenleri kapsamlı olarak arandığında bir değere ulaşılamaz ve değer çöpe dönüşür.

Çöp grafiği

JavaScript'te Bellek Sızıntısı nedir?

JavaScript'te bellek sızıntısı, genellikle sayfanın DOM ağacından erişilemeyen ancak yine de bir JavaScript nesnesi tarafından referans verilen DOM düğümleri olduğunda ortaya çıkar. Modern tarayıcılar farkında olmadan sızıntı oluşturmayı gittikçe daha zor hale getirse de, bu durum hâlâ sanıldığından daha kolay. DOM ağacına şu şekilde bir öğe eklediğinizi varsayalım:

email.message = document.createElement("div");
displayList.appendChild(email.message);

Daha sonra öğeyi görüntüleme listesinden kaldırırsınız:

displayList.removeAllChildren();

email mevcut olduğu sürece, iletide referans verilen DOM öğesi artık sayfanın DOM ağacından ayrılmış olsa bile kaldırılmaz.

Bloat nedir?

Optimum sayfa hızı için gerekenden daha fazla bellek kullandığınızda sayfanız şişirilir. Bellek sızıntıları dolaylı olarak şişmeye de neden olur ancak bu tasarımdan kaynaklanmaz. Herhangi bir boyut sınırlaması olmayan uygulama önbelleği, yaygın bir bellek şişmesi kaynağıdır. Ayrıca sayfanız, resimlerden yüklenen piksel verileri gibi ana makine verileri nedeniyle şişirilebilir.

Çöp Toplama nedir?

Atık toplama işlemi, JavaScript'te belleğin geri kazanılma yöntemidir. Bunun ne zaman olacağına tarayıcı karar verir. Toplama sırasında sayfanızdaki tüm komut dosyası yürütmeleri askıya alınır. Canlı değerler ise GC köklerinden başlayan nesne grafiği geçişiyle keşfedilir. Ulaşılabilir olmayan tüm değerler çöp olarak sınıflandırılır. Gereksiz değerler için bellek, bellek yöneticisi tarafından geri alınır.

V8 Ayrıntılı Çöp Toplayıcı

Atık toplama işleminin nasıl gerçekleştiğini daha iyi anlamak için V8 çöp toplayıcıyı ayrıntılı olarak inceleyelim. V8, kuşaklardan oluşan bir toplayıcı kullanır. Hafıza iki nesle ayrılır: genç ve yaşlı. Genç nesilde ayırma ve toplama işlemleri hızlı ve sık gerçekleştirilir. Eski nesilde ayırma ve toplama işlemleri daha yavaştır ve daha az sıklıkta gerçekleşir.

Nesil Toplayıcı

V8, iki nesilli bir toplayıcı kullanır. Bir değerin yaşı, ayrıldığından beri ayrılan bayt sayısı olarak tanımlanır. Pratikte, bir değerin yaşı genellikle hayatta kaldığı genç nesil koleksiyonların sayısına göre tahmin edilir. Bir değer, yeterince eski olduğunda eski nesil olarak kabul edilir.

Pratikte, yeni ayrılmış değerler uzun süre dayanmaz. Smalltalk programlarıyla ilgili bir çalışma, değerlerin yalnızca% 7'sinin genç kuşak bir koleksiyondan sonra hayatta kalabildiğini gösterdi. Çalışma zamanlarıyla ilgili benzer çalışmalarda, yeni tahsis edilen değerlerin ortalama% 90 ila% 70'inin hiçbir zaman eski nesle alınmadığı ortaya çıktı.

Genç Nesil

V8'deki genç nesil yığın, adlandırılmış ve arası adlı iki alana bölünür. Bellek, alandan alana ayrılır. Ayırma işlemi çok hızlıdır. Ayrılan alan dolana kadar genç nesil koleksiyon tetiklenir. Genç nesil koleksiyonlar önce bir uzaydan diğerine, eskiden alana (artık ise uzaydan) taranır ve tüm canlı değerler alana kopyalanır ya da eski nesle korunur. Tipik bir genç nesil koleksiyonda bu süre 10 milisaniye (ms) olarak değişir.

Sezgisel olarak, uygulamanızın yaptığı her tahsisin, alanı tüketmeyi ve GC'nin duraklatılmasına neden olacağını bilmeniz gerekir. Oyun geliştiricilerinin şunu göz önünde bulundurması: 16 ms'lik kare süresinden (saniyede 60 kareye ulaşmak için gereklidir) emin olmak için uygulamanızın sıfır ayırma yapması gerekir çünkü tek bir genç nesil koleksiyon kare süresinin çoğunu kaplar.

Genç nesil yığını

Eski Nesil

V8'deki eski nesil yığın, toplama için bir kompakt işaretleme algoritması kullanır. Eski nesil tahsisler, bir değer genç nesilden eski nesle devredilen her durumda ortaya çıkar. Eski nesil bir koleksiyon gerçekleştiğinde genç nesil koleksiyon da oluşturulur. Uygulamanız, saniye sırasına göre duraklatılacak. Eski nesil koleksiyonlar seyrek görüldüğünden, pratikte bu kabul edilebilir bir durumdur.

V8 GC Özeti

Atık toplama özelliğiyle otomatik bellek yönetimi, geliştirici üretkenliği için mükemmeldir ancak her değer atadığınızda çöp toplama işlemini duraklama aşamasına daha da yaklaşırsınız. Çöp toplama durakları, olumsuzluk yaratarak uygulamanızın verdiği hissi zedeleyebilir. Artık JavaScript'in belleği nasıl yönettiğini anladığınıza göre, uygulamanız için doğru seçimleri yapabilirsiniz.

Gmail'i düzeltme

Geçtiğimiz yıl, çok sayıda özellik ve hata düzeltmesi sayesinde Chrome Geliştirici Araçları'nı her zamankinden daha güçlü hale getirdiler. Ayrıca tarayıcının kendisi de Performance.memory API'da önemli bir değişiklik yaparak Gmail ve diğer uygulamaların sahadan bellek istatistikleri toplamasını mümkün hale getirmiştir. Bir zamanlar imkansız gibi görünen bu muhteşem araçlarla donanmış bu görev, kısa süre içinde suçluları bulmayla ilgili heyecan verici bir oyuna dönüştü.

Araçlar ve Teknikler

Alan Verileri ve performans.bellek API'si

Chrome 22 sürümünden itibaren performance.memory API varsayılan olarak etkindir. Gmail gibi uzun süredir devam eden uygulamalar için gerçek kullanıcıların verileri çok değerlidir. Bu bilgiler, Gmail'de günde 8-16 saat geçiren ve günde yüzlerce ileti alan deneyimli kullanıcıları, Gmail'de günde birkaç dakika geçiren ve haftada onlarca ileti alan ortalama kullanıcılardan ayırt etmemizi sağlar.

Bu API üç veri parçası döndürür:

  1. jsHeapSizeLimit - JavaScript yığınının sınırlandırıldığı bellek miktarı (bayt cinsinden).
  2. totalJSHeapSize - JavaScript yığınının boş alan da dahil olmak üzere ayırdığı bellek miktarı (bayt cinsinden).
  3. usedJSHeapSize - Şu anda kullanılan bellek miktarı (bayt cinsinden).

Unutulmaması gereken önemli bir nokta, API'nın tüm Chrome işlemi için bellek değerleri döndürdüğüdür. Varsayılan mod olmasa da, Chrome belirli durumlarda aynı oluşturucu işleminde birden fazla sekme açabilir. Bu nedenle, performans.memory tarafından döndürülen değerlerin, uygulamanızı içeren sekmelerin yanı sıra diğer tarayıcı sekmelerinin de bellek ayak izlerini içerebileceği anlamına gelir.

Belleği Geniş Ölçekte Ölçme

Gmail, JavaScript'lerini yaklaşık 30 dakikada bir bellek bilgilerini toplamak için performans.memory API'sini kullanacak şekilde ayarlamıştır. Birçok Gmail kullanıcısı tek seferde günlerce uygulamayı kullanmayı bıraktığından ekip, zaman içindeki bellek artışının yanı sıra genel bellek ayak izi istatistiklerini de izleyebildi. Rastgele bir kullanıcı örneklemiyle bellek bilgisi toplamak için Gmail'in kullanılmasını izleyen birkaç gün içinde ekip, bellek sorunlarının ortalama kullanıcılar arasında ne kadar yaygın olduğunu anlamaya yetecek kadar veriye sahip oldu. Ekip bir temel belirledi ve bellek tüketimini azaltma hedefine doğru ilerleme durumunu izlemek için gelen veri akışını kullandı. Zamanla bu veriler, herhangi bir bellek regresyonunu yakalamak için de kullanılır.

Alan ölçümleri, izleme amaçlarının ötesinde, bellek ayak izi ve uygulama performansı arasındaki ilişki hakkında da isabetli bilgiler sağlar. "Daha fazla bellek sayesinde daha iyi performans elde edildiğine dair yaygın inanışın aksine, Gmail ekibi, bellek ayak izi ne kadar büyükse genel Gmail işlemleri için daha uzun gecikmeler yaşandığını fark etti. Bu yeni duyguyla hareketlenen ikili, hafızalarını dinlendirme konusunda hiç olmadığı kadar motive oldu.

Belleği Geniş Ölçekte Ölçme

DevTools Zaman Çizelgesi ile Bellek Sorununu Tanımlama

Herhangi bir performans sorununu çözmenin ilk adımı, sorunun var olduğunu kanıtlamak, tekrarlanabilir bir test oluşturmak ve problemin temel ölçümünü yapmaktır. Yeniden oluşturulabilir bir program olmadan sorunu güvenilir bir şekilde ölçemezsiniz. Temel bir ölçüm olmadan performansı ne kadar iyileştirdiğinizi bilemezsiniz.

DevTools Zaman Çizelgesi paneli, sorunun var olduğunu kanıtlamak için ideal bir adaydır. Web uygulaması veya sayfanız yüklenirken ve bu sayfayla etkileşimde bulunurken zamanın nerede harcandığına dair kapsamlı bir genel bakış sunar. Kaynakların yüklenmesinden JavaScript'in ayrıştırılmasına, stillerin hesaplanmasına, atık toplama duraklamalarına ve yeniden boyamaya kadar tüm etkinlikler bir zaman çizelgesinde gösterilir. Zaman Çizelgesi panelinde, bellek sorunlarının araştırılması amacıyla, ayrılan toplam belleği, DOM düğümü sayısını, pencere nesnelerinin sayısını ve ayrılan etkinlik işleyici sayısını izleyen bir Bellek modu da bulunur.

Bir sorunun mevcut olduğunu kanıtlama

Bellek sızıntısı yaptığından şüphelendiğiniz bir dizi işlemi tanımlayarak başlayın. Zaman çizelgesini kaydetmeye başlayın ve işlem sırasını gerçekleştirin. Çöplerin tamamen toplanmaya başlaması için alt kısımdaki çöp kutusu düğmesini kullanın. Birkaç yinelemeden sonra testere dişi şeklinde bir grafik görürseniz kısa ömürlü birçok nesneyi ayırıyorsunuz demektir. Ancak işlem sırasının herhangi bir bellekte tutulmasına neden olması beklenmiyorsa ve DOM düğüm sayısı başladığınız referans değere inmiyorsa bir sızıntı olduğundan şüphelenmek için iyi bir nedeniniz vardır.

Testere dişi şekilli grafik

Sorunun mevcut olduğunu doğruladıktan sonra, DevTools Yığın Profil Aracı'ndan sorunun kaynağını belirleme konusunda yardım alabilirsiniz.

Geliştirici Araçları Yığın Profil Aracı ile Bellek Sızıntılarını Bulma

Profiler paneli hem CPU profil aracı hem de Yığın profil aracı sağlar. Yığın profili oluşturma işlemi, nesne grafiğinin anlık görüntüsünü alarak yapılır. Anlık görüntü alınmadan önce hem genç hem yaşlı kuşak atıklarla toplanır. Diğer bir deyişle, yalnızca anlık görüntü çekildiğinde aktif olan değerleri görürsünüz.

Yığın profil aracındaki çok fazla işlev bu makalede yeterince ele alınmamıştır, ancak ayrıntılı dokümanlara Chrome Geliştiricileri sitesinden ulaşabilirsiniz. Burada Yığın Ayırma profil aracı üzerinde odaklanacağız.

Yığın Atama Profil Aracı'nı Kullanma

Yığın Ayırma profil aracı, Yığın Profil Aracı'nın ayrıntılı anlık görüntü bilgilerini, Zaman Çizelgesi panelinin artımlı güncellenmesi ve takibiyle birleştirir. Profiller panelini açın, Kayıt Yığını Ayırma profili başlatın, bir dizi işlem gerçekleştirin ve ardından analiz için kaydı durdurun. Ayırma profil aracı, kayıt boyunca düzenli aralıklarla (en fazla 50 ms'de bir) ve kaydın sonunda son bir anlık görüntü alır.

Yığın ayırma profil aracı

Üstteki çubuklar, yığında yeni nesnelerin ne zaman bulunduğunu gösterir. Her bir çubuğun yüksekliği, en son ayrılan nesnelerin boyutuna karşılık gelir ve çubukların rengi, bu nesnelerin son yığın anlık görüntüsünde hâlâ aktif olup olmadığını belirtir. Mavi çubuklar, zaman çizelgesinin sonunda hâlâ yayında olan nesneleri, gri çubuklar ise zaman çizelgesi sırasında ayrılan, ancak o zamandan beri çöp toplanmış olan nesneleri belirtir.

Yukarıdaki örnekte, bir işlem 10 kez gerçekleştirilmiştir. Örnek program beş nesneyi önbelleğe aldığı için son beş mavi çubuk beklenir. Ancak en soldaki mavi çubuk, potansiyel bir sorun olduğunu gösterir. Ardından, söz konusu anlık görüntüyü yakınlaştırmak ve o noktada yakın zamanda ayrılan nesneleri görmek için yukarıdaki zaman çizelgesinde bulunan kaydırma çubuklarını kullanabilirsiniz. Yığındaki belirli bir nesneyi tıkladığınızda, yığın anlık görüntüsünün alt kısmında bu nesnenin tutma ağacı gösterilir. Nesneye giden koruma yolunu incelemek, nesnenin neden toplanmadığını anlamanız için size yeterli bilgiyi sağlayacaktır. Gereksiz referansı kaldırmak için gerekli kod değişikliklerini yapabilirsiniz.

Gmail'in Bellek Krizini Çözme

Gmail ekibi yukarıda ele alınan araçları ve teknikleri kullanarak birkaç hata kategorisi belirlemeyi başardı: sınırsız önbellekler, gerçekte hiçbir zaman gerçekleşmeyen bir şeyin gerçekleşmesini bekleyen, sonsuz sayıda artan geri çağırma dizileri ve istemeden hedeflerini elinde tutan etkinlik dinleyicileri. Bu sorunlar giderildiğinde, Gmail'in genel bellek kullanımı önemli ölçüde azaltılmıştır. %99 oranındaki kullanıcılar eskiye kıyasla% 80 daha az bellek kullanırken ortanca kullanıcıların bellek tüketimi yaklaşık %50 oranında azaldı.

Gmail bellek kullanımı

Gmail daha az bellek kullandığından GC duraklatma gecikmesi azaltılarak genel kullanıcı deneyimi artırıldı.

Ayrıca, Gmail ekibinin bellek kullanımıyla ilgili istatistikleri toplamasıyla birlikte Chrome'daki çöp toplama regresyonlarını ortaya çıkarmayı da başardılar. Özellikle, Gmail'in bellek verileri, ayrılan toplam bellek ile canlı bellek arasındaki boşlukta önemli bir artış göstermeye başladığında iki parçalama hatası keşfedildi.

Harekete Geçirici Mesaj

Kendinize şu soruları sorun:

  1. Uygulamam ne kadar bellek kullanıyor? Çok fazla bellek kullanıyor olabilirsiniz. Bu durum, yaygın inanışın aksine, genel uygulama performansını olumsuz yönde etkiler. Doğru sayının tam olarak ne olduğunu bilmek zordur. Ancak, sayfanızda fazladan önbelleğe almanın ölçülebilir bir etkisi olduğundan emin olun.
  2. Sayfam sızıntısız mı? Sayfanızda bellek sızıntıları varsa bu durum yalnızca sayfanızın performansını değil, diğer sekmeleri de etkileyebilir. Sızıntıları tespit etmek için nesne takip aracını kullanın.
  3. Sayfam ne sıklıkta GC kullanıyor? Chrome Geliştirici Araçları'ndaki Zaman çizelgesi panelini kullanarak GC duraklatmalarını görebilirsiniz. Sayfanız sıklıkla GC kullanıyorsa enerji ayırma işlemini çok sık gerçekleştiriyor, yeni nesil belleğinizi tüketiyor olabilirsiniz.

Sonuç

Kriz anında başlamıştık. Özellikle JavaScript ve V8'de bellek yönetiminin temel temellerini ele aldı. Chrome'un en yeni sürümlerindeki yeni nesne izleyici özelliği de dahil olmak üzere araçları nasıl kullanacağınızı öğrendiniz. Bu bilgilerle donanmış Gmail ekibi, bellek kullanımı sorununu çözdü ve performansta artış gözlemledi. Bunu web uygulamalarınız için de yapabilirsiniz!