Rumah >hujung hadapan web >tutorial js >Mari kita bincangkan tentang pengurusan memori V8 dan algoritma pengumpulan sampah

Mari kita bincangkan tentang pengurusan memori V8 dan algoritma pengumpulan sampah

青灯夜游
青灯夜游ke hadapan
2022-04-27 20:44:483057semak imbas

Artikel ini akan memperkenalkan anda kepada pengurusan memori dan algoritma pengumpulan sampah enjin V8 saya harap ia akan membantu anda!

Mari kita bincangkan tentang pengurusan memori V8 dan algoritma pengumpulan sampah

Seperti yang kita sedia maklum, JS menguruskan kutipan sampah secara automatik dan pembangun tidak perlu mengambil berat tentang peruntukan memori dan kitar semula. Selain itu, mekanisme kutipan sampah juga merupakan bahagian yang biasa diuji dalam temu bual hadapan. Artikel ini menerangkan algoritma kutipan sampah generasi V8. Saya harap rakan-rakan selepas membaca artikel ini dapat memahami V8 (haha, itu 痛彻!!!) tentang 痛彻mekanisme pengumpulan sampah meliputi kandungan berikut : Had memori dan penyelesaian untuk

  • V8
  • Scavenge algoritma untuk objek memori generasi baharu
  • adalah berdasarkan logik 可达性分析算法 menanda objek hidup dan Pengoptimuman bermakna
  • Syarat promosi untuk objek memori generasi baharu,
  • Scavenge Perbezaan pertama dalam/keluasan algoritma
  • Tulis halangan untuk penjanaan silang ingatan
  • Tandakan algoritma pembersihan/penyusun untuk objek memori generasi lama
  • GCsebab STW dan strategi pengoptimuman

Had memori V8 dan penyelesaian

V8 pada asalnya direka untuk penyemak imbas, dan terdapat beberapa senario di mana memori besar digunakan Terdapat sekatan pada penggunaan memori secara lalai dalam reka bentuk, dan hanya sebahagian daripada memori dibenarkan digunakan. Sistem 64-bit boleh membenarkan penggunaan kira-kira 1.4g ingatan untuk sistem 32-bit. Seperti yang ditunjukkan dalam kod berikut, semak kaedah had memori enjin V8 bergantung dalam Node:

process.memoryUsage();

// 返回内存的使用量,单位字节
{
  rss: 22953984,
  // 申请的总的堆内存
  heapTotal: 9682944,
  // 已使用的堆内存
  heapUsed: 5290344,
  external: 9388
}

Mari kita bincangkan tentang pengurusan memori V8 dan algoritma pengumpulan sampah

V8Ada satu lagi sebab penting untuk mengehadkan penggunaan memori . , apabila memori timbunan terlalu besar, ia akan mengambil masa V8 lebih lama untuk melaksanakan kutipan sampah (1.5g hingga 50ms), dan kutipan sampah bukan tambahan akan mengambil masa yang lebih lama (1.5g hingga 1s) . Selepas menerangkan mekanisme kutipan sampah V8 nanti, saya percaya semua orang akan dapat mengaitkannya dengan lebih lanjut.

Walaupun enjin V8 mempunyai had penggunaan memori, kaedah mengubah suai had memori juga didedahkan, iaitu menambah parameter yang berkaitan apabila menghidupkan enjin V8 Kod berikut menunjukkan pengubahsuaian bergantung Node 🎜>Had memori enjin: V8

# 更改老生代的内存限制,单位mb
node --max-old-space-size=2048 index.js

# 更改新生代的内存限制,单位mb
node --max-semi-space-size=1024=64 index.js
Apa yang perlu diperhatikan di sini ialah sintaks memori generasi baharu telah ditukar kepada kaedah penulisan yang disebutkan di atas, dan unit itu telah juga ditukar daripada

kepada kb, yang lama Kaedah penulisan ialah mb Anda boleh menanyakan sintaks mengubah suai memori generasi baharu dalam persekitaran node --max-new-space-size semasa melalui arahan berikut: Node

.
node --v8-options | grep max

Mari kita bincangkan tentang pengurusan memori V8 dan algoritma pengumpulan sampah

Strategi kutipan sampah V8

Dalam evolusi sejarah mekanisme pengumpulan sampah automatik enjin, orang ramai mendapati bahawa tiada algoritma universal yang boleh menyelesaikan kutipan sampah dalam sebarang senario. Oleh itu, algoritma pengumpulan sampah moden membahagikan sampah memori kepada

generasi berdasarkan masa hidup objek Algoritma pengumpulan sampah generasi melaksanakan kaedah yang berbeza untuk jenis memori yang berbeza. algoritma kitar semula.

Bahagi memori kepada dua jenis: V8 dan 新生代: 老生代

    Objek dalam memori generasi baharu mempunyai masa hidup yang lebih singkat
  • Lama generasi Objek generasi dalam ingatan mempunyai masa hidup yang lama atau berada dalam ingatan
Memori generasi baharu disimpan dalam ruang memori generasi baharu (

), dan ingatan generasi lama disimpan dalam ruang memori generasi lama (semispace), seperti ditunjukkan dalam rajah di bawah: oldspace

Mari kita bincangkan tentang pengurusan memori V8 dan algoritma pengumpulan sampah

    Memori generasi baharu menggunakan algoritma
  • Scavenge
  • Memori generasi lama menggunakan
  • dan Mark-SweepAlgoritmaMark-Compact
Mari kita lihat logik algoritma

! Scavenge

Algoritma Scavenge

menggunakan algoritma

untuk kitar semula memori memori generasi baharu Pelaksanaan khusus Scavenge menggunakan algoritma Scavenge. Algoritma Cheney membahagikan ruang memori generasi baharu kepada dua, dengan satu ruang sedang digunakan (Cheney) dan satu ruang dalam keadaan bebas (dipanggil FromSpace). ToSpace

Mari kita bincangkan tentang pengurusan memori V8 dan algoritma pengumpulan sampah

在内存开始分配时,首先在FromSpace中进行分配,垃圾回收机制执行时会检查FromSpace中的存活对象,存活对象会被会被复制到ToSpace,非存活对象所占用的空间将被释放,复制完成后FromSpaceToSpace的角色将翻转。当一个对象多次复制后依然处于存活状态,则认为其是长期存活对象,此时将发生晋升,然后该对象被移动到老生代空间oldSpace中,采用新的算法进行管理。

Mari kita bincangkan tentang pengurusan memori V8 dan algoritma pengumpulan sampah

Scavenge算法其实就是在两个空间内来回复制存活对象,是典型的空间换时间做法,所以非常适合新生代内存,因为仅复制存活的对象且新生代内存中存活对象是占少数的。但是有如下几个重要问题需要考虑:

  • 引用避免重复拷贝

假设存在三个对象temp1、temp2、temp3,其中temp2、temp3都引用了temp1,js代码示例如下:

var temp2 = {
  ref: temp1,
}

var temp3 = {
  ref: temp1,
}

var temp1 = {}

FromSpace中拷贝temp2ToSpace中时,发现引用了temp1,便把temp1也拷贝到ToSpace,是一个递归的过程。但是在拷贝temp3时发现也引用了temp1,此时再把temp1拷贝过去则重复了。

要避免重复拷贝,做法是拷贝时给对象添加一个标记visited表示该节点已被访问过,后续通过visited属性判断是否拷贝对象。

  • 拷贝后保持正确的引用关系

还是上述引用关系,由于temp1不需要重复拷贝,temp3被拷贝到ToSpace之后不知道temp1对象在ToSpace中的内存地址。

做法是temp1被拷贝过去后该对象节点上会生成新的field属性指向新的内存空间地址,同时更新到旧内存对象的forwarding属性上,因此temp3就可以通过旧temp1forwarding属性找到在ToSpace中的引用地址了。

内存对象同时存在于新生代和老生代之后,也带来了问题:

  • 内存对象跨代(跨空间)后如何标记
const temp1 = {}

const temp2 = {
  ref: temp1,
}

比如上述代码中的两个对象temp1temp2都存在于新生代,其中temp2引用了temp1。假设在经过GC之后temp2晋升到了老生代,那么在下次GC的标记阶段,如何判断temp1是否是存活对象呢?

在基于可达性分析算法中要知道temp1是否存活,就必须要知道是否有根对象引用引用了temp1对象。如此的话,年轻代的GC就要遍历所有的老生代对象判断是否有根引用对象引用了temp1对象,如此的话分代算法就没有意义了。

解决版本就是维护一个记录所有的跨代引用的记录集,它是写缓冲区的一个列表。只要有老生代中的内存对象指向了新生代内存对象时,就将老生代中该对象的内存引用记录到记录集中。由于这种情况一般发生在对象写的操作,顾称此为写屏障,还一种可能的情况就是发生在晋升时。记录集的维护只要关心对象的写操作和晋升操作即可。此是又带来了另一个问题:

  • 每次写操作时维护记录集的额外开销

优化的手段是在一些Crankshaft操作中是不需要写屏障的,还有就是栈上内存对象的写操作是不需要写屏障的。还有一些,更多的手段就不在这里过多讨论。

  • 缓解Scavenge算法内存利用率不高问题

新生代内存中存活对象占比是相对较小的,因此可以在分配空间时,ToSpace可以分配的小一些。做法是将ToSpace空间分成S0S1两部分,S0用作于ToSpaceS1与原FromSpace合并当成FromSpace

Mari kita bincangkan tentang pengurusan memori V8 dan algoritma pengumpulan sampah

Scavenge算法中深度/广度优先的区别

垃圾回收算法中,识别内存对象是否是垃圾的机制一般有两种:引用计数基于可达性分析

Berdasarkan analisis kebolehcapaian, ia adalah untuk mencari semua rujukan akar (seperti pembolehubah global, dll.), merentasi semua rujukan akar dan semua rujukan pada rujukan akar rekursif Semua yang dilalui adalah Objek hidup dan tandainya Pada masa ini, objek ingatan lain dalam ruang adalah objek mati, dengan itu membina graf terarah.

Memandangkan pengehadan rekursif, logik rekursif secara amnya menggunakan pelaksanaan bukan rekursif yang biasa termasuk algoritma keluasan-didahulukan dan kedalaman-didahulukan. Perbezaan antara keduanya ialah:

  • menukar susunan objek ingatan apabila menyalin depth-first kepada ToSpace, menjadikan objek dengan hubungan rujukan lebih rapat. Sebabnya ialah selepas menyalin dirinya sendiri, objek yang dirujuk dengan sendirinya disalin secara langsung, jadi objek yang berkaitan lebih rapat bersama dalam ToSpace
  • Keutamaan mendalam adalah sebaliknya

kerana daripada Strategi cache CPU, apabila membaca objek memori, terdapat kebarangkalian tinggi bahawa objek di belakangnya akan dibaca bersama, untuk memukul cache dengan lebih cepat. Kerana senario yang sangat biasa semasa pembangunan kod ialah obj1.obj2.obj3 Apabila CPU membaca obj1, jika ia membaca obj2 dan obj3 berikut bersama-sama, ia akan sangat membantu untuk menekan cache.

Jadi algoritma depth-first adalah lebih kondusif untuk logik perniagaan mencecah cache, tetapi pelaksanaannya memerlukan pelaksanaan algoritme berbantu tindanan tambahan, yang menggunakan ruang memori. Breadth terlebih dahulu, sebaliknya, tidak dapat meningkatkan hits cache, tetapi pelaksanaannya boleh menggunakan penunjuk untuk mengelakkan penggunaan ruang dengan bijak, dan kecekapan pelaksanaan algoritma adalah tinggi.

Syarat promosi untuk objek memori generasi baharu

Jika objek memori dalam generasi baharu ingin dinaikkan pangkat kepada generasi lama, mereka perlu memenuhi syarat berikut:

  • Sama ada objek telah mengalami Scavengekitar semula
  • ToSpacenisbah penggunaan memori tidak boleh melebihi had

Logik untuk menentukan sama ada ia telah mengalami GC Scavenge ialah, Setiap kali GC diberikan kepada atribut age 1 objek yang masih hidup, dan apabila ia GC sekali lagi, nilaikan atribut age. Gambar rajah promosi asas adalah seperti berikut:

Mari kita bincangkan tentang pengurusan memori V8 dan algoritma pengumpulan sampah

Terdapat banyak objek jangka panjang yang masih hidup dalam ingatan generasi lama, dan sebab mengapa algoritma Scavenge tidak boleh dikitar semula ialah :

  • Lebih banyak objek yang masih hidup membawa kepada kecekapan penyalinan yang rendah
  • Membazir separuh daripada ruang memori

Algoritma kitar semula untuk objek memori generasi lama

Pengumpulan sampah ruang memori generasi lama menggunakan gabungan 标记清除 (Mark-Sweep) dan 标记整理 (Mark-Compact). Menanda dan mengosongkan dibahagikan kepada dua bahagian:

  • Fasa penandaan
  • Fasa pembersihan (jika menanda dan mengisih, ia adalah fasa pengisihan)

Melintasi pelajar lama dalam fasa penandaan Semua objek memori dalam memori timbunan dijana dan objek hidup ditanda Fasa pembersihan hanya membersihkan objek yang tidak ditanda. Sebabnya ialah: objek bukan hidup menyumbang minoriti dalam ingatan generasi lama.

Mari kita bincangkan tentang pengurusan memori V8 dan algoritma pengumpulan sampah

Seperti yang ditunjukkan dalam rajah di atas, satu masalah dengan pembersihan tanda ialah selepas pembersihan, terdapat ruang terputus yang tidak boleh digunakan lagi, jadi pembersihan memori memori generasi lama ruang diperlukan Penyelesaian yang digabungkan dengan penandaan. Penyelesaian ini adalah untuk mengalihkan objek hidup ke satu sisi semasa proses penandaan, dan kemudian membersihkan dan mengalih keluar semua objek bukan hidup di luar sempadan selepas pergerakan selesai.

Mari kita bincangkan tentang pengurusan memori V8 dan algoritma pengumpulan sampah

Jeda penuh kutipan sampah

Perlu menjeda logik pelaksanaan aplikasi semasa kutipan sampah, dan kemudian menyambung semula aplikasi selepas mekanisme kutipan sampah selesai Logik pelaksanaan, tingkah laku ini dipanggil "Jeda Penuh", yang sering dipanggil Stop The World, atau STW singkatannya. Pengumpulan sampah memori generasi muda mempunyai sedikit kesan ke atas pelaksanaan aplikasi, tetapi kerana terdapat banyak objek yang masih hidup dalam memori generasi lama, kesan jeda penuh yang disebabkan oleh kutipan sampah memori generasi lama adalah sangat besar.

Mari kita bincangkan tentang pengurusan memori V8 dan algoritma pengumpulan sampah

Untuk mengoptimumkan masa jeda penuh GC, V8 turut memperkenalkan 增量标记, 并发标记, 并行标记, 增量整理, 并行清理, 延迟清理 dsb.

Pengoptimuman STW

Metrik penting untuk mengukur masa yang dihabiskan dalam kutipan sampah ialah jumlah masa utas utama dijeda semasa melaksanakan GC. Kesan STW tidak boleh diterima, jadi V8 juga telah menggunakan banyak kaedah pengoptimuman.

  • GC Selari

Proses GC perlu melakukan banyak perkara, yang akan membawa kepada fenomena STW pada utas utama Kaedah GC selari adalah dengan membuka pelbagai tambahan benang untuk berkongsi kerja GC. Pendekatan ini masih tidak dapat mengelakkan fenomena STW, tetapi ia boleh mengurangkan jumlah masa STW, bergantung pada bilangan utas tambahan yang didayakan.

1Mari kita bincangkan tentang pengurusan memori V8 dan algoritma pengumpulan sampah

  • Tambahan GC

GC Inkremental membahagikan kerja GC dan mengedarkannya secara berselang-seli dalam pelaksanaan langkah urutan utama. Pendekatan ini tidak akan mengurangkan masa GC Sebaliknya, ia akan menelan kos sedikit, tetapi ia juga akan mengurangkan jumlah masa STW GC.

1Mari kita bincangkan tentang pengurusan memori V8 dan algoritma pengumpulan sampah

  • Concurrency GC

Concurrent GC bermaksud GC berjalan di latar belakang dan tidak lagi berjalan pada utas utama. Pendekatan ini akan mengelakkan fenomena STW. Paparan animasi dalam

1Mari kita bincangkan tentang pengurusan memori V8 dan algoritma pengumpulan sampah

  • masa melahu GC

Chrome adalah lebih kurang 60 bingkai (setiap bingkai adalah lebih kurang 16ms), jika masa pemaparan semasa mencapai 16.6ms, akan ada masa terluang untuk melakukan perkara lain, seperti beberapa GC tugasan.

1Mari kita bincangkan tentang pengurusan memori V8 dan algoritma pengumpulan sampah

Kurangkan kesan kutipan sampah

Untuk meningkatkan kecekapan pelaksanaan, meminimumkan pelaksanaan dan penggunaan kutipan sampah:

  • Berhati-hati apabila menggunakan memori sebagai cache, dan berhati-hati menggunakan objek sebagai cache Untuk mengehadkan masa tamat tempoh dan isu pertumbuhan yang tidak terhingga secara munasabah, anda boleh menggunakan strategi lru

  • Elakkan menggunakan memori untuk menyimpan sesi pengguna Jika tidak, menyimpan sejumlah besar objek sesi pengguna dalam memori akan menyebabkan memori generasi lama melonjak, menjejaskan prestasi pembersihan dan dengan itu menjejaskan pelaksanaan aplikasi. prestasi dan limpahan ingatan. Cara yang lebih baik untuk menggunakan redis, dsb. Faedah mengalihkan cache secara luaran: Node

      Mengurangkan bilangan objek memori pemastautin dan menjadikan kutipan sampah lebih cekap
    • Cache boleh dikongsi antara proses
Untuk lebih banyak pengetahuan berkaitan nod, sila lawati:

tutorial nodejs!

Atas ialah kandungan terperinci Mari kita bincangkan tentang pengurusan memori V8 dan algoritma pengumpulan sampah. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Artikel ini dikembalikan pada:juejin.cn. Jika ada pelanggaran, sila hubungi admin@php.cn Padam