JavaScript memori statis dengan Kumpulan Objek

Pengantar

Jadi Anda mendapatkan email yang menginformasikan bagaimana performa game web / aplikasi web Anda buruk setelah jangka waktu tertentu, Anda mempelajari kode Anda, tidak melihat apa pun yang menonjol, hingga Anda membuka alat performa memori Chrome, dan melihat ini:

Ringkasan dari linimasa memori Anda

Salah satu rekan kerja tertawa, karena dia menyadari bahwa Anda memiliki masalah performa terkait memori.

Dalam tampilan grafik memori, pola gigi gergaji ini sangat menceritakan masalah kinerja yang berpotensi kritis. Seiring meningkatnya penggunaan memori, Anda akan melihat area diagram juga berkembang dalam pengambilan linimasa. Saat diagram turun tiba-tiba, ini adalah instance saat Garbage Collector berjalan, dan membersihkan objek memori yang Anda referensikan.

Arti Gigi Gergaji

Dalam grafik seperti ini, Anda dapat melihat bahwa ada banyak peristiwa Pengumpulan Sampah yang terjadi, yang dapat membahayakan performa aplikasi web Anda. Artikel ini akan membahas cara mengontrol penggunaan memori Anda, sehingga mengurangi dampaknya pada performa.

Biaya pembersihan dan pembersihan sampah memori

Model memori JavaScript dibuat dengan teknologi yang dikenal sebagai Garbage Collector. Dalam banyak bahasa, programmer bertanggung jawab langsung untuk mengalokasikan dan mengosongkan memori dari Memory Heap sistem. Namun, sistem Pembersih Sampah mengelola tugas ini atas nama programmer, yang berarti bahwa objek tidak langsung dibebaskan dari memori saat pemrogram menghapus referensinya, tetapi di lain waktu ketika heuristik GC memutuskan bahwa akan bermanfaat untuk melakukannya. Proses keputusan ini mengharuskan GC menjalankan beberapa analisis statistik pada objek aktif dan tidak aktif, yang membutuhkan blok waktu untuk melakukannya.

Pembersihan sampah memori sering digambarkan sebagai kebalikan dari manajemen memori manual, yang mengharuskan {i>programmer<i} menentukan objek mana yang akan dialokasikan kembali dan dikembalikan ke sistem memori

Proses di mana GC mengklaim kembali memori tidak bebas, biasanya mengurangi kinerja yang tersedia dengan meluangkan waktu untuk melakukan pekerjaannya; sementara itu, sistem sendiri yang membuat keputusan kapan harus menjalankannya. Anda tidak memiliki kontrol atas tindakan ini. Pulsa GC dapat terjadi kapan saja selama eksekusi kode, yang akan memblokir eksekusi kode hingga selesai. Durasi denyut ini biasanya tidak Anda ketahui; akan memakan waktu beberapa saat untuk dijalankan, tergantung pada bagaimana program Anda menggunakan memori pada titik tertentu.

Aplikasi berperforma tinggi mengandalkan batas performa yang konsisten untuk memastikan pengalaman yang lancar bagi pengguna. Sistem pengumpul sampah memori dapat mempersingkat sasaran ini, karena dapat berjalan pada waktu yang acak dengan durasi acak, dan menghabiskan waktu yang tersedia yang diperlukan aplikasi untuk memenuhi sasaran performanya.

Mengurangi Churn Memori, Mengurangi pajak Pengumpulan Sampah

Seperti disebutkan, pulsa GC akan terjadi setelah serangkaian heuristik menentukan bahwa ada cukup objek tidak aktif sehingga pulsa akan bermanfaat. Dengan demikian, kunci untuk mengurangi jumlah waktu yang dibutuhkan Pengumpul Sampah dari aplikasi Anda terletak pada menghilangkan sebanyak mungkin kasus pembuatan dan pelepasan objek yang berlebihan. Proses pembuatan/pembebasan objek ini sering disebut "churn memori". Jika Anda dapat mengurangi churn memori selama masa aktif aplikasi, Anda juga mengurangi jumlah waktu yang diperlukan GC dari eksekusi. Ini berarti Anda harus menghapus / mengurangi jumlah objek yang dibuat dan dihancurkan. Secara efektif, Anda harus berhenti mengalokasikan memori.

Proses ini akan memindahkan grafik memori Anda dari :

Ringkasan dari linimasa memori Anda

menjadi:

JavaScript Memori Statis

Dalam model ini, Anda dapat melihat bahwa grafik tidak lagi memiliki pola seperti gigi gergaji, melainkan tumbuh cukup besar di awal, dan kemudian perlahan-lahan meningkat seiring waktu. Jika Anda mengalami masalah performa karena churn memori, inilah jenis grafik yang sebaiknya Anda buat.

Beralih ke JavaScript memori statis

JavaScript Memori Statis adalah teknik yang melibatkan pra-pengalokasian, di awal aplikasi, semua memori yang akan diperlukan selama masa aktifnya, dan pengelolaan memori tersebut selama eksekusi karena objek tidak lagi diperlukan. Kita dapat mendekati sasaran ini dalam beberapa langkah sederhana:

  1. Melengkapi aplikasi untuk menentukan jumlah maksimum objek memori live yang diperlukan (per jenis) untuk berbagai skenario penggunaan
  2. Terapkan kembali kode Anda untuk mengalokasikan jumlah maksimum tersebut di awal, lalu mengambil/melepaskannya secara manual, bukan ke memori utama.

Pada kenyataannya, mencapai #1 mengharuskan kita melakukan sedikit hal #2, jadi mari kita mulai dari sana.

Kumpulan Objek

Secara sederhana, penggabungan objek adalah proses mempertahankan sekumpulan objek yang tidak digunakan yang memiliki suatu jenis yang sama. Saat memerlukan objek baru untuk kode Anda, alih-alih mengalokasikan objek baru dari Memory Heap sistem, Anda dapat mendaur ulang salah satu objek yang tidak digunakan dari pool. Setelah kode eksternal selesai dengan objek, kode akan dikembalikan ke kumpulan, bukan melepaskannya ke memori utama. Karena objek tidak pernah dicabut referensinya (alias dihapus) dari kode, objek tidak akan dibersihkan sebagai sampah memori. Dengan memanfaatkan kumpulan objek, memori kembali dapat dikontrol oleh programmer, sehingga mengurangi pengaruh pembersih sampah memori terhadap performa.

Karena ada serangkaian jenis objek heterogen yang dikelola oleh aplikasi, penggunaan kumpulan objek yang tepat mengharuskan Anda memiliki satu kumpulan per jenis yang mengalami churn tinggi selama runtime aplikasi.

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

Untuk sebagian besar aplikasi, Anda pada akhirnya akan mencapai batas tertentu terkait kebutuhan untuk mengalokasikan objek baru. Melalui beberapa kali pengoperasian aplikasi, Anda akan dapat memahami batas atas ini dengan baik, dan dapat mengalokasikan lebih dahulu jumlah objek tersebut di awal aplikasi.

Melakukan pra-alokasi objek

Mengimplementasikan penggabungan objek ke dalam project akan memberi Anda jumlah maksimum secara teoretis untuk jumlah objek yang diperlukan selama runtime aplikasi. Setelah menjalankan situs melalui berbagai skenario pengujian, Anda dapat memahami jenis persyaratan memori yang akan diperlukan, dan dapat membuat katalog data tersebut di suatu tempat, serta menganalisisnya untuk memahami batas atas persyaratan memori untuk aplikasi Anda.

Kemudian, pada versi pengiriman aplikasi, Anda dapat menetapkan fase inisialisasi untuk mengisi otomatis semua kumpulan objek ke jumlah yang ditentukan. Tindakan ini akan mendorong semua inisialisasi objek ke bagian depan aplikasi Anda, dan mengurangi jumlah alokasi yang terjadi secara dinamis selama eksekusi.

function init() {
  //preallocate all our pools. 
  //Note that we keep each pool homogeneous wrt object types
  gEntityObjectPool.preAllocate(256);
  gDomObjectPool.preAllocate(888);
}

Jumlah yang Anda pilih sangat terkait dengan perilaku aplikasi; terkadang secara teori batas maksimum bukanlah pilihan terbaik. Misalnya, memilih nilai maksimum rata-rata dapat memberi Anda footprint memori yang lebih kecil untuk non-pengguna super.

Gampang sekali

Ada banyak klasifikasi aplikasi di mana pola pertumbuhan memori statis bisa menjadi keunggulan. Namun, seperti yang ditunjukkan oleh Renato Mangini dari sesama Chrome DevRel, ada beberapa kekurangan.

Kesimpulan

Salah satu alasan mengapa JavaScript ideal untuk web bergantung pada fakta bahwa JavaScript adalah bahasa yang cepat, menyenangkan, dan mudah untuk memulai. Hal ini terutama disebabkan oleh kecilnya hambatan terhadap pembatasan sintaks dan penanganannya atas masalah memori untuk Anda. Anda bisa membuat kode dan membiarkannya menangani pekerjaan kotor. Namun untuk aplikasi web berperforma tinggi, seperti game HTML5, GC sering kali dapat menghabiskan kecepatan frame yang sangat diperlukan, sehingga mengurangi pengalaman bagi pengguna akhir. Dengan beberapa instrumentasi dan penerapan kumpulan objek yang cermat, Anda dapat mengurangi beban pada kecepatan frame, dan mendapatkan kembali waktu tersebut untuk hal-hal yang lebih mengagumkan.

Kode Sumber

Ada banyak implementasi kumpulan objek yang beredar di web, jadi saya tidak akan membuat Anda bosan dengan yang lainnya. Sebagai gantinya, saya akan mengarahkan Anda ke bagian ini, yang masing-masing memiliki nuansa implementasi spesifik; yang penting, mengingat setiap penggunaan aplikasi mungkin memiliki kebutuhan implementasi yang spesifik.

Referensi