Saya melihat beberapa posting di web orang-orang yang kelihatannya mengeluhkan VPS yang di-host secara tidak sengaja membunuh proses karena mereka menggunakan terlalu banyak RAM.
Bagaimana ini mungkin? Saya pikir semua OS modern menyediakan "infinite RAM" dengan hanya menggunakan disk swap untuk apa pun yang melebihi RAM fisik. Apakah ini benar?
Apa yang mungkin terjadi jika suatu proses "mati karena RAM rendah"?
Jawaban:
Terkadang dikatakan bahwa linux secara default tidak pernah menolak permintaan untuk lebih banyak memori dari kode aplikasi - mis
malloc()
. 1 Ini sebenarnya tidak benar; default menggunakan heuristik dimanaDari
[linux_src]/Documentation/vm/overcommit-accounting
(semua kutipan berasal dari pohon 3.11). Tepatnya apa yang dianggap sebagai "alokasi liar yang serius" tidak dibuat eksplisit, jadi kami harus melalui sumber untuk menentukan detailnya. Kita juga dapat menggunakan metode eksperimental pada catatan kaki 2 (di bawah) untuk mencoba dan mendapatkan beberapa refleksi heuristik - berdasarkan itu, pengamatan empiris awal saya adalah bahwa dalam keadaan ideal (== sistem idle), jika Anda tidak t memiliki swap apa pun, Anda akan diizinkan untuk mengalokasikan sekitar setengah RAM Anda, dan jika Anda memiliki swap, Anda akan mendapatkan sekitar setengah RAM Anda ditambah semua swap Anda. Itu lebih atau kurang per proses (tetapi perhatikan batas ini dinamis dan dapat berubah karena keadaan, lihat beberapa pengamatan di catatan kaki 5).Setengah RAM Anda plus swap secara eksplisit merupakan standar untuk bidang "CommitLimit" di
/proc/meminfo
. Inilah artinya - dan perhatikan itu sebenarnya tidak ada hubungannya dengan batas yang baru saja didiskusikan (dari[src]/Documentation/filesystems/proc.txt
):Doc-akuntansi overcommit yang dikutip sebelumnya menyatakan bahwa standarnya
vm.overcommit_ratio
adalah 50. Jadi jika Andasysctl vm.overcommit_memory=2
, Anda dapat menyesuaikan vm.covercommit_ratio (withsysctl
) dan melihat konsekuensinya. 3 Mode default, ketikaCommitLimit
tidak ditegakkan dan hanya "ruang alamat overcommits jelas ditolak", adalah ketikavm.overcommit_memory=0
.Sementara strategi default memang memiliki batas heuristik per-proses yang mencegah "alokasi liar yang serius", itu membuat sistem secara keseluruhan bebas untuk menjadi liar, alokasi yang bijak. 4 Ini berarti pada titik tertentu kehabisan memori dan harus menyatakan kebangkrutan untuk beberapa proses melalui pembunuh OOM .
Apa yang dibunuh pembunuh OOM? Belum tentu proses yang meminta memori ketika tidak ada, karena itu belum tentu proses yang benar-benar bersalah, dan yang lebih penting, belum tentu proses yang paling cepat akan mengeluarkan sistem dari masalah yang ada.
Ini dikutip dari sini yang mungkin mengutip sumber 2.6.x:
Yang sepertinya alasan yang layak. Namun, tanpa mendapatkan forensik, # 5 (yang berlebihan dari # 1) tampaknya seperti implementasi penjualan yang sulit, dan # 3 berlebihan dari # 2. Jadi mungkin masuk akal untuk mempertimbangkan penurunan ini ke # 2/3 dan # 4.
Saya memahami sebuah sumber baru-baru ini (3.11) dan memperhatikan bahwa komentar ini telah berubah untuk sementara:
Ini sedikit lebih eksplisit tentang # 2: "Tujuannya adalah untuk [membunuh] tugas yang menghabiskan sebagian besar memori untuk menghindari kegagalan berikutnya," dan dengan implikasi # 4 ( "kami ingin membunuh jumlah minimum proses ( satu ) ) .
Jika Anda ingin melihat pembunuh OOM beraksi, lihat catatan kaki 5.
1 Khayalan Gilles untungnya membebaskan saya dari, lihat komentar.
2 Inilah sedikit C yang langsung yang meminta potongan memori yang semakin besar untuk menentukan kapan permintaan untuk lebih banyak akan gagal:
Jika Anda tidak tahu C, Anda bisa mengkompilasi ini
gcc virtlimitcheck.c -o virtlimitcheck
, lalu jalankan./virtlimitcheck
. Ini benar-benar tidak berbahaya, karena prosesnya tidak menggunakan ruang yang diminta - yaitu, ia tidak pernah benar-benar menggunakan RAM apa pun.Pada sistem 3.11 x86_64 dengan sistem 4 GB dan 6 GB swap, saya gagal pada ~ 7400000 kB; jumlahnya berfluktuasi, jadi mungkin keadaan adalah faktor. Ini kebetulan dekat dengan
CommitLimit
di/proc/meminfo
, tetapi memodifikasi ini melaluivm.overcommit_ratio
tidak ada bedanya. Pada sistem 3.6M 32-bit ARM 448 MB dengan 64 MB swap, bagaimanapun, saya gagal ~ 230 MB. Ini menarik karena dalam kasus pertama jumlahnya hampir dua kali lipat jumlah RAM, sedangkan pada yang kedua adalah sekitar 1/4 itu - sangat menyiratkan jumlah swap adalah faktor. Ini dikonfirmasi dengan mematikan swap pada sistem pertama, ketika ambang kegagalan turun ke ~ 1,95 GB, rasio yang sangat mirip dengan kotak ARM kecil.Tetapi apakah ini benar-benar per proses? Tampaknya begitu. Program singkat di bawah ini meminta sepotong memori yang ditentukan pengguna, dan jika berhasil, menunggu Anda untuk kembali - dengan cara ini Anda dapat mencoba beberapa contoh bersamaan:
Berhati-hatilah, bagaimanapun, bahwa ini bukan semata-mata tentang jumlah RAM dan swap terlepas dari penggunaan - lihat catatan kaki 5 untuk pengamatan tentang efek dari status sistem.
3
CommitLimit
mengacu pada jumlah ruang alamat yang diizinkan untuk sistem ketika vm.overcommit_memory = 2. Agaknya, jumlah yang dapat Anda alokasikan harus dikurangi dengan apa yang sudah dilakukan, yang tampaknya adalahCommitted_AS
bidang.Eksperimen yang berpotensi menarik yang menunjukkan ini adalah untuk menambah
#include <unistd.h>
bagian atas virtlimitcheck.c (lihat catatan kaki 2), danfork()
tepat sebelumwhile()
perulangan. Itu tidak dijamin berfungsi seperti yang dijelaskan di sini tanpa sinkronisasi yang melelahkan, tetapi ada kemungkinan yang layak, YMMV:Ini masuk akal - melihat tmp.txt secara rinci Anda dapat melihat proses berganti alokasi yang lebih besar dan lebih besar (ini lebih mudah jika Anda melemparkan pid ke dalam output) sampai satu, jelas, telah mengklaim cukup bahwa yang lain gagal. Pemenang kemudian bebas untuk mengambil semuanya hingga
CommitLimit
minusCommitted_AS
.4 Perlu disebutkan, pada titik ini, jika Anda belum memahami pengalamatan virtual dan paging permintaan, bahwa yang membuat komitmen lebih mungkin di tempat pertama adalah bahwa apa yang dialokasikan kernel untuk proseslandland bukanlah memori fisik sama sekali - itu adalah ruang alamat virtual . Misalnya, jika suatu proses menyimpan 10 MB untuk sesuatu, itu ditata sebagai urutan alamat (virtual), tetapi alamat tersebut belum sesuai dengan memori fisik. Ketika alamat tersebut diakses, ini menghasilkan kesalahan halamandan kemudian kernel mencoba untuk memetakannya ke memori nyata sehingga dapat menyimpan nilai nyata. Proses biasanya menyimpan lebih banyak ruang virtual daripada yang sebenarnya mereka akses, yang memungkinkan kernel membuat penggunaan RAM yang paling efisien. Namun, memori fisik masih merupakan sumber daya yang terbatas dan ketika semuanya telah dipetakan ke ruang alamat virtual, beberapa ruang alamat virtual harus dihilangkan untuk membebaskan beberapa RAM.
5 Pertama peringatan : Jika Anda mencoba ini
vm.overcommit_memory=0
, pastikan Anda menyimpan pekerjaan Anda terlebih dahulu dan menutup semua aplikasi penting, karena sistem akan dibekukan selama ~ 90 detik dan beberapa proses akan mati!Idenya adalah untuk menjalankan bom garpu yang keluar setelah 90 detik, dengan garpu mengalokasikan ruang dan beberapa dari mereka menulis sejumlah besar data ke RAM, sambil melaporkan ke stderr.
Kompilasi ini
gcc forkbomb.c -o forkbomb
. Pertama, coba dengansysctl vm.overcommit_memory=2
- Anda mungkin akan mendapatkan sesuatu seperti:Di lingkungan ini, bom fork semacam ini tidak terlalu jauh. Perhatikan bahwa angka dalam "kata N garpu" bukan jumlah total proses, itu adalah jumlah proses dalam rantai / cabang yang mengarah ke yang itu.
Sekarang coba dengan
vm.overcommit_memory=0
. Jika Anda mengarahkan stderr ke file, Anda dapat melakukan beberapa analisis kasar sesudahnya, misalnya:Hanya 15 proses gagal mengalokasikan 1 GB - menunjukkan bahwa heuristik untuk overcommit_memory = 0 adalah dipengaruhi oleh negara. Berapa banyak proses yang ada di sana? Melihat akhir tmp.txt, mungkin> 100.000. Sekarang bagaimana sebenarnya bisa menggunakan 1 GB?
Delapan - yang lagi-lagi masuk akal, karena pada saat itu saya punya ~ 3 GB RAM gratis dan 6 GB swap.
Lihatlah log sistem Anda setelah Anda melakukan ini. Anda harus melihat skor pelaporan pembunuh OOM (antara lain); mungkin ini berhubungan dengan
oom_badness
.sumber
Ini tidak akan terjadi pada Anda jika Anda hanya memuat 1G data ke dalam memori. Bagaimana jika Anda memuat lebih banyak? Sebagai contoh, saya sering bekerja dengan file besar yang berisi jutaan probabilitas yang perlu dimuat ke R. Ini membutuhkan sekitar 16GB RAM.
Menjalankan proses di atas pada laptop saya akan menyebabkannya mulai bertukar gila-gilaan segera setelah 8GB RAM saya telah terisi. Itu, pada gilirannya, akan memperlambat semuanya karena membaca dari disk jauh lebih lambat daripada membaca dari RAM. Bagaimana jika saya memiliki laptop dengan 2GB RAM dan hanya 10GB ruang kosong? Setelah proses mengambil semua RAM, itu juga akan mengisi disk karena sedang menulis untuk swap dan saya tidak memiliki RAM lagi dan tidak ada lagi ruang untuk swap (orang cenderung membatasi swap ke partisi khusus daripada sebuah swapfile untuk alasan itu). Di situlah pembunuh OOM masuk dan mulai membunuh proses.
Jadi, sistem memang bisa kehabisan memori. Selain itu, sistem swapping yang berat dapat menjadi tidak dapat digunakan jauh sebelum ini terjadi hanya karena operasi I / O yang lambat karena swapping. Orang umumnya ingin menghindari bertukar sebanyak mungkin. Bahkan pada server high-end dengan SSD cepat ada penurunan kinerja yang jelas. Di laptop saya, yang memiliki drive 7200RPM klasik, setiap pertukaran signifikan pada dasarnya membuat sistem tidak dapat digunakan. Semakin banyak swap, semakin lambat. Jika saya tidak membunuh proses pelanggaran dengan cepat semuanya hang sampai pembunuh OOM masuk
sumber
Proses tidak terbunuh ketika tidak ada lagi RAM, mereka terbunuh ketika mereka telah ditipu dengan cara ini:
Ini mungkin terjadi bahkan ketika sistem tidak bertukar aktif, misalnya jika area swap diisi dengan halaman memori daemon yang tertidur.
Ini tidak pernah terjadi pada OS yang tidak kehabisan memori. Dengan mereka tidak ada proses acak yang terbunuh tetapi proses pertama yang meminta memori virtual saat itu habis memiliki malloc (atau yang serupa) kembali dalam kesalahan. Oleh karena itu diberikan kesempatan untuk menangani situasi dengan tepat. Namun, pada OS-OS ini, bisa juga terjadi sistem kehabisan memori virtual sementara masih ada RAM bebas, yang cukup membingungkan dan umumnya disalahpahami.
sumber
Ketika RAM yang tersedia habis, kernel mulai menukar bit pemrosesan ke disk. Sebenarnya, kernel mulai bertukar ketika RAM hampir habis: kernel mulai bertukar secara proaktif saat momen idle, sehingga lebih responsif jika aplikasi tiba-tiba membutuhkan lebih banyak memori.
Perhatikan bahwa RAM tidak hanya digunakan untuk menyimpan memori proses. Pada sistem sehat yang khas, hanya sekitar setengah RAM yang digunakan oleh proses, dan setengah lainnya digunakan untuk cache disk dan buffer. Ini memberikan keseimbangan yang baik antara proses yang berjalan dan input / output file.
Ruang swap tidak terbatas. Pada titik tertentu, jika proses terus mengalokasikan semakin banyak memori, data spillover dari RAM akan mengisi swap. Ketika itu terjadi, proses yang mencoba untuk meminta lebih banyak memori melihat permintaan mereka ditolak.
Secara default, Linux overcommits memory. Ini berarti bahwa kadang-kadang akan memungkinkan proses untuk berjalan dengan memori yang telah dicadangkan, tetapi tidak digunakan. Alasan utama untuk memiliki komitmen berlebihan adalah cara forking bekerja. Ketika suatu proses meluncurkan subproses, proses anak secara konseptual beroperasi dalam replika memori orangtua - kedua proses awalnya memiliki memori dengan konten yang sama, tetapi konten tersebut akan berbeda ketika proses membuat perubahan masing-masing di ruang mereka sendiri. Untuk mengimplementasikan ini sepenuhnya, kernel harus menyalin semua memori induknya. Ini akan membuat forking lambat, sehingga kernel mempraktikkan copy-on-write: pada awalnya, anak berbagi semua ingatannya dengan orang tua; setiap kali salah satu proses menulis ke halaman yang dibagikan, kernel membuat salinan halaman itu untuk memecah berbagi.
Seringkali seorang anak akan meninggalkan banyak halaman tanpa tersentuh. Jika kernel mengalokasikan cukup memori untuk mereplikasi ruang memori induk pada setiap garpu, banyak memori akan terbuang sia-sia karena proses anak tidak akan pernah menggunakan. Karenanya overcommitting: kernel hanya mencadangkan sebagian dari memori itu, berdasarkan perkiraan berapa banyak halaman yang dibutuhkan anak.
Jika suatu proses mencoba mengalokasikan sebagian memori dan tidak ada cukup memori yang tersisa, proses tersebut menerima respons kesalahan dan menanganinya sesuai keinginan. Jika suatu proses secara tidak langsung meminta memori dengan menulis ke halaman bersama yang harus dibagikan, itu adalah cerita yang berbeda. Tidak ada cara untuk melaporkan situasi ini ke aplikasi: ia percaya bahwa ia memiliki data yang dapat ditulis di sana, dan bahkan bisa membacanya - hanya saja menulis melibatkan beberapa operasi yang sedikit lebih rumit di bawah tenda. Jika kernel tidak dapat memberikan halaman memori baru, yang bisa dilakukan hanyalah mematikan proses permintaan, atau membunuh beberapa proses lain untuk mengisi memori.
Anda mungkin berpikir pada titik ini bahwa membunuh proses permintaan adalah solusi yang jelas. Namun dalam praktiknya, ini tidak begitu baik. Prosesnya mungkin yang penting yang terjadi hanya perlu mengakses salah satu halamannya sekarang, sementara mungkin ada proses lain yang kurang penting berjalan. Jadi kernel menyertakan heuristik yang kompleks untuk memilih proses mana yang akan dibunuh - pembunuh OOM yang terkenal .
sumber
Hanya untuk menambahkan sudut pandang lain dari jawaban lain, banyak VPS meng-host beberapa mesin virtual pada server tertentu. Setiap VM tunggal akan memiliki jumlah RAM yang ditentukan untuk penggunaannya sendiri. Banyak penyedia menawarkan "burst RAM", di mana mereka dapat menggunakan RAM melebihi jumlah yang ditentukan. Ini dimaksudkan hanya untuk penggunaan jangka pendek, dan mereka yang melampaui jumlah waktu yang diperpanjang ini dapat dihukum oleh tuan rumah yang mematikan proses untuk menurunkan jumlah RAM yang digunakan sehingga orang lain tidak menderita mesin host kelebihan beban.
sumber
Beberapa waktu linux membutuhkan ruang virtual eksternal. Itu adalah partisi swap. Ketika Ram diisi, linux mengambil area swap ini untuk menjalankan proses dengan prioritas rendah.
sumber