Memperbaiki masalah memori

Pelajari cara menggunakan Chrome dan DevTools untuk menemukan masalah memori yang memengaruhi performa halaman, termasuk kebocoran memori, pembengkakan memori, dan pembersihan sampah memori yang sering terjadi.

Ringkasan

  • Cari tahu jumlah memori yang digunakan halaman Anda dengan Pengelola Tugas Chrome.
  • Visualisasikan penggunaan memori dari waktu ke waktu dengan rekaman Linimasa.
  • Identifikasi hierarki DOM yang terlepas (penyebab umum kebocoran memori) dengan Snapshot Heap.
  • Cari tahu kapan memori baru dialokasikan di heap JS Anda dengan rekaman Linimasa Alokasi.
  • Identifikasi elemen terpisah yang dipertahankan oleh referensi JavaScript.

Ringkasan

Sesuai dengan semangat model performa RAIL, fokus upaya performa Anda harus pengguna.

Masalah memori penting karena sering kali dapat dirasakan oleh pengguna. Pengguna dapat merasakan masalah memori dengan cara berikut:

  • Performa halaman semakin memburuk dari waktu ke waktu. Hal ini mungkin merupakan gejala kebocoran memori. Kebocoran memori terjadi saat bug di halaman menyebabkan halaman secara bertahap menggunakan lebih banyak memori dari waktu ke waktu.
  • Performa halaman selalu buruk. Hal ini mungkin merupakan gejala pembengkakan memori. Pembesaran memori adalah saat halaman menggunakan lebih banyak memori daripada yang diperlukan untuk kecepatan halaman yang optimal.
  • Performa halaman tertunda atau tampaknya sering dijeda. Hal ini mungkin merupakan gejala pembersihan sampah memori yang sering. Pembersihan sampah memori adalah saat browser mengklaim ulang memori. Browser akan memutuskan kapan hal ini terjadi. Selama pengumpulan, semua eksekusi skrip dijeda. Jadi, jika browser melakukan pembersihan sampah memori secara berlebihan, eksekusi skrip akan sering dijeda.

Memory bloat: berapa banyak yang "terlalu banyak"?

Kebocoran memori mudah ditentukan. Jika situs secara bertahap menggunakan lebih banyak memori, berarti Anda mengalami kebocoran. Namun, pembengkakan memori sedikit lebih sulit untuk diidentifikasi. Apa yang dimaksud dengan "menggunakan terlalu banyak memori"?

Tidak ada angka pasti di sini, karena perangkat dan browser yang berbeda memiliki kemampuan yang berbeda. Halaman yang sama yang berjalan lancar di smartphone kelas atas mungkin error di smartphone kelas bawah.

Kuncinya adalah menggunakan model RAIL dan berfokus pada pengguna Anda. Cari tahu perangkat yang populer di kalangan pengguna Anda, lalu uji halaman Anda di perangkat tersebut. Jika pengalamannya terus-menerus buruk, halaman mungkin melebihi kemampuan memori perangkat tersebut.

Memantau penggunaan memori secara real time dengan Pengelola Tugas Chrome

Gunakan Pengelola Tugas Chrome sebagai titik awal untuk investigasi masalah memori Anda. Pengelola Tugas adalah monitor real-time yang memberi tahu Anda jumlah memori yang digunakan halaman.

  1. Tekan Shift+Esc atau buka menu utama Chrome, lalu pilih Alat lainnya > Pengelola tugas untuk membuka Pengelola Tugas.

    Membuka Task Manager.

  2. Klik kanan header tabel Pengelola Tugas dan aktifkan memori JavaScript.

    Mengaktifkan memori JS di header Task Manager.

Kedua kolom ini memberi tahu Anda hal-hal yang berbeda tentang cara halaman menggunakan memori:

  • Kolom Jejak memori mewakili memori OS. Node DOM disimpan di memori OS. Jika nilai ini meningkat, node DOM akan dibuat.
  • Kolom JavaScript Memory mewakili heap JS. Kolom ini berisi dua nilai. Nilai yang Anda minati adalah nomor aktif (nomor dalam tanda kurung). Angka live mewakili jumlah memori yang digunakan objek yang dapat dijangkau di halaman Anda. Jika jumlahnya meningkat, objek baru sedang dibuat, atau objek yang ada bertambah.

    Pengelola Tugas dengan header memori JavaScript diaktifkan.

Memvisualisasikan kebocoran memori dengan Perekaman performa

Anda juga dapat menggunakan panel Performa sebagai titik awal lain dalam investigasi. Panel Performa membantu Anda memvisualisasikan penggunaan memori halaman dari waktu ke waktu.

  1. Buka panel Performa di DevTools.
  2. Aktifkan kotak centang Memory.
  3. Buat rekaman.

Untuk menunjukkan perekaman memori Performa, pertimbangkan kode berikut:

var x = [];

function grow() {
  for (var i = 0; i < 10000; i++) {
    document.body.appendChild(document.createElement('div'));
  }
  x.push(new Array(1000000).join('x'));
}

document.getElementById('grow').addEventListener('click', grow);

Setiap kali tombol yang dirujuk dalam kode ditekan, sepuluh ribu node div akan ditambahkan ke isi dokumen, dan string satu juta karakter x akan didorong ke array x. Menjalankan kode ini akan menghasilkan rekaman Linimasa seperti screenshot berikut:

Contoh pertumbuhan sederhana.

Pertama, penjelasan tentang antarmuka pengguna. Grafik HEAP di panel Ringkasan (di bawah NET) mewakili heap JS. Di bawah panel Ringkasan adalah panel Penghitung. Di sini Anda dapat melihat penggunaan memori yang dikelompokkan menurut heap JS (sama seperti grafik HEAP di panel Ringkasan), dokumen, node DOM, pemroses, dan memori GPU. Menonaktifkan kotak centang akan menyembunyikannya dari grafik.

Sekarang, analisis kode dibandingkan dengan screenshot. Jika melihat penghitung node (grafik hijau), Anda dapat melihat bahwa penghitung tersebut cocok dengan kode. Jumlah node meningkat dalam langkah terpisah. Anda dapat mengasumsikan bahwa setiap peningkatan jumlah node adalah panggilan ke grow(). Grafik heap JS (grafik biru) tidak semudah itu. Sesuai dengan praktik terbaik, penurunan pertama sebenarnya adalah pengumpulan sampah paksa (dicapai dengan menekan tombol collect garbage). Seiring progres perekaman, Anda dapat melihat bahwa ukuran heap JS melonjak. Hal ini wajar dan sudah diperkirakan: kode JavaScript membuat node DOM pada setiap klik tombol dan melakukan banyak pekerjaan saat membuat string satu juta karakter. Hal terpenting di sini adalah fakta bahwa heap JS berakhir lebih tinggi daripada saat dimulai ("awal" di sini adalah titik setelah pengumpulan sampah paksa). Di dunia nyata, jika Anda melihat pola peningkatan ukuran heap JS atau ukuran node ini, hal ini berpotensi berarti kebocoran memori.

Menemukan kebocoran memori hierarki DOM yang terlepas dengan Snapshot Heap

Node DOM hanya dapat dihapus saat tidak ada referensi ke node tersebut dari hierarki DOM halaman atau kode JavaScript. Node dikatakan "terlepas" saat dihapus dari hierarki DOM, tetapi beberapa JavaScript masih mereferensikannya. Node DOM yang terpisah adalah penyebab umum kebocoran memori. Bagian ini mengajarkan cara menggunakan profiler heap DevTools untuk mengidentifikasi node yang terpisah.

Berikut adalah contoh sederhana node DOM yang terpisah.

var detachedTree;

function create() {
  var ul = document.createElement('ul');
  for (var i = 0; i < 10; i++) {
    var li = document.createElement('li');
    ul.appendChild(li);
  }
  detachedTree = ul;
}

document.getElementById('create').addEventListener('click', create);

Mengklik tombol yang dirujuk dalam kode akan membuat node ul dengan sepuluh turunan li. Node ini direferensikan oleh kode, tetapi tidak ada dalam hierarki DOM, sehingga dilepaskan.

Snapshot heap adalah salah satu cara untuk mengidentifikasi node yang terlepas. Seperti namanya, snapshot heap menunjukkan kepada Anda cara memori didistribusikan di antara objek JS dan node DOM halaman Anda pada titik waktu snapshot.

Untuk membuat snapshot, buka DevTools dan buka panel Memory, pilih tombol pilihan Heap Snapshot, lalu tekan tombol Take snapshot.

Tombol pilihan Ambil snapshot heap dipilih.

Mungkin perlu waktu beberapa saat untuk memproses dan memuat snapshot. Setelah selesai, pilih dari panel sebelah kiri (bernama Snapshot heap).

Ketik Detached di kotak input Class filter untuk menelusuri hierarki DOM yang terpisah.

Pemfilteran untuk node yang dilepas.

Luaskan tanda karat untuk menyelidiki hierarki yang terpisah.

Menyelidiki hierarki yang terpisah.

Klik node untuk menyelidikinya lebih lanjut. Di panel Objects, Anda dapat melihat informasi selengkapnya tentang kode yang mereferensikannya. Misalnya, pada screenshot berikut, Anda dapat melihat bahwa variabel detachedTree mereferensikan node. Untuk memperbaiki kebocoran memori khusus ini, Anda akan mempelajari kode yang menggunakan detachedTree dan memastikan kode tersebut menghapus referensinya ke node saat tidak lagi diperlukan.

Menyelidiki node yang dilepas.

Mengidentifikasi kebocoran memori heap JS dengan Linimasa Alokasi

Linimasa Alokasi adalah alat lain yang dapat membantu Anda melacak kebocoran memori di heap JS.

Untuk menunjukkan Linimasa Alokasi, pertimbangkan kode berikut:

var x = [];

function grow() {
  x.push(new Array(1000000).join('x'));
}

document.getElementById('grow').addEventListener('click', grow);

Setiap kali tombol yang dirujuk dalam kode didorong, string satu juta karakter akan ditambahkan ke array x.

Untuk merekam Linimasa Alokasi, buka DevTools, buka panel Memory, pilih tombol pilihan Allocations on timeline, tekan tombol Record, lakukan tindakan yang Anda curigai menyebabkan kebocoran memori, lalu tekan tombol Stop recording setelah selesai.

Saat merekam, perhatikan apakah ada batang biru yang muncul di Linimasa Alokasi, seperti pada screenshot berikut.

Alokasi baru di linimasa performa.

Batang biru tersebut mewakili alokasi memori baru. Alokasi memori baru tersebut adalah kandidat untuk kebocoran memori. Anda dapat memperbesar panel untuk memfilter panel Constructor agar hanya menampilkan objek yang dialokasikan selama jangka waktu yang ditentukan.

Linimasa alokasi yang diperbesar.

Luaskan objek dan klik nilainya untuk melihat detail selengkapnya di panel Object. Misalnya, pada screenshot di bawah, dengan melihat detail objek yang baru dialokasikan, Anda dapat melihat bahwa objek tersebut dialokasikan ke variabel x dalam cakupan Window.

Detail objek array string.

Menyelidiki alokasi memori menurut fungsi

Gunakan jenis profil Pengambilan sampel alokasi di panel Memori untuk melihat alokasi memori menurut fungsi JavaScript.

Profiler sampling alokasi di panel Memori.

  1. Pilih tombol pilihan Pengambilan sampel alokasi. Jika ada pekerja di halaman, Anda dapat memilihnya sebagai target pembuatan profil dari jendela Select JavaScript VM instance.
  2. Tekan tombol Start.
  3. Lakukan tindakan di halaman yang ingin Anda periksa.
  4. Tekan tombol Stop setelah Anda menyelesaikan semua tindakan.

DevTools menampilkan perincian alokasi memori berdasarkan fungsi. Tampilan default adalah Heavy (Bottom Up), yang menampilkan fungsi yang mengalokasikan memori terbanyak di bagian atas.

Halaman hasil profil alokasi.

Mengidentifikasi objek yang dipertahankan oleh referensi JS

Profil Elemen terpisah menampilkan elemen terpisah yang tetap ada karena direferensikan oleh kode JavaScript.

Rekam profil Elemen terpisah untuk melihat node HTML dan jumlah node yang tepat.

Contoh profil elemen terpisah.

Menemukan pembersihan sampah memori yang sering terjadi

Jika halaman Anda tampaknya sering dijeda, Anda mungkin mengalami masalah pembersihan sampah.

Anda dapat menggunakan Pengelola Tugas Chrome atau rekaman memori Linimasa untuk menemukan pembersihan sampah yang sering terjadi. Di Task Manager, nilai Memory atau JavaScript Memory yang sering naik dan turun menunjukkan pembersihan sampah memori yang sering. Dalam rekaman Linimasa, grafik jumlah node atau heap JS yang sering naik dan turun menunjukkan pembersihan sampah yang sering terjadi.

Setelah mengidentifikasi masalah, Anda dapat menggunakan rekaman Allocation Timeline untuk mengetahui tempat memori dialokasikan dan fungsi mana yang menyebabkan alokasi.