Nesne havuzları içeren statik bellek JavaScript'i

Colt McAnlis
Colt McAnlis

Giriş

Web oyununuzun/web uygulamanızın belirli bir süre sonra kötü performans gösterdiğini belirten bir e-posta alırsınız. Kodunuzu inceler, dikkat çeken bir şey görmezsiniz. Ancak Chrome'un bellek performansı araçlarını açtığınızda şunları görürsünüz:

Anı zaman çizelgenizden bir anlık görüntü

İş arkadaşlarınızdan biri, bellekle ilgili bir performans sorunu yaşadığınızı fark ettiği için kıkırdıyor.

Bellek grafiği görünümündeki bu testere dişi deseni, potansiyel olarak kritik bir performans sorunu hakkında çok şey anlatır. Bellek kullanımınız arttıkça zaman çizelgesi yakalama işleminde grafik alanının da büyüdüğünü görürsünüz. Grafik aniden düştüğünde, Çöp Toplayıcı çalışmış ve referans verilen bellek nesnelerinizi temizlemiştir.

Testere dişlerinin anlamı

Bunun gibi bir grafikte, çok sayıda çöp toplama etkinliğinin gerçekleştiğini görebilirsiniz. Bu durum, web uygulamalarınızın performansına zarar verebilir. Bu makalede, bellek kullanımınızı nasıl kontrol altına alacağınız ve performans üzerindeki etkisini nasıl azaltacağınız açıklanmaktadır.

Atık toplama ve performans maliyetleri

JavaScript'in bellek modeli, çöp toplayıcı olarak bilinen bir teknoloji üzerine kuruludur. Birçok dilde programcı, sistemin bellek yığınından bellek ayırma ve boşaltma işlemlerinden doğrudan sorumludur. Ancak çöp toplayıcı sistemi, bu görevi programcı adına yönetir. Yani nesneler, programcı referansını kaldırdığında bellekten doğrudan serbest bırakılmaz, daha sonra çöp toplayıcının sezgisel yöntemleri bunun faydalı olacağına karar verdiğinde 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 gerçekleştirilmesi ise belirli bir zaman alır.

Çöp toplama genellikle manuel bellek yönetiminin tersi olarak tanımlanır. Manuel bellek yönetiminde programcının hangi nesnelerin serbest bırakılacağını ve bellek sistemine geri döndürüleceğini belirtmesi gerekir.

GC'nin belleği geri alma işlemi ücretsiz değildir. Genellikle işini yapmak için bir zaman dilimi ayırarak mevcut performansınızı azaltır. Bunun yanı sıra, sistem ne zaman çalıştırılacağına kendisi karar verir. Bu işlem üzerinde kontrolünüz yoktur. Kod yürütme sırasında herhangi bir zamanda GC darbesi gerçekleşebilir. Bu durumda, işlem tamamlanana kadar kod yürütme engellenir. Bu darbenin süresi genellikle 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 rastgele sürelerle çalışarak uygulamanın performans hedeflerine ulaşması için gereken süreyi kısaltabilir ve bu hedefe ulaşmayı engelleyebilir.

Bellek karmaşıklığını ve çöp toplama vergilerini azaltma

Belirtildiği gibi, bir dizi sezgisel yöntem, bir GC pulse'un faydalı olacağı kadar etkin olmayan nesne olduğunu belirlediğinde GC pulse gerçekleşir. Bu nedenle, Çöp Toplayıcı'nın uygulamanızdan aldığı süreyi azaltmanın anahtarı, aşırı nesne oluşturma ve serbest bırakma durumlarını mümkün olduğunca ortadan kaldırmaktır. Sık sık nesne oluşturma/serbest bırakma işlemine "bellek karmaşıklığı" denir. Uygulamanızın kullanım ömrü boyunca bellek karmaşıklığını azaltabilirseniz GC'nin yürütme işleminizden aldığı süreyi de azaltırsınız. Yani oluşturulan ve yok edilen nesnelerin sayısını azaltmanız veya tamamen kaldırmanız, yani bellek ayırmayı durdurmanız gerekir.

Bu işlem, hafıza grafiğinizi şuradan taşır :

Anı zaman çizelgenizden bir anlık görüntü

Şu şekilde değiştirin:

Statik Bellek JavaScript

Bu modelde, grafiğin artık testere dişi benzeri bir desene sahip olmadığını, başlangıçta büyük bir artış gösterdiğini ve ardından zaman içinde yavaşça arttığını görebilirsiniz. Bellek karmaşıklığı nedeniyle performans sorunları yaşıyorsanız bu tür bir grafik oluşturmanız gerekir.

Statik bellekli JavaScript'e geçiş

Statik bellek JavaScript, uygulamanızın başlangıcında kullanım ömrü boyunca ihtiyaç duyulacak tüm belleğin önceden ayrılmasını ve yürütme sırasında artık ihtiyaç duyulmayan nesneler için bu belleğin yönetilmesini içeren bir tekniktir. Bu hedefe birkaç basit adımda ulaşabiliriz:

  1. Çeşitli kullanım senaryoları için gereken maksimum canlı bellek nesnesi sayısını (tür başına) belirlemek üzere uygulamanızı donatın.
  2. Kodunuzu, bu maksimum miktarı önceden tahsis edecek şekilde yeniden uygulayın ve ardından ana belleğe gitmek yerine bunları manuel olarak getirin/serbest bırakın.

Aslında 1. maddeyi gerçekleştirmek için 2. maddeyi de biraz uygulamamız gerekiyor. Bu nedenle, 2. maddeden başlayalım.

Nesne havuzu

Basitçe ifade etmek gerekirse nesne havuzu oluşturma, aynı türdeki kullanılmayan bir grup nesneyi saklama işlemidir. Kodunuz için yeni bir nesneye ihtiyacınız olduğunda, sistemin bellek yığınından yeni bir nesne ayırmak yerine havuzdaki kullanılmayan nesnelerden birini geri dönüştürürsünüz. Harici kodun nesneyle işi bittiğinde nesne ana belleğe bırakılmak yerine havuza döndürülür. Nesne, koddan hiçbir zaman referans kaldırılmadığı (yani silinmediği) için çöp toplama işlemine tabi tutulmaz. Nesne havuzlarını kullanmak, bellek kontrolünü programcıya geri vererek çöp toplayıcının performans üzerindeki etkisini azaltır.

Bir uygulamanın yönettiği heterojen bir nesne türü kümesi olduğundan, nesne havuzlarının doğru kullanımı, uygulamanızın çalışma süresi boyunca yüksek değişim yaşayan her tür için bir havuzunuz olmasını gerektirir.

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 çoğunluğunda, yeni nesneler ayırma ihtiyacı açısından bir noktada düzlüğe ulaşırsınız. Uygulamanızı birkaç kez çalıştırdıktan sonra bu üst sınırın ne olduğu hakkında iyi bir fikir edinebilir ve uygulamanızın başlangıcında bu sayıda nesneyi önceden tahsis edebilirsiniz.

Nesneleri önceden tahsis etme

Projenizde nesne havuzu oluşturma yöntemini uyguladığınızda, uygulamanızın çalışma zamanında gereken nesne sayısı için teorik bir maksimum değer elde edersiniz. Sitenizi çeşitli test senaryolarından geçirdikten sonra, ne tür bellek gereksinimlerine ihtiyaç duyulacağı konusunda iyi bir fikir edinebilir ve bu verileri bir yerde kataloglayıp analiz ederek uygulamanız için bellek gereksinimlerinin üst sınırlarının ne olduğunu anlayabilirsiniz.

Ardından, uygulamanızın yayınlanan sürümünde, başlatma aşamasını tüm nesne havuzlarını belirtilen miktarda önceden dolduracak şekilde ayarlayabilirsiniz. Bu işlem, tüm nesne başlatma işlemlerini uygulamanızın önüne taşır ve yürütme sırasında dinamik olarak gerçekleşen ayırma 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çerseniz deneyimli olmayan kullanıcılar için bellekte kaplanan yer daha az olabilir.

Sihirli değnek değil

Statik bellek büyüme modellerinin avantaj sağlayabileceği bir uygulama sınıflandırması vardır. Ancak Chrome DevRel'den Renato Mangini'nin de belirttiği gibi, bu yöntemin bazı dezavantajları vardır.

Sonuç

JavaScript'in web için ideal olmasının nedenlerinden biri, hızlı, eğlenceli ve kolay bir dil olmasıdır. Bunun temel nedeni, söz dizimi kısıtlamalarıyla ilgili engellerin az olması ve bellek sorunlarını sizin adınıza yönetmesidir. Siz kod yazmaya devam ederken o da sıkıcı işleri halleder. Ancak HTML5 oyunları gibi yüksek performanslı web uygulamalarında GC, kritik öneme sahip kare hızını azaltarak son kullanıcı deneyimini olumsuz etkileyebilir. Dikkatli bir araç kullanımı ve nesne havuzlarının benimsenmesiyle kare hızınız üzerindeki bu yükü azaltabilir ve bu zamanı daha muhteşem şeyler için geri kazanabilirsiniz.

Kaynak kodu

İnternette nesne havuzlarının birçok uygulaması var. Bu yüzden sizi başka bir uygulamayla sıkmayacağım. Bunun yerine, her birinin belirli uygulama nüansları olan bu kaynaklara yönlendireceğim. Her uygulama kullanımının belirli uygulama ihtiyaçları olabileceği düşünüldüğünde bu önemlidir.

Referanslar