Pertanyaan Bagaimana panggilan AJAX yang sinkron dapat menyebabkan kebocoran memori?


saya mengerti ini saran umum yang diberikan terhadap penggunaan panggilan ajax sinkron, karena panggilan sinkron memblokir rendering UI.

Alasan lain yang secara umum diberikan adalah masalah kebocoran memori sinkronis AJAX.

Dari MDN dokumen -

Catatan: Anda tidak harus menggunakan XMLHttpRequests yang sinkron karena, karena   sifat jaringan yang tidak sinkron, ada berbagai macam   memori cara dan acara dapat bocor ketika menggunakan permintaan sinkron. Itu   satu-satunya pengecualian adalah bahwa permintaan sinkron bekerja dengan baik di dalam Pekerja.

Bagaimana panggilan sinkron dapat menyebabkan kebocoran memori?

Saya mencari contoh praktis. Setiap petunjuk untuk literatur tentang topik ini akan sangat bagus.


32
2018-01-16 18:15


asal


Jawaban:


Jika XHR diimplementasikan dengan benar per spesifikasi, maka tidak akan bocor:

Objek XMLHttpRequest tidak boleh berupa sampah yang dikumpulkan jika statusnya   DIBUKA dan tanda kirim () disetel, statusnya HEADERS_RECEIVED, atau   negaranya adalah LOADING, dan salah satu dari hal berikut ini benar:

Ini memiliki satu atau lebih pendengar acara yang terdaftar yang jenisnya   readystatechange, progress, abort, error, load, timeout, atau loadend.

Bendera lengkap unggahan tidak disetel dan yang terkait   Objek XMLHttpRequestUpload memiliki satu atau beberapa pendengar acara yang terdaftar   yang jenisnya adalah kemajuan, batalkan, galat, pemuatan, batas waktu, atau pemuatan.

Jika objek XMLHttpRequest adalah sampah yang dikumpulkan sementara koneksinya   masih terbuka, agen pengguna harus membatalkan semua contoh pengambilan   Algoritma dibuka oleh objek ini, membuang semua tugas yang diantrekan untuk mereka,   dan membuang data lebih lanjut yang diterima dari jaringan untuk mereka.

Jadi setelah kamu memukul .send() objek XHR (dan referensi apa pun) menjadi kebal terhadap GC. Namun, kesalahan atau kesuksesan apa pun akan membuat XHR menjadi status DONE dan itu menjadi subjek GC lagi. Itu tidak masalah sama sekali jika objek XHR sinkron atau asinkron. Dalam kasus permintaan sinkronisasi panjang lagi tidak masalah karena Anda hanya akan terjebak pada pernyataan kirim sampai server merespon.

Namun, menurut slide ini itu tidak diterapkan dengan benar setidaknya di Chrome / Chromium pada tahun 2012. Per spesifikasi, tidak perlu menelepon .abort() sejak DONE menyatakan bahwa objek XHR seharusnya sudah normal GCd.

Saya tidak dapat menemukan bukti sekecil apa pun untuk mencadangkan pernyataan MDN dan saya telah menghubungi penulis melalui twitter.


13
2017-08-17 14:51



Saya pikir kebocoran memori terjadi terutama karena pengumpul sampah tidak dapat melakukan tugasnya. Yaitu. Anda memiliki referensi untuk sesuatu dan GC tidak dapat menghapusnya. Saya menulis sebuah contoh sederhana:

var getDataSync = function(url) {
    console.log("getDataSync");
    var request = new XMLHttpRequest();
    request.open('GET', url, false);  // `false` makes the request synchronous
    try {
        request.send(null);
        if(request.status === 200) {
            return request.responseText;
        } else {
            return "";
        }
    } catch(e) {
        console.log("!ERROR");
    }
}

var getDataAsync = function(url, callback) {
    console.log("getDataAsync");
    var xhr = new XMLHttpRequest();
    xhr.open("GET", url, true);
    xhr.onload = function (e) {
        if (xhr.readyState === 4) {
            if (xhr.status === 200) {
                callback(xhr.responseText);
            } else {
                callback("");
            }
        }
    };
    xhr.onerror = function (e) {
        callback("");
    };
    xhr.send(null);
}

var requestsMade = 0
var requests = 1;
var url = "http://missing-url";
for(var i=0; i<requests; i++, requestsMade++) {
    getDataSync(url);
    // getDataAsync(url);
}

Kecuali fakta bahwa fungsi sinkron memblokir banyak hal, ada perbedaan besar lainnya. Penanganan kesalahan. Jika Anda menggunakan getDataSync dan menghapus blok try-catch dan me-refresh halaman Anda akan melihat bahwa kesalahan dilemparkan. Itu karena url tidak ada, tetapi pertanyaannya sekarang adalah bagaimana garbage collector bekerja ketika kesalahan dilemparkan. Apakah itu membersihkan semua objek yang terhubung dengan kesalahan, apakah itu membuat objek kesalahan atau sesuatu seperti itu. Saya akan senang jika ada yang tahu lebih banyak tentang itu dan menulis di sini.


3
2017-08-17 12:31



Jika panggilan sinkron terganggu (yaitu oleh peristiwa pengguna menggunakan kembali objek XMLHttpRequest) sebelum selesai, maka permintaan jaringan yang luar biasa dapat dibiarkan menggantung, tidak dapat menjadi sampah yang dikumpulkan.

Ini karena, jika objek yang memulai permintaan tidak ada saat permintaan kembali, pengembalian tidak dapat diselesaikan, tetapi (jika browser tidak sempurna) tetap berada di memori. Anda dapat dengan mudah menyebabkan ini menggunakan setTimeout untuk menghapus objek permintaan setelah permintaan dibuat tetapi sebelum mengembalikan.

Saya ingat saya punya masalah besar dengan ini di IE, sekitar tahun 2009, tetapi saya berharap bahwa browser modern tidak mudah terpengaruh. Tentu saja, perpustakaan modern (yaitu JQuery) mencegah situasi di mana hal itu mungkin terjadi, memungkinkan permintaan dibuat tanpa harus memikirkannya.


3
2017-08-22 13:11



Sync XHR block thread execution dan semua objek dalam tumpukan eksekusi fungsi dari thread ini dari GC.

Misalnya.:

function (b) { 
  var a = <big data>;
  <work with> a and b
  sync XHR
}

Variabel a dan b diblokir di sini (dan seluruh tumpukan juga). Jadi, jika GC mulai berfungsi maka sinkronisasi XHR telah memblokir tumpukan, semua variabel tumpukan akan ditandai sebagai "GC yang selamat" dan dipindahkan dari awal ke yang lebih persisten. Dan nada objek yang seharusnya tidak bertahan bahkan GC tunggal akan hidup banyak Koleksi Sampah dan bahkan referensi dari objek ini akan bertahan hidup GC.

Tentang klaim tumpukan blok GC, dan objek yang ditandai sebagai objek long-live: lihat bagian Pengumpulan Sampah Konservatif di Mencakar Jalan Kami Kembali Ke Presisi. Juga, "ditandai" objek GCed setelah tumpukan biasa adalah GCed, dan biasanya hanya jika masih perlu untuk membebaskan lebih banyak memori (karena mengumpulkan tanda-tanda-dan-menyapu obj membutuhkan lebih banyak waktu).

MEMPERBARUI: Apakah itu benar-benar kebocoran, bukan hanya solusi awal yang tidak efektif? Ada beberapa hal yang perlu dipertimbangkan.

  • Berapa lama objek ini akan dikunci setelah permintaan selesai?
  • Sync XHR dapat memblokir stack untuk waktu yang tidak terbatas, XHR tidak memiliki properti timeout (di semua browser non-IE), masalah jaringan tidak jarang.
  • Berapa banyak elemen UI yang dikunci? Jika memblokir 20M memori hanya 1 detik == 200k memimpin dalam 2 menit. Pertimbangkan banyak tab latar belakang.
  • Pertimbangkan kasus ketika sinkronisasi tunggal memblokir nada sumber daya dan browser pergi ke file swap
  • Ketika acara lain mencoba untuk mengubah DOM di dapat diblokir oleh sync XHR, thread lain diblokir (dan seluruh tumpukan itu juga)
  • Jika pengguna akan mengulangi tindakan yang mengarah pada sinkronisasi XHR, seluruh jendela browser akan dikunci. Browser menggunakan max = 2 utas untuk menangani peristiwa jendela.
  • Bahkan tanpa memblokir ini menghabiskan banyak sumber daya internal OS dan browser: benang, sumber daya bagian penting, sumber daya UI, DOM ... Bayangkan bahwa Anda dapat membuka (karena masalah memori) 10 tab dengan situs yang menggunakan XHR sinkronisasi dan 100 tab dengan situs yang menggunakan async XHR. Bukankah kebocoran memori ini.

3
2017-08-22 07:00



Kebocoran memori menggunakan permintaan AJAX yang sinkron sering disebabkan oleh:

  • menggunakan setInterval / setTimout menyebabkan panggilan melingkar.
  • XmlHttpRequest - ketika referensi dihapus, sehingga xhr menjadi tidak dapat diakses

Kebocoran memori terjadi ketika browser karena suatu alasan tidak melepaskan memori dari objek yang tidak diperlukan lagi.

Ini mungkin terjadi karena bug peramban, masalah ekstensi peramban dan, lebih jarang, kesalahan kami dalam arsitektur kode.

Berikut ini contoh penyebab kebocoran memori saat menjalankan setInterval dalam konteks baru:

var
Context  = process.binding('evals').Context,
Script   = process.binding('evals').Script,
total    = 5000,
result   = null;

process.nextTick(function memory() {
  var mem = process.memoryUsage();
  console.log('rss:', Math.round(((mem.rss/1024)/1024)) + "MB");
  setTimeout(memory, 100);
});

console.log("STARTING");
process.nextTick(function run() {
  var context = new Context();

  context.setInterval = setInterval;

  Script.runInContext('setInterval(function() {}, 0);',
                      context, 'test.js');
  total--;
  if (total) {
    process.nextTick(run);
  } else {
    console.log("COMPLETE");
  }
});

0
2017-08-16 16:34