Ini kode saya:
int size = 100000000;
double sizeInMegabytes = (size * 8.0) / 1024.0 / 1024.0; //762 mb
double[] randomNumbers = new double[size];
Pengecualian: Pengecualian jenis 'System.OutOfMemoryException' dilempar.
Saya memiliki memori 4GB pada mesin ini. 2,5GB kosong ketika saya mulai menjalankan ini, jelas ada cukup ruang pada PC untuk menangani 762mb dari 100000000 nomor acak. Saya perlu menyimpan sebanyak mungkin nomor acak mengingat memori yang tersedia. Ketika saya pergi ke produksi akan ada 12GB di kotak dan saya ingin memanfaatkannya.
Apakah CLR membatasi saya ke memori maks default untuk memulai? dan bagaimana cara meminta lebih banyak?
Memperbarui
Saya pikir memecah ini menjadi potongan-potongan yang lebih kecil dan secara bertahap menambahkan ke persyaratan memori saya akan membantu jika masalahnya disebabkan oleh fragmentasi memori , tetapi ternyata saya tidak dapat melewati ukuran ArrayList total 256mb terlepas dari apa yang saya lakukan tweaking blockSize .
private static IRandomGenerator rnd = new MersenneTwister();
private static IDistribution dist = new DiscreteNormalDistribution(1048576);
private static List<double> ndRandomNumbers = new List<double>();
private static void AddNDRandomNumbers(int numberOfRandomNumbers) {
for (int i = 0; i < numberOfRandomNumbers; i++) {
ndRandomNumbers.Add(dist.ICDF(rnd.nextUniform()));
}
}
Dari metode utama saya:
int blockSize = 1000000;
while (true) {
try
{
AddNDRandomNumbers(blockSize);
}
catch (System.OutOfMemoryException ex)
{
break;
}
}
double arrayTotalSizeInMegabytes = (ndRandomNumbers.Count * 8.0) / 1024.0 / 1024.0;
sumber
Jawaban:
Anda mungkin ingin membaca ini: " " Out Of Memory "Tidak Merujuk ke Memori Fisik " oleh Eric Lippert.
Singkatnya, dan sangat disederhanakan, "Memori habis" tidak berarti jumlah memori yang tersedia terlalu kecil. Alasan paling umum adalah bahwa dalam ruang alamat saat ini, tidak ada bagian memori yang berdekatan yang cukup besar untuk melayani alokasi yang diinginkan. Jika Anda memiliki 100 blok, masing-masing berukuran 4 MB, itu tidak akan membantu Anda ketika Anda membutuhkan satu blok 5 MB.
Poin Utama:
sumber
Periksa bahwa Anda sedang membuat proses 64-bit, dan bukan 32-bit, yang merupakan mode kompilasi default Visual Studio. Untuk melakukan ini, klik kanan pada proyek Anda, Properties -> Build -> platform target: x64. Seperti proses 32-bit apa pun, aplikasi Visual Studio yang dikompilasi dalam 32-bit memiliki batas memori virtual 2GB.
Proses 64-bit tidak memiliki batasan ini, karena mereka menggunakan pointer 64-bit, sehingga ruang alamat maksimum teoritisnya (ukuran memori virtualnya) adalah 16 exabyte (2 ^ 64). Pada kenyataannya, Windows x64 membatasi memori virtual proses hingga 8TB. Solusi untuk masalah batas memori kemudian dikompilasi dalam 64-bit.
Namun, ukuran objek di Visual Studio masih dibatasi hingga 2GB, secara default. Anda akan dapat membuat beberapa array yang ukuran gabungannya akan lebih besar dari 2GB, tetapi Anda tidak dapat secara default membuat array lebih besar dari 2GB. Mudah-mudahan, jika Anda masih ingin membuat array yang lebih besar dari 2GB, Anda bisa melakukannya dengan menambahkan kode berikut ke file app.config Anda:
<configuration> <runtime> <gcAllowVeryLargeObjects enabled="true" /> </runtime> </configuration>
sumber
Anda tidak memiliki blok memori berkelanjutan untuk mengalokasikan 762MB, memori Anda terfragmentasi dan pengalokasi tidak dapat menemukan lubang yang cukup besar untuk mengalokasikan memori yang dibutuhkan.
sumber
Seperti yang mungkin Anda ketahui, masalahnya adalah Anda mencoba mengalokasikan satu blok memori yang berdekatan, yang tidak berfungsi karena fragmentasi memori. Jika saya perlu melakukan apa yang Anda lakukan, saya akan melakukan hal berikut:
int sizeA = 10000, sizeB = 10000; double sizeInMegabytes = (sizeA * sizeB * 8.0) / 1024.0 / 1024.0; //762 mb double[][] randomNumbers = new double[sizeA][]; for (int i = 0; i < randomNumbers.Length; i++) { randomNumbers[i] = new double[sizeB]; }
Kemudian, untuk mendapatkan indeks tertentu yang akan Anda gunakan
randomNumbers[i / sizeB][i % sizeB]
.Pilihan lain jika Anda selalu mengakses nilai secara berurutan mungkin menggunakan konstruktor kelebihan beban untuk menentukan seed. Dengan cara ini Anda akan mendapatkan nomor semi acak (seperti
DateTime.Now.Ticks
) menyimpannya dalam variabel, lalu kapan pun Anda mulai menelusuri daftar, Anda akan membuat instance Random baru menggunakan seed asli:private static int randSeed = (int)DateTime.Now.Ticks; //Must stay the same unless you want to get different random numbers. private static Random GetNewRandomIterator() { return new Random(randSeed); }
Penting untuk dicatat bahwa meskipun blog yang ditautkan dalam jawaban Fredrik Mörk menunjukkan bahwa masalah tersebut biasanya disebabkan oleh kurangnya ruang alamat , blog tersebut tidak mencantumkan sejumlah masalah lain, seperti batasan ukuran objek CLR 2GB (disebutkan dalam komentar dari ShuggyCoUk di blog yang sama), menyoroti fragmentasi memori, dan gagal menyebutkan dampak ukuran file halaman (dan bagaimana hal itu dapat diatasi dengan penggunaan
CreateFileMapping
fungsi ).Batasan 2GB artinya
randomNumbers
harus kurang dari 2GB. Karena array adalah kelas dan memiliki beberapa overhead sendiri, ini berarti arraydouble
harus lebih kecil dari 2 ^ 31. Saya tidak yakin berapa jauh lebih kecil dari 2 ^ 31 Panjangnya harus, tetapi Overhead dari array NET.? menunjukkan 12 - 16 byte.Fragmentasi memori sangat mirip dengan fragmentasi HDD. Anda mungkin memiliki ruang alamat 2GB, tetapi saat Anda membuat dan menghancurkan objek, akan ada celah di antara nilai-nilai tersebut. Jika celah ini terlalu kecil untuk objek besar Anda, dan ruang tambahan tidak dapat diminta, Anda akan mendapatkan
System.OutOfMemoryException
. Misalnya, jika Anda membuat 2 juta, 1024 objek byte, maka Anda menggunakan 1.9GB. Jika Anda menghapus setiap objek yang alamatnya bukan kelipatan 3 maka Anda akan menggunakan memori .6GB, tetapi akan tersebar di seluruh ruang alamat dengan blok terbuka 2024 byte di antaranya. Jika Anda perlu membuat objek berukuran .2GB, Anda tidak akan dapat melakukannya karena tidak ada blok yang cukup besar untuk memuatnya dan ruang tambahan tidak dapat diperoleh (dengan asumsi lingkungan 32 bit). Solusi yang mungkin untuk masalah ini adalah hal-hal seperti menggunakan objek yang lebih kecil, mengurangi jumlah data yang Anda simpan di memori, atau menggunakan algoritma manajemen memori untuk membatasi / mencegah fragmentasi memori. Perlu dicatat bahwa kecuali Anda mengembangkan program besar yang menggunakan banyak memori, ini tidak akan menjadi masalah. Juga,Karena sebagian besar program meminta memori kerja dari OS dan tidak meminta pemetaan file, program tersebut akan dibatasi oleh RAM sistem dan ukuran file halaman. Seperti dicatat dalam komentar oleh Néstor Sánchez (Néstor Sánchez) di blog, dengan kode terkelola seperti C # Anda terjebak pada batasan file RAM / halaman dan ruang alamat sistem operasi.
Itu jauh lebih lama dari yang diharapkan. Semoga membantu seseorang. Saya mempostingnya karena saya
System.OutOfMemoryException
menjalankan program x64 pada sistem dengan RAM 24GB meskipun array saya hanya menyimpan barang 2GB.sumber
Saya menyarankan agar opsi boot windows / 3GB. Terlepas dari yang lainnya (terlalu berlebihan untuk melakukan ini untuk satu aplikasi yang berperilaku buruk, dan itu mungkin tidak akan menyelesaikan masalah Anda), ini dapat menyebabkan banyak ketidakstabilan.
Banyak driver Windows tidak diuji dengan opsi ini, jadi beberapa dari mereka menganggap bahwa penunjuk mode pengguna selalu mengarah ke 2GB ruang alamat yang lebih rendah. Yang berarti mereka mungkin rusak parah dengan / 3GB.
Namun, Windows biasanya membatasi proses 32-bit ke ruang alamat 2GB. Tetapi itu tidak berarti Anda harus berharap dapat mengalokasikan 2GB!
Ruang alamat sudah dikotori dengan semua jenis data yang dialokasikan. Ada tumpukan, dan semua rakitan yang dimuat, variabel statis, dan sebagainya. Tidak ada jaminan bahwa akan ada 800MB memori yang tidak terisi di mana saja.
Mengalokasikan 2.400MB potongan mungkin akan berjalan lebih baik. Atau 4 potongan 200MB. Alokasi yang lebih kecil jauh lebih mudah untuk menemukan ruang dalam ruang memori yang terfragmentasi.
Bagaimanapun, jika Anda akan menerapkan ini ke mesin 12GB, Anda pasti ingin menjalankan ini sebagai aplikasi 64-bit, yang seharusnya menyelesaikan semua masalah.
sumber
Mengubah dari 32 menjadi 64 bit berhasil untuk saya - patut dicoba jika Anda menggunakan pc 64 bit dan tidak perlu port.
sumber
Jika Anda membutuhkan struktur besar seperti itu, mungkin Anda dapat memanfaatkan File yang Dipetakan Memori. Artikel ini terbukti berguna: http://www.codeproject.com/KB/recipes/MemoryMappedGenericArray.aspx
LP, Dejan
sumber
Jendela 32bit memiliki batas memori proses 2GB. Opsi boot / 3GB yang disebutkan orang lain akan membuat 3GB ini dengan hanya tersisa 1GB untuk penggunaan kernel OS. Secara realistis jika ingin menggunakan lebih dari 2GB tanpa ribet maka diperlukan OS 64bit. Ini juga mengatasi masalah di mana meskipun Anda mungkin memiliki RAM fisik 4GB, ruang alamat yang diperlukan untuk kartu video dapat membuat potongan yang cukup besar dari memori itu tidak dapat digunakan - biasanya sekitar 500MB.
sumber
Daripada mengalokasikan array yang sangat besar, dapatkah Anda mencoba menggunakan iterator? Ini adalah penundaan-dieksekusi, artinya nilai-nilai dihasilkan hanya seperti yang diminta dalam pernyataan foreach; Anda tidak boleh kehabisan memori dengan cara ini:
private static IEnumerable<double> MakeRandomNumbers(int numberOfRandomNumbers) { for (int i = 0; i < numberOfRandomNumbers; i++) { yield return randomGenerator.GetAnotherRandomNumber(); } } ... // Hooray, we won't run out of memory! foreach(var number in MakeRandomNumbers(int.MaxValue)) { Console.WriteLine(number); }
Di atas akan menghasilkan nomor acak sebanyak yang Anda inginkan, tetapi hanya menghasilkannya seperti yang diminta melalui pernyataan foreach. Anda tidak akan kehabisan memori dengan cara itu.
Bergantian, Jika Anda harus memiliki semuanya di satu tempat, simpanlah dalam file, bukan di memori.
sumber
Nah, saya mendapat masalah serupa dengan kumpulan data yang besar dan mencoba memaksa aplikasi untuk menggunakan begitu banyak data bukanlah pilihan yang tepat. Tip terbaik yang dapat saya berikan adalah memproses data Anda dalam potongan kecil jika memungkinkan. Karena berurusan dengan begitu banyak data, masalah akan kembali cepat atau lambat. Selain itu, Anda tidak dapat mengetahui konfigurasi setiap mesin yang akan menjalankan aplikasi Anda sehingga selalu ada risiko pengecualian akan terjadi pada komputer lain.
sumber
Saya memiliki masalah serupa, itu karena StringBuilder.ToString ();
sumber
Ubah solusi Anda menjadi x64. Jika Anda masih menghadapi masalah, berikan panjang maksimal untuk semua yang memiliki pengecualian seperti di bawah ini:
var jsSerializer = new JavaScriptSerializer(); jsSerializer.MaxJsonLength = Int32.MaxValue;
sumber
Jika Anda tidak memerlukan Proses Hosting Visual Studio:
Hapus centang opsi: Project-> Properties-> Debug-> Aktifkan Proses Hosting Visual Studio
Dan kemudian membangun.
Jika Anda masih menghadapi masalah:
Pergi ke Project-> Properties-> Build Events-> Post-Build Event Command line dan tempel yang berikut ini:
call "$(DevEnvDir)..\..\vc\vcvarsall.bat" x86 "$(DevEnvDir)..\..\vc\bin\EditBin.exe" "$(TargetPath)" /LARGEADDRESSAWARE
Sekarang, bangun proyeknya.
sumber
Tingkatkan batas proses Windows menjadi 3gb. (melalui boot.ini atau manajer boot Vista)
sumber