Mengapa Large Object Heap dan mengapa kami peduli?

105

Saya telah membaca tentang Generations and Large object heap. Tetapi saya masih gagal untuk memahami apa pentingnya (atau manfaat) memiliki tumpukan objek besar?

Apa yang bisa salah (dalam hal kinerja atau memori) jika CLR hanya mengandalkan Generasi 2 (Mengingat ambang batas untuk Gen0 dan Gen1 kecil untuk menangani objek Besar) untuk menyimpan objek besar?

Manish Basantani
sumber
6
Ini memberi saya dua pertanyaan untuk desainer NET.: 1. Mengapa defrag LOH tidak dipanggil sebelum OutOfMemoryException dilemparkan? 2. Mengapa tidak objek LOH memiliki afinitas untuk tetap bersama (besar lebih memilih akhir tumpukan dan kecil di awal)
Jacob Brewer

Jawaban:

195

Pengumpulan sampah tidak hanya menyingkirkan objek yang tidak direferensikan, tetapi juga memadatkan heap. Itu pengoptimalan yang sangat penting. Ini tidak hanya membuat penggunaan memori lebih efisien (tidak ada lubang yang tidak digunakan), tetapi juga membuat cache CPU jauh lebih efisien. Cache adalah masalah yang sangat besar pada prosesor modern, mereka jauh lebih cepat daripada bus memori.

Pemadatan dilakukan hanya dengan menyalin byte. Namun itu membutuhkan waktu. Semakin besar objeknya, semakin besar kemungkinan biaya untuk menyalinnya melebihi kemungkinan peningkatan penggunaan cache CPU.

Jadi mereka menjalankan banyak tolok ukur untuk menentukan titik impas. Dan mencapai 85.000 byte sebagai titik potong di mana penyalinan tidak lagi meningkatkan kinerja. Dengan pengecualian khusus untuk array ganda, mereka dianggap 'besar' ketika array memiliki lebih dari 1000 elemen. Itu adalah pengoptimalan lain untuk kode 32-bit, pengalokasi tumpukan objek besar memiliki properti khusus yang mengalokasikan memori pada alamat yang disejajarkan dengan 8, tidak seperti pengalokasi generasi biasa yang hanya mengalokasikan selaras 4. Penyelarasan itu adalah masalah besar untuk ganda , membaca atau menulis tulisan ganda yang tidak sejajar sangat mahal. Anehnya info Microsoft yang jarang tidak pernah menyebutkan array yang panjang, tidak yakin ada apa dengan itu.

Fwiw, ada banyak kecemasan programmer tentang tumpukan objek yang besar tidak dipadatkan. Ini selalu dipicu ketika mereka menulis program yang menghabiskan lebih dari setengah dari seluruh ruang alamat yang tersedia. Dilanjutkan dengan menggunakan tool seperti memory profiler untuk mencari tahu mengapa program tersebut di-bomb meskipun masih banyak tersedia virtual memory yang tidak terpakai. Alat semacam itu menunjukkan lubang di LOH, potongan memori yang tidak terpakai di mana sebelumnya ada benda besar, tetapi sampah dikumpulkan. Begitulah harga LOH yang tak terelakkan, lubang hanya dapat digunakan kembali dengan alokasi untuk benda yang ukurannya sama atau lebih kecil. Masalah sebenarnya adalah mengasumsikan bahwa program harus diizinkan untuk menggunakan semua memori virtual kapan saja.

Masalah yang jika tidak hilang sepenuhnya hanya dengan menjalankan kode pada sistem operasi 64-bit. Proses 64-bit memiliki 8 terabyte ruang alamat memori virtual yang tersedia, 3 kali lipat lebih besar dari proses 32-bit. Anda tidak bisa kehabisan lubang.

Singkat cerita, LOH membuat kode berjalan lebih efisien. Dengan biaya penggunaan ruang alamat memori virtual yang tersedia kurang efisien.


UPDATE, .NET 4.5.1 sekarang mendukung pemadatan properti LOH, GCSettings.LargeObjectHeapCompactionMode . Berhati-hatilah dengan konsekuensinya.

Hans Passant
sumber
3
@Hans Passant, bisakah Anda menjelaskan tentang sistem x64, maksud Anda masalah ini benar-benar hilang?
Johnny_D
Beberapa detail implementasi LOH masuk akal, tetapi beberapa membingungkan saya. Sebagai contoh, saya dapat memahami bahwa jika banyak objek besar dibuat dan ditinggalkan, secara umum mungkin diinginkan untuk menghapusnya secara massal dalam koleksi Gen2 daripada sedikit demi sedikit dalam koleksi Gen0, tetapi jika seseorang membuat dan meninggalkan, misalnya, array 22.000 string yang padanya tidak ada referensi luar, apa keuntungan memiliki koleksi Gen0 dan Gen1 yang menandai semua 22.000 string sebagai "hidup" tanpa memperhatikan apakah ada referensi ke larik?
supercat
6
Tentu saja masalah fragmentasi sama pada x64. Hanya perlu beberapa hari lagi untuk menjalankan proses server Anda sebelum mulai bekerja.
Lothar
1
Hmm, tidak, jangan pernah meremehkan 3 kali lipat. Berapa lama waktu yang dibutuhkan untuk mengumpulkan sampah dengan tumpukan 4 terabyte adalah sesuatu yang tidak dapat Anda hindari untuk menemukan jauh sebelum itu mendekati itu.
Hans Passant
2
@HansPassant Bisakah Anda, tolong, uraikan pernyataan ini: "Berapa lama waktu yang dibutuhkan untuk mengumpulkan sampah dari tumpukan 4 terabyte adalah sesuatu yang tidak dapat Anda hindari untuk menemukan jauh sebelum mendekati itu."
relative_random
9

Jika ukuran objek lebih besar dari beberapa nilai yang disematkan (85000 byte dalam .NET 1), maka CLR meletakkannya di Large Object Heap. Ini mengoptimalkan:

  1. Alokasi objek (objek kecil tidak dicampur dengan objek besar)
  2. Pengumpulan sampah (LOH dikumpulkan hanya dengan GC penuh)
  3. Defragmentasi memori (LOH tidak pernah jarang dipadatkan)
oleksii
sumber
9

Perbedaan penting dari Small Object Heap (SOH) dan Large Object Heap (LOH) adalah, memori di SOH akan dipadatkan saat dikumpulkan, sedangkan LOH tidak, seperti artikel ini diilustrasikan dalam . Memadatkan benda besar membutuhkan banyak biaya. Mirip dengan contoh di artikel, katakanlah memindahkan satu byte dalam memori membutuhkan 2 siklus, kemudian memadatkan objek 8MB di komputer 2GHz membutuhkan 8ms, yang merupakan biaya yang besar. Mempertimbangkan objek besar (array dalam banyak kasus) cukup umum dalam praktiknya, saya kira itulah alasan mengapa Microsoft menyematkan objek besar di memori dan mengusulkan LOH.

BTW, menurut posting ini , LOH biasanya tidak menimbulkan masalah fragmen memori.

anggur
sumber
1
Memuat data dalam jumlah besar ke dalam objek yang dikelola biasanya mengecilkan biaya 8ms untuk memadatkan LOH. Dalam praktiknya di sebagian besar aplikasi data besar, biaya LOH tidak seberapa dibandingkan kinerja aplikasi lainnya.
Shiv
3

Prinsipnya adalah bahwa tidak mungkin (dan sangat mungkin desain yang buruk) bahwa suatu proses akan membuat banyak objek besar berumur pendek sehingga CLR mengalokasikan objek besar ke heap terpisah yang menjalankan GC pada jadwal yang berbeda dengan heap biasa. http://msdn.microsoft.com/en-us/magazine/cc534993.aspx

Myles McDonnell
sumber
Juga meletakkan objek besar pada, katakanlah, generasi 2 dapat merusak kinerja, karena akan memakan waktu lama untuk memadatkan memori, terutama jika sejumlah kecil dibebaskan dan objek BESAR harus disalin ke lokasi baru. LOH saat ini tidak dipadatkan karena alasan kinerja.
Christopher Currens
Saya pikir itu hanya desain yang buruk karena GC tidak menanganinya dengan baik.
CodesInChaos
@CodeInChaos Rupanya, ada beberapa peningkatan yang datang di .NET 4,5
Christian.K
1
@CodeInChaos: Meskipun mungkin masuk akal jika sistem menunggu hingga koleksi gen2 sebelum mencoba mendapatkan kembali memori dari objek LOH yang berumur pendek, saya tidak dapat melihat keuntungan kinerja apa pun untuk mendeklarasikan objek LOH (dan objek apa pun yang mereka pegang referensi) hidup tanpa syarat selama koleksi gen0 dan gen1. Apakah ada beberapa pengoptimalan yang dimungkinkan oleh asumsi seperti itu?
supercat
@supercat Saya melihat tautan yang disebutkan oleh Myles McDonnell. Pemahaman saya adalah: 1. Pengumpulan LOH terjadi di gen 2 GC. 2. Koleksi LOH tidak termasuk pemadatan (pada saat artikel ditulis). Sebaliknya, ini akan menandai objek mati sebagai dapat digunakan kembali dan lubang ini akan digunakan untuk alokasi LOH di masa mendatang jika cukup besar. Karena poin 1, mengingat GC gen 2 akan lambat jika ada banyak objek di gen 2, saya pikir lebih baik menghindari penggunaan LOH sebanyak mungkin dalam kasus ini.
penggemar robbie
0

Saya bukan ahli CLR, tetapi saya membayangkan bahwa memiliki heap khusus untuk objek besar dapat mencegah penyapuan GC yang tidak perlu dari heap generasi yang ada. Mengalokasikan objek yang besar membutuhkan memori bebas yang berdekatan dalam jumlah yang signifikan . Untuk menyediakannya dari "lubang" yang tersebar di heap generasi, Anda memerlukan pemadatan yang sering (yang hanya dilakukan dengan siklus GC).

Chris Shain
sumber