Saya memiliki aplikasi Javascript yang cukup kompleks, yang memiliki loop utama yang dipanggil 60 kali per detik. Sepertinya ada banyak pengumpulan sampah yang terjadi (berdasarkan keluaran 'gigi gergaji' dari timeline Memori di alat pengembang Chrome) - dan ini sering memengaruhi kinerja aplikasi.
Jadi, saya mencoba meneliti praktik terbaik untuk mengurangi jumlah pekerjaan yang harus dilakukan pemulung. (Sebagian besar informasi yang dapat saya temukan di web berkenaan dengan menghindari kebocoran memori, yang merupakan pertanyaan yang sedikit berbeda - ingatan saya semakin dibebaskan, hanya saja ada terlalu banyak pengumpulan sampah yang terjadi.) Saya berasumsi bahwa ini sebagian besar bermuara pada penggunaan kembali objek sebanyak mungkin, tetapi tentu saja iblis ada dalam detailnya.
Aplikasi ini disusun dalam 'kelas' di sepanjang garis Warisan JavaScript Sederhana John Resig .
Saya pikir satu masalah adalah bahwa beberapa fungsi dapat dipanggil ribuan kali per detik (karena digunakan ratusan kali selama setiap iterasi loop utama), dan mungkin variabel kerja lokal dalam fungsi ini (string, array, dll.) mungkin masalahnya.
Saya mengetahui pengumpulan objek untuk objek yang lebih besar / lebih berat (dan kami menggunakan ini sampai tingkat tertentu), tetapi saya mencari teknik yang dapat diterapkan di seluruh papan, terutama yang berkaitan dengan fungsi yang sering dipanggil dalam loop ketat .
Teknik apa yang dapat saya gunakan untuk mengurangi jumlah pekerjaan yang harus dilakukan oleh pengumpul sampah?
Dan, mungkin juga - teknik apa yang dapat digunakan untuk mengidentifikasi objek mana yang paling banyak dikumpulkan sampah? (Ini adalah basis kode yang sangat besar, jadi membandingkan snapshot dari heap belum terlalu membuahkan hasil)
sumber
Jawaban:
Banyak hal yang perlu Anda lakukan untuk meminimalkan churn GC bertentangan dengan apa yang dianggap JS idiomatik di sebagian besar skenario lainnya, jadi harap perhatikan konteksnya saat menilai saran yang saya berikan.
Alokasi terjadi pada penafsir modern di beberapa tempat:
new
atau melalui sintaks literal[...]
, atau{}
.(function (...) { ... })
.Object(myNumber)
atauNumber.prototype.toString.call(42)
Array.prototype.slice
.arguments
untuk merefleksikan daftar parameter.Hindari melakukan itu, dan kumpulkan serta gunakan kembali objek jika memungkinkan.
Secara khusus, perhatikan peluang untuk:
split
atau pencocokan ekspresi reguler karena masing-masing memerlukan beberapa alokasi objek. Ini sering terjadi dengan kunci ke dalam tabel pemeta dan ID simpul DOM dinamis. Misalnya,lookupTable['foo-' + x]
dandocument.getElementById('foo-' + x)
keduanya melibatkan alokasi karena ada rangkaian string. Seringkali Anda dapat melampirkan kunci ke objek berumur panjang alih-alih menggabungkan kembali. Bergantung pada browser yang perlu Anda dukung, Anda mungkin dapatMap
menggunakan objek sebagai kunci secara langsung.try { op(x) } catch (e) { ... }
, lakukanif (!opCouldFailOn(x)) { op(x); } else { ... }
.JSON.stringify
yang menggunakan buffer asli internal untuk mengakumulasi konten daripada mengalokasikan beberapa objek.arguments
karena fungsi yang digunakan harus membuat objek seperti array saat dipanggil.Saya menyarankan menggunakan
JSON.stringify
untuk membuat pesan jaringan keluar. Mengurai pesan input menggunakanJSON.parse
jelas melibatkan alokasi, dan banyak untuk pesan besar. Jika Anda dapat merepresentasikan pesan masuk Anda sebagai array primitif, maka Anda dapat menghemat banyak alokasi. Satu-satunya bawaan lain yang bisa Anda gunakan untuk membuat parser yang tidak mengalokasikan adalahString.prototype.charCodeAt
. Parser untuk format kompleks yang hanya menggunakan itu akan sangat sulit untuk dibaca.sumber
JSON.parse
objek d mengalokasikan lebih sedikit (atau sama) ruang daripada string pesan?Alat pengembang Chrome memiliki fitur yang sangat bagus untuk melacak alokasi memori. Ini disebut Memory Timeline. Artikel ini menjelaskan beberapa detail. Saya kira ini yang Anda bicarakan tentang "gigi gergaji"? Ini adalah perilaku normal untuk sebagian besar runtime yang di-GC. Alokasi berlanjut hingga ambang penggunaan tercapai yang memicu pengumpulan. Biasanya ada berbagai jenis koleksi di ambang yang berbeda.
Koleksi sampah termasuk dalam daftar acara yang terkait dengan jejak beserta durasinya. Di buku catatan saya yang agak lama, koleksi singkat terjadi pada sekitar 4 MB dan memakan waktu 30 md. Ini adalah 2 dari iterasi loop 60Hz Anda. Jika ini adalah animasi, koleksi 30 md mungkin menyebabkan gagap. Anda harus mulai di sini untuk melihat apa yang terjadi di lingkungan Anda: di mana ambang batas pengumpulan dan berapa lama waktu pengambilan koleksi Anda. Ini memberi Anda titik referensi untuk menilai pengoptimalan. Tetapi Anda mungkin tidak akan melakukan lebih baik daripada mengurangi frekuensi gagap dengan memperlambat tingkat alokasi, memperpanjang interval antar koleksi.
Langkah selanjutnya adalah menggunakan Profiles | Fitur Record Heap Allocations untuk menghasilkan katalog alokasi berdasarkan jenis record. Ini akan segera menunjukkan tipe objek mana yang paling banyak mengonsumsi memori selama periode pelacakan, yang setara dengan tingkat alokasi. Fokus pada ini dalam urutan suku bunga menurun.
Tekniknya bukanlah ilmu roket. Hindari objek dalam kotak jika Anda dapat melakukannya dengan yang tidak dikotakkan. Gunakan variabel global untuk menahan dan menggunakan kembali objek kotak tunggal daripada mengalokasikan yang baru di setiap iterasi. Gabungkan jenis objek umum dalam daftar gratis daripada mengabaikannya. Hasil penggabungan string cache yang kemungkinan dapat digunakan kembali di iterasi mendatang. Hindari alokasi hanya untuk mengembalikan hasil fungsi dengan mengatur variabel dalam lingkup tertutup sebagai gantinya. Anda harus mempertimbangkan setiap jenis objek dalam konteksnya sendiri untuk menemukan strategi terbaik. Jika Anda membutuhkan bantuan dengan spesifik, posting suntingan yang menjelaskan rincian tantangan yang Anda lihat.
Saya menyarankan agar tidak mengubah gaya pengkodean normal Anda di seluruh aplikasi dalam upaya senapan untuk menghasilkan lebih sedikit sampah. Ini untuk alasan yang sama Anda tidak harus mengoptimalkan kecepatan sebelum waktunya. Sebagian besar usaha Anda ditambah banyak kerumitan tambahan dan ketidakjelasan kode tidak akan berarti.
sumber
request animation frame
,animation frame fired
, dancomposite layers
. Saya tidak tahu mengapa saya tidak melihatGC Event
seperti Anda (ini ada di versi terbaru chrome, dan juga kenari).@342342
dancode relocation info
.Sebagai prinsip umum, Anda ingin menyimpan sebanyak mungkin dan melakukan sesedikit mungkin pembuatan dan penghancuran untuk setiap putaran loop Anda.
Hal pertama yang muncul di kepala saya adalah mengurangi penggunaan fungsi anonim (jika ada) di dalam loop utama Anda. Juga akan mudah untuk jatuh ke dalam perangkap membuat dan menghancurkan objek yang diteruskan ke fungsi lain. Saya sama sekali bukan ahli javascript, tetapi saya akan membayangkan bahwa ini:
akan berjalan lebih cepat dari ini:
Apakah ada waktu henti untuk program Anda? Mungkin Anda membutuhkannya agar berjalan lancar selama satu atau dua detik (misalnya untuk animasi) dan kemudian memiliki lebih banyak waktu untuk memproses? Jika ini kasusnya, saya dapat melihat mengambil objek yang biasanya akan menjadi sampah yang dikumpulkan selama animasi dan menyimpan referensi ke objek tersebut di beberapa objek global. Kemudian saat animasi berakhir Anda dapat menghapus semua referensi dan membiarkan pengumpul sampah melakukan tugasnya.
Maaf jika ini semua agak sepele dibandingkan dengan apa yang sudah Anda coba dan pikirkan.
sumber
Saya akan membuat satu atau beberapa objek di
global scope
(di mana saya yakin pengumpul sampah tidak diizinkan untuk menyentuhnya), lalu saya akan mencoba memfaktor ulang solusi saya untuk menggunakan objek tersebut untuk menyelesaikan pekerjaan, daripada menggunakan variabel lokal .Tentu saja ini tidak dapat dilakukan di semua tempat dalam kode, tetapi secara umum itulah cara saya untuk menghindari pengumpul sampah.
PS Ini mungkin membuat bagian tertentu dari kode sedikit kurang dapat dipelihara.
sumber