Saat browser kehabisan memori, mereka membongkar tab tertua darinya. Ini menjengkelkan karena mengklik tab semacam itu memaksa memuat ulang halaman. Hari ini kami akan memberi tahu para pembaca Habr bagaimana tim Yandex.Browser memecahkan masalah ini menggunakan teknologi Hibernate.
Peramban berbasis kromium membuat proses untuk setiap tab. Pendekatan ini memiliki banyak keunggulan. Ini adalah keamanan (isolasi situs dari satu sama lain), stabilitas (crash satu proses tidak menyeret seluruh browser), dan akselerasi pekerjaan pada prosesor modern dengan sejumlah besar core. Tetapi ada juga minus - konsumsi memori yang lebih tinggi daripada saat menggunakan satu proses untuk semuanya. Jika browser tidak melakukan apa pun dengan ini, maka pengguna mereka akan terus melihat sesuatu seperti ini:

Dalam proyek Chromium, mereka berjuang dengan konsumsi memori tab latar belakang dengan menghapus berbagai cache. Ini bukan tentang cache di mana gambar dari halaman yang dimuat disimpan. Tidak ada masalah dengan dia - dia hidup di hard drive. Di peramban modern, ada banyak informasi cache lainnya yang disimpan dalam RAM.
Selain itu, Chromium telah bekerja cukup lama untuk menghentikan timer JS di tab latar belakang. Kalau tidak, membersihkan cache tidak masuk akal, karena kegiatan di tab latar belakang mengembalikannya. Dipercayai bahwa jika situs ingin bekerja di latar belakang, maka Anda perlu menggunakan pekerja layanan, bukan timer.
Jika langkah-langkah ini tidak membantu, maka hanya ada satu hal yang tersisa - untuk membongkar seluruh proses rendering tab dari memori. Situs terbuka tidak lagi ada. Jika Anda beralih ke tab, itu mulai mengunduh dari jaringan. Jika ada video jeda di tab, itu akan mulai diputar dari awal. Jika formulir diisi pada halaman, maka informasi yang dimasukkan dapat hilang. Jika aplikasi JS berat bekerja di tab, Anda harus memulainya lagi.
Masalah membongkar tab sangat tidak menyenangkan ketika tidak ada akses ke jaringan. Sudahkah Anda menunda tab dengan Habr untuk membaca di pesawat terbang? Bersiaplah bahwa artikel yang bermanfaat akan berubah menjadi labu.
Pengembang browser memahami bahwa tindakan ekstrem ini menjengkelkan bagi pengguna (cukup
buka pencarian untuk memperkirakan luasnya), sehingga mereka menerapkannya pada saat terakhir. Pada titik ini, komputer sudah mulai melambat karena kurangnya memori, pengguna memerhatikan hal ini dan mencari cara alternatif untuk menyelesaikan masalah, oleh karena itu, misalnya, ekstensi
The Great Suspender memiliki lebih dari 1,4 juta pengguna.
Orang-orang ingin browser dan memori disimpan, dan mereka tidak mulai melambat. Untuk melakukan ini, tab tidak boleh diturunkan pada saat terakhir, tetapi sedikit lebih awal. Dan untuk ini, Anda harus berhenti kehilangan konten tab, mis. membuat proses penyimpanan tidak terlihat. Tapi lalu apa yang harus dihemat? Lingkaran ditutup. Tapi solusinya ditemukan.
Hibernasi di Yandex Browser
Banyak pembaca Habr sudah bisa menebak apa yang harus menghapus memori, tetapi untuk menyimpan keadaan tab sangat mungkin jika Anda pertama kali membongkar keadaan pada hard drive. Jika Anda mengklik tab untuk memulihkan tab dari hard drive, pengguna tidak akan melihat apa pun.
Tim kami terlibat dalam pengembangan proyek Chromium, di mana ia mengirimkan
pengeditan yang signifikan dan
fitur -
fitur baru. Kembali pada tahun 2015, kami
berdiskusi dengan rekan-rekan dari proyek mengenai gagasan mempertahankan status tab pada hard drive dan bahkan berhasil membuat sejumlah perbaikan, tetapi mereka memutuskan untuk membekukan area ini dalam Chromium. Kami memutuskan secara berbeda dan melanjutkan pengembangan di Yandex.Browser. Butuh lebih banyak waktu daripada yang direncanakan, tetapi itu sepadan. Di bawah ini kita akan berbicara tentang isian teknis teknologi Hibernate, tetapi untuk sekarang, mari kita mulai dengan logika umum.
Beberapa kali dalam satu menit, Yandex.Browser memeriksa jumlah memori yang tersedia, dan jika kurang dari nilai ambang batas 600 megabita, maka Hibernate ikut berperan. Semuanya dimulai dengan fakta bahwa Browser menemukan tab latar belakang tertua (untuk digunakan). Omong-omong, rata-rata pengguna membuka 7 tab, tetapi 5% memiliki lebih dari 30.
Anda tidak dapat melepas tab lama dari memori - Anda dapat merusak sesuatu yang sangat penting. Misalnya, memainkan musik atau mengobrol di messenger web. Ada 28 pengecualian seperti itu sekarang.Jika tab tidak cocok dengan setidaknya satu di antaranya, maka Browser melanjutkan untuk memeriksa yang berikutnya.
Jika tab ditemukan yang memenuhi persyaratan, maka proses menyimpannya dimulai.
Menyimpan dan mengembalikan tab di Hibernate
Setiap halaman dapat dibagi menjadi dua bagian besar, yang terkait dengan mesin V8 (JS) dan Blink (HTML / DOM). Pertimbangkan sebuah contoh kecil:
<html> <head> <script type="text/javascript"> function onLoad() { var div = document.createElement("div"); div.textContent = "Look ma, I can set div text"; document.body.appendChild(div); } </script> </head> <body onload="onLoad()"></body> </html>
Kami memiliki beberapa pohon DOM dan skrip kecil yang hanya menambahkan div ke tubuh. Dari sudut pandang Blink, halaman ini terlihat seperti ini:

Mari kita lihat hubungan antara Blink dan V8 menggunakan contoh HTMLBodyElement:

Anda mungkin memperhatikan bahwa Blink dan V8 memiliki representasi berbeda dari entitas yang sama dan terkait erat satu sama lain. Jadi kami sampai pada ide asli - untuk menyimpan keadaan penuh V8, dan untuk Blink hanya menyimpan atribut HTML dalam bentuk teks. Tapi ini kesalahan, karena kami kehilangan status objek DOM yang tidak disimpan dalam atribut. Kami juga kehilangan status yang tidak disimpan di DOM. Solusi untuk masalah ini adalah menyelamatkan Blink sepenuhnya. Tapi tidak sesederhana itu.
Pertama, Anda perlu mengumpulkan informasi tentang objek Blink. Oleh karena itu, pada saat menyimpan V8, kami tidak hanya menghentikan JS dan melemparkannya, tetapi juga mengumpulkan referensi ke objek DOM dan objek tambahan lainnya yang tersedia untuk JS dalam memori. Kami juga menelusuri semua objek yang dapat dijangkau dari objek Dokumen - elemen root dari setiap bingkai halaman. Jadi kami mengumpulkan informasi tentang segala hal yang penting untuk dilestarikan. Bagian tersulit adalah belajar menabung.
Jika kami menghitung semua kelas Blink yang mewakili pohon DOM, serta API HTML5 yang berbeda (misalnya, kanvas, media, geolokasi), kami mendapatkan ribuan kelas. Hampir tidak mungkin untuk menulis logika menyelamatkan semua kelas dengan tangan Anda. Tetapi bagian terburuknya adalah bahwa bahkan jika Anda melakukan ini, itu tidak mungkin dipertahankan, karena kami secara teratur memperbarui versi baru Chromium yang membuat perubahan tak terduga ke kelas mana pun.
Browser kami untuk semua platform dibangun menggunakan dentang. Untuk mengatasi masalah melestarikan kelas Blink, kami membuat plugin untuk dentang, yang membangun AST (pohon sintaksis abstrak) untuk kelas. Misalnya, kode ini:
Kode kelas class Bar : public foo_namespace::Foo { struct BarInternal { int int_field_; float float_field_; } bar_internal_field_; std::string string_field_; };
Itu berubah menjadi XML seperti itu:
Hasil plugin dalam XML <class> <name>bar_namespace::Bar::BarInternal</name> <is_union>false</is_union> <is_abstract>false</is_abstract> <decl_source_file>src/bar.h</decl_source_file> <base_class_names></base_class_names> <fields> <field> <name>int_field_</name> <type> <builtin> <is_const>0</is_const> <name>int</name> </builtin> </type> </field> <field> <name>float_field_</name> <type> <builtin> <is_const>0</is_const> <name>float</name> </builtin> </type> </field> </class> <class> <name>bar_namespace::Bar</name> <is_union>false</is_union> <is_abstract>false</is_abstract> <decl_source_file>src/bar.h</decl_source_file> <base_class_names> <class_name>foo_namespace::Foo</class_name> </base_class_names> <fields> <field> <name>bar_internal_field_</name> <type> <class> <is_const>0</is_const> <name>bar_namespace::Bar::BarInternal</name> </class> </type> </field> <field> <name>string_field_</name> <type> <class> <is_const>0</is_const> <name>std::string</name> </class> </type> </field> </fields> </class>
Selanjutnya, skrip lain yang ditulis oleh kami menghasilkan kode C ++ dari informasi ini untuk menyimpan dan memulihkan kelas, yang termasuk dalam perakitan Yandex.Browser.
C ++ menyimpan kode yang diperoleh dengan skrip dari XML void serialize_bar_namespace_Bar_BarInternal( WriteVisitor* writer, Bar::BarInternal* instance) { writer->WriteBuiltin<size_t>(instance->int_vector_field_.size()); for (auto& item : instance->int_vector_field_) { writer->WriteBuiltin<int>(item); } writer->WriteBuiltin<float>(instance->float_field_); } void serialize_bar_namespace_Bar(WriteVisitor* writer, Bar* instance) { serialize_foo_namespace_Foo(writer, instance); serialize_bar_namespace_Bar_BarInternal( writer, &instance->bar_internal_field_); writer->WriteString(instance->string_field_); }
Secara total, kami menghasilkan kode untuk sekitar 1000 kelas Blink. Sebagai contoh, kami belajar untuk menyelamatkan kelas yang kompleks seperti Canvas. Anda bisa menggambar darinya dalam kode JS, mengatur banyak properti, mengatur parameter kuas untuk menggambar, dan sebagainya. Kami menyimpan semua properti, parameter, dan gambar itu sendiri.
Setelah berhasil mengenkripsi dan menyimpan semua data ke hard disk, proses tab diturunkan dari memori hingga pengguna kembali ke tab ini. Di antarmuka, seperti sebelumnya, itu tidak menonjol.
Pemulihan tab tidak instan, tetapi jauh lebih cepat daripada saat mengunduh dari jaringan. Namun demikian, kami melakukan langkah yang rumit agar tidak mengganggu pengguna dengan kilasan layar putih. Kami menampilkan tangkapan layar dari halaman yang dibuat selama fase simpan. Ini membantu kelancaran transisi. Kalau tidak, proses pemulihan mirip dengan navigasi normal dengan satu-satunya perbedaan adalah bahwa browser tidak membuat permintaan jaringan. Ini menciptakan struktur bingkai dan pohon DOM di dalamnya, dan kemudian menggantikan keadaan V8.
Kami merekam video dengan demonstrasi yang jelas tentang bagaimana Hibernate membongkar dan mengembalikan tab klik sambil menjaga kemajuan dalam permainan JS yang dimasukkan dalam posisi teks dan video:
Ringkasan
Dalam waktu dekat, teknologi Hibernate akan tersedia untuk semua pengguna Yandex.Browser untuk Windows. Kami juga berencana untuk mulai bereksperimen dengan itu dalam versi alpha untuk Android. Dengan itu, Browser menghemat memori lebih efisien dari sebelumnya. Misalnya, untuk pengguna dengan sejumlah besar tab terbuka, Hibernate menyimpan rata-rata lebih dari 330 megabita memori dan tidak kehilangan informasi di tab, yang tetap dapat diakses dalam satu klik di bawah kondisi jaringan apa pun. Kami memahami bahwa akan bermanfaat bagi webmaster untuk mempertimbangkan membongkar tab latar belakang, jadi kami berencana untuk mendukung
API Siklus Hidup Halaman .
Hibernate bukan satu-satunya solusi kami yang bertujuan menghemat sumber daya. Ini bukan tahun pertama kami bekerja untuk memastikan bahwa Browser beradaptasi dengan sumber daya yang tersedia di sistem. Misalnya, pada perangkat yang lemah, Browser memasuki mode yang disederhanakan, dan ketika laptop terputus dari sumber daya, itu mengurangi konsumsi daya. Menyimpan sumber daya adalah cerita besar dan rumit, yang mana kita pasti akan kembali ke Habré.