Giriş
Web oyununuzun/web uygulamanızın belirli bir süre sonra kötü performans gösterdiğine dair bir e-posta alırsınız. Kodunuzu incelersiniz, göze çarpan bir şey görmezsiniz. Sonra Chrome'un bellek performansı araçlarını açar ve şunu görürsünüz:
İş arkadaşlarınızdan biri, hafızayla ilgili bir performans sorunu yaşadığınızı fark ettiği için kıkırdar.
Bellek grafiği görünümünde, bu testere dişi desen, kritik olabilecek bir performans sorunu hakkında çok bilgi verir. Bellek kullanımınız arttıkça zaman çizelgesi yakalamasında grafik alanının da büyüdüğünü görürsünüz. Grafikte ani bir düşüş olduğunda, Çöp Toplayıcı'nın çalıştığı ve referans verilen bellek nesnelerinizi temizlediği bir durum söz konusudur.
Bunun gibi bir grafikte, web uygulamalarınızın performansına zarar verebilecek çok fazla Çöp Toplama etkinliği olduğunu görebilirsiniz. Bu makalede, performansınız üzerindeki etkiyi azaltmak için bellek kullanımınızı nasıl kontrol edeceğiniz açıklanmaktadır.
Çöp toplama ve performans maliyetleri
JavaScript'in bellek modeli, çöp toplayıcı olarak bilinen bir teknolojiye dayanır. Birçok dilde, sistemin Bellek Yığını'ndan bellek ayırıp yer açmaktan doğrudan programcı sorumludur. Ancak Çöp Toplayıcı sistemi bu görevi programcı adına yönetir. Yani programcı referansı kaldırdığında nesneler doğrudan bellekten serbest bırakılmaz. Bunun yerine, Çöp Toplayıcı'nın sezgisel kurallarının bunu yapmanın yararlı olduğuna karar verdiği daha sonraki bir zamanda serbest bırakılır. Bu karar süreci, Çöp Toplayıcı'nın etkin ve etkin olmayan nesneler üzerinde bazı istatistiksel analizler yapmasını gerektirir. Bu analizlerin yapılması zaman alır.
Çöp toplama, genellikle programcının hangi nesnelerin bellek ayırımını kaldıracağını ve bellek sistemine döndüreceğini belirtmesini gerektiren manuel bellek yönetiminin zıttı olarak gösterilir.
GC'nin belleği geri kazandığı süreç ücretsiz değildir. Genellikle işini yapmak için bir zaman aralığı alarak kullanılabilir performansınızı azaltır. Bununla birlikte, ne zaman çalışacağına karar veren sistemdir. Bu işlem üzerinde hiçbir kontrolünüz yoktur. Kod yürütme sırasında herhangi bir zamanda GC darbesi gerçekleşebilir. Bu darbe, tamamlanana kadar kod yürütmeyi engeller. Bu nabızın ne kadar süreceği genellikle sizin tarafınızdan bilinmez; programınızın herhangi bir noktada belleği nasıl kullandığına bağlı olarak çalışması biraz zaman alır.
Yüksek performanslı uygulamalar, kullanıcılara sorunsuz bir deneyim sunmak için tutarlı performans sınırlarına dayanır. Çöp toplayıcı sistemler, rastgele zamanlarda ve rastgele süreler boyunca çalışarak uygulamanın performans hedeflerini karşılaması için gereken kullanılabilir süreyi azalttığından bu hedefi kısa devre yaptırabilir.
Bellek kullanımını azaltın, çöp toplama vergilerini azaltın
Belirtildiği gibi, bir dizi sezgisel kural, bir darbenin faydalı olacağı kadar etkin olmayan nesne olduğunu belirlediğinde bir GC darbesi gerçekleşir. Bu nedenle, Çöp Toplayıcı'nın uygulamanızdan aldığı süreyi azaltmanın anahtarı, mümkün olduğunca fazla aşırı nesne oluşturma ve bırakma durumunu ortadan kaldırmaktır. Sık sık nesne oluşturma/boşaltma işlemine "bellek karmaşası" adı verilir. Uygulamanızın kullanım ömrü boyunca bellek kaybını azaltabilirsiniz. Bu, GC'nin yürütmeniz için gereken süreyi de azaltır. Yani, oluşturulan ve yok edilen nesnelerin sayısını kaldırmanız/azaltmanız, yani bellek ayırmayı bırakmanız gerekir.
Bu işlem, anı grafiğinizi şuradan taşır:
şuna:
Bu modelde, grafiğin artık testere dişi şeklinde bir modele sahip olmadığını, bunun yerine başlangıçta çok fazla büyüdüğünü ve zaman içinde yavaş yavaş arttığını görebilirsiniz. Bellek değişimi nedeniyle performans sorunları yaşıyorsanız bu tür bir grafik oluşturmanız gerekir.
Statik bellek JavaScript'e geçiş
Statik Bellek JavaScript, uygulamanızın başında, uygulamanın yaşam süresi boyunca ihtiyaç duyulacak tüm belleği önceden ayırmayı ve artık nesnelere ihtiyaç duyulmadığı için yürütme sırasında bu belleği yönetmeyi içeren bir tekniktir. Bu hedefe birkaç basit adımda yaklaşabiliriz:
- Çeşitli kullanım senaryoları için gerekli maksimum canlı bellek nesnesi (tür başına) sayısını belirlemek üzere uygulamanızı kullanın.
- Bu maksimum tutarı önceden ayıracak şekilde kodunuzu yeniden uygulayın ve ardından ana belleğe gitmek yerine bunları manuel olarak alın/serbest bırakın.
Aslında 1. maddeyi gerçekleştirmek için 2. maddeyi biraz yapmamız gerekir. Bu nedenle, önce 2. maddeden başlayalım.
Nesne Havuzu
Basit bir şekilde anlatmak gerekirse nesne havuzu, aynı türe sahip kullanılmayan bir grup nesneyi tutma işlemidir. Kodunuz için yeni bir nesneye ihtiyaç duyduğunuzda sistem Bellek Yığını'ndan yeni bir nesne ayırmak yerine havuzdaki kullanılmayan nesnelerden birini geri dönüştürürsünüz. Harici kod, nesneyle ilgili işlemlerini tamamladıktan sonra nesneyi ana belleğe bırakmak yerine havuza döndürür. Nesne koddan hiçbir zaman referansla ilişkisi kaldırılmadığı (yani silinmediği) için çöp toplanmayacaktır. Nesne havuzlarını kullanmak, belleğin kontrolünü programcıya geri verir ve çöp toplayıcının performans üzerindeki etkisini azaltır.
Bir uygulamanın yönettiği heterojen bir nesne türü grubu olduğundan, nesne havuzlarının düzgün bir şekilde kullanılması için uygulamanızın çalışma zamanında yüksek değişim yaşayan her tür için bir havuzunuz olması gerekir.
var newEntity = gEntityObjectPool.allocate();
newEntity.pos = {x: 215, y: 88};
//..... do some stuff with the object that we need to do
gEntityObjectPool.free(newEntity); //free the object when we're done
newEntity = null; //free this object reference
Uygulamaların büyük bir çoğunluğu için, yeni nesneler ayırma ihtiyacı açısından bir noktada sabit bir seviyeye ulaşırsınız. Uygulamanızı birden çok kez çalıştırarak bu üst sınırın ne olduğunu anlayabilir ve uygulamanızın başında bu sayıda nesneyi önceden ayırabilirsiniz.
Nesneleri önceden ayırma
Nesne havuzunu projenize uygulamak, uygulamanızın çalışma zamanında gerekli olan nesne sayısı için teorik bir maksimum değer sağlar. Sitenizi çeşitli test senaryolarında çalıştırdıktan sonra, ihtiyaç duyulacak bellek gereksinimleri türleri hakkında iyi bir fikir edinebilir, bu verileri bir yerde kataloglayabilir ve uygulamanız için bellek gereksinimlerinin üst sınırlarını anlamak üzere analiz edebilirsiniz.
Ardından, uygulamanızın gönderim sürümünde, tüm nesne havuzlarını belirli bir miktarda önceden dolduracak şekilde başlatma aşamasını ayarlayabilirsiniz. Bu işlem, tüm nesne başlatma işlemlerini uygulamanızın önüne getirir ve yürütülmesi sırasında dinamik olarak gerçekleşen tahsis miktarını azaltır.
function init() {
//preallocate all our pools.
//Note that we keep each pool homogeneous wrt object types
gEntityObjectPool.preAllocate(256);
gDomObjectPool.preAllocate(888);
}
Seçtiğiniz miktar, uygulamanızın davranışıyla yakından ilgilidir. Bazen teorik maksimum değer en iyi seçenek olmayabilir. Örneğin, ortalama maksimum değeri seçmek, ileri düzey kullanıcı olmayanlar için daha küçük bir bellek kullanımı sağlayabilir.
Her sorunu çözen sihirli bir değnek değildir.
Statik bellek büyüme modellerinin avantajlı olabileceği bir dizi uygulama sınıflandırması vardır. Ancak diğer Chrome DevRel Renato Mangini'nin de belirttiği gibi, bazı dezavantajlar var.
Sonuç
JavaScript'in web için ideal olmasının nedenlerinden biri de hızlı, eğlenceli ve kullanımı kolay bir dil olmasıdır. Bunun başlıca nedeni, söz dizimi kısıtlamalarına karşı düşük engel olması ve bellek sorunlarını sizin adınıza ele almasıdır. Kod yazabilir ve kirli işleri onun halletmesine izin verebilirsiniz. Ancak HTML5 oyunları gibi yüksek performanslı web uygulamalarında, GC genellikle kritik düzeyde ihtiyaç duyulan kare hızını tüketebilir ve son kullanıcı deneyimini azaltabilir. Dikkatli bir şekilde ölçüm yaparak ve nesne havuzlarını kullanarak kare hızınız üzerindeki bu yükü azaltabilir ve bu zamanı daha da etkileyici şeyler için kullanabilirsiniz.
Kaynak Kod
Web'de çok sayıda nesne havuzu uygulaması var, bu yüzden başka bir uygulama sunarak sizi sıkmayacağım. Bunun yerine sizi, her uygulamanın kendine özgü incelikleri olan bu bölümlere yönlendireceğim. Her uygulama kullanımının belirli uygulama ihtiyaçları olabileceğini göz önünde bulundurduğumuzda bunlar önemli.
- Gamecore.js'nin nesne havuzu
- Beej'in Nesne Havuzları
- Emehrkay’ın süper basit nesne havuzu
- Steven Lambert'in oyun odaklı nesne havuzu
- RenderEngine nesnePool kurulumu