Angular – Subscribe Durumlarında Memory Leaks Yönetimi İle Bellek Optimizasyonu
Merhaba,
Angular uygulamalarında “subscribe” fonksiyonu neticesinde ihtiyaca dönük veri akışını asenkron olarak takip edebildiğimiz yapılarımız mevcuttur. Bunlar; kâh ActivatedRoute nesnesi ile yapılan route üzerinde parametreden değer okuma amaçlı takip iken kâh herhangi bir adrese istek gönderen HttpClient nesnesinin herhangi bir http fonksiyonu neticesinde result değerini elde etmek için yapılan takiptir. Bunlara benzer yapılanmalarda “subscribe” fonksiyonu ile abone olunmakta ve veriler bu abonelik süresince dinlenmekte, neticede gelen veri elde edilmektedir. Bu içeriğimizde ise pratikte “unsubscribe” fonksiyonu ile bu abonelikleri sonlandırmayı inceleyecek, teorik olarak neden sonlandırmalıyız sorusuna cevap arayacağız.
İlk olarak component yapılanması nasıl? sorusunu değerlendirerek işe başlayalım. Angular’da; herhangi bir x componenti, üretildikten sonra kullanım sonunda yok edilebilmekte ve ihtiyaca dönük tekrar üretilebilmektedir. Lakin bu süreçte önceden üretilen instance yok olsa dahi mevcut abonelikler muhafaza edilmekte, temizlenmemektedir. Dolayısıyla componentler yeniden yaratıldıkça daha fazla abonelik oluşturulmakta ve bu işlemler bir noktadan sonra bellekte lüzumsuz maliyete sebep olmaktadır. İşte bizler bu tarz uygulamayla ilgili tüm işlemler bittiği halde bellekte yer işgal eden durumlara memory leaks ismini vermekteyiz.
Memory Leaks
Yukarıda da açıklamaya çalıştığımız memory leaks durumunu biraz daha açarsak eğer; işletim sistemi tarafından uygulamaya ayrılan hafızayı, uygulamanın işlevsel özelliği sona erince geri vermeyi ihmal etmesi durumudur diyebiliriz. Memory leaks’i farketmek bir miktar zaman, mevcut duruma müdahalede bulunup bu durumu gidermek daha fazla zaman alabilir. C# vs gibi dillerde bildiğimiz gereksiz bilgi toplayıcısı(garbage collection) bir nebze bu duruma karşı mukavemet göstermekte lakin baş edemediği durumlarda uygulamanın yediği hafızanın paçalardan aktığı bilinmektedir.
İşte Angular uygulamalarında memory leaks konseptini engellemek için işlerimiz bittikten sonra tüm aboneliklerden çıkılması gerekmektedir. O halde ne zaman abonelikten çıkmak zorundayız? sorusuna karşılık açık cevap “ihtiyacımız olmadığı zaman” demek oldukça doğru ve net bir cevap olacaktır. Burada ihtiyacımız olmadığı zamandan kastımız aboneliği sonlandıracağımız zamanı opsiyonel hale getirmektedir. Bu durum bir anlık öfke yahut dikkatsizlikten çok erkenden abonelikten çıkma gibi bir riski getirmektedir. O halde aboneliklerden çıkış yapmak için bizlere çok daha güvenli, basit ve dünyaca önerilen yöntem olan “OnDestroy” interface’inin elemanı “ngOnDestroy” metodu yetişmektedir. Component imha edilirken tetiklenen “ngOnDestroy” fonksiyonu, componente ait olan tüm abonelikleri sonlandırma görevini üstlenerek bu şekilde süreçteki bellek yönetimine maksimum katkıyı sağlayabilir.
unsubscribe Fonksiyonunun Kullanımı
Component içerisinde aboneliği “Subscription” tipinden bir değişken ile refere edebilir/tutabilir ve aşağıdaki gibi “ngOnDestroy” fonksiyonu ile abonelikleri koparabilirsiniz.
import { Component, OnInit, OnDestroy } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { Subscription } from 'rxjs'; @Component({ selector: 'app-employees', templateUrl: './employees.component.html', styleUrls: ['./employees.component.css'] }) export class EmployeesComponent implements OnInit, OnDestroy { constructor(private httpClient: HttpClient) { } employees; getObservable: Subscription; ngOnInit() { this.getObservable = this.httpClient.get("https://localhost:5001/api/example").subscribe(data => this.employees = data); } ngOnDestroy() { this.getObservable.unsubscribe(); } }
Birden fazla aboneliğin olması durumunda ilgili abonelikleri tek tek değişkende refere edip aboneliklerini sonlandırabileceğiniz gibi aşağıda olduğu gibi hepsini tek bir “Subscription” içerisinde toplayıp toplu şekilde de aboneliklerini koparabilirsiniz.
import { Component, OnInit, OnDestroy } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { Subscription } from 'rxjs'; @Component({ selector: 'app-employees', templateUrl: './employees.component.html', styleUrls: ['./employees.component.css'] }) export class EmployeesComponent implements OnInit, OnDestroy { constructor(private httpClient: HttpClient) { } employees; getObservable: Subscription; ngOnInit() { this.getObservable = this.httpClient.get("https://localhost:5001/api/example").subscribe(data => this.employees = data); this.getObservable.add(this.httpClient.post("https://localhost:5001/api/example", {}).subscribe(data => { })); } ngOnDestroy() { this.getObservable.unsubscribe(); } }
takeUntil RxJS Operatörünün Kullanımı
Her ne kadar unsubscribe fonksiyonu abonelik iptalini açıkça gerçekleştiriyor olsada aynı işlemi yapmanın daha da iyi bir yolu olan “takeUntil” RxJS operatörü mevcuttur. Keza “takeUntil” operatörü unsubscribe’a nazaran daha çok önerilen bir yaklaşımdır.
import { Component, OnInit, OnDestroy } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { Subscription, Subject } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; @Component({ selector: 'app-employees', templateUrl: './employees.component.html', styleUrls: ['./employees.component.css'] }) export class EmployeesComponent implements OnInit, OnDestroy { constructor(private httpClient: HttpClient) { } employees; private ngUnsubscribe = new Subject(); ngOnInit() { this.httpClient.get("https://localhost:5001/api/example").pipe(takeUntil(this.ngUnsubscribe)).subscribe(data => this.employees = data); this.httpClient.post("https://localhost:5001/api/example", {}).pipe(takeUntil(this.ngUnsubscribe)).subscribe(data => { }); } ngOnDestroy() { this.ngUnsubscribe.next(); this.ngUnsubscribe.complete(); } }
Sonuç olarak; hangi platformda olursak olalım, hangi mimari yahut çekirdeği kullanırsak kullanalım bellek yönetiminde derin boşluk oluşturan “Memory Leaks” durumunu göz önüne almalı ve uygulamanın işlevi bittiği zaman uygulamaya ayrılmış olan tüm alanlardan çekilmeli, varsa veri akışlarını veya abonelikleri sonlandırmalıyız. Dolayısıyla bu mantıkla Angular uygulamalarında da ilgili duruma dikat etmeli ve tüm subscribe durumlarını işlevi bittiği zaman sonlandırmalıyız.
İlgilenenlerin faydalanması dileğiyle…
Sonraki yazılarımda görüşmek üzere…
İyi çalışmalar…