Pertanyaan 'System.OutOfMemoryException' dilempar ketika masih ada banyak memori gratis


Ini adalah kode saya:

int size = 100000000;
double sizeInMegabytes = (size * 8.0) / 1024.0 / 1024.0; //762 mb
double[] randomNumbers = new double[size];

Pengecualian: Pengecualian tipe 'System.OutOfMemoryException' dilemparkan.

Saya memiliki memori 4GB pada mesin ini 2,5GB gratis ketika saya mulai menjalankan ini, ada cukup jelas ruang pada PC untuk menangani 762mb dari 10.000.000 nomor acak. Saya perlu menyimpan sebanyak mungkin nomor acak yang diberikan memori yang tersedia. Ketika saya pergi ke produksi akan ada 12GB pada kotak dan saya ingin memanfaatkannya.

Apakah CLR membatasi saya ke memori maks standar untuk memulai? dan bagaimana saya meminta lebih banyak?

Memperbarui

Saya pikir memecah ini menjadi bagian yang lebih kecil dan secara bertahap menambah kebutuhan memori saya akan membantu jika masalahnya adalah karena fragmentasi memori, tetapi tidak Saya tidak bisa melewati ukuran ArrayList total 256mb terlepas dari apa yang saya lakukan tweaker 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;

76
2017-07-20 13:50


asal


Jawaban:


Anda mungkin ingin membaca ini: ""Out Of Memory" Tidak Mengacu pada Memori Fisik"oleh Eric Lippert.

Singkatnya, dan sangat disederhanakan, "Kehabisan memori" tidak berarti bahwa 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, setiap 4 MB besar, itu tidak akan membantu Anda ketika Anda membutuhkan satu blok 5 MB.

Poin Utama: 

  • penyimpanan data yang kita sebut "memori proses" menurut saya paling baik divisualisasikan sebagai file besar pada disk.
  • RAM dapat dilihat hanya sebagai pengoptimalan kinerja
  • Jumlah total memori virtual yang dikonsumsi program Anda benar-benar tidak relevan dengan kinerjanya
  • "kehabisan RAM" jarang menghasilkan kesalahan "kehabisan memori". Alih-alih kesalahan, itu menghasilkan kinerja yang buruk karena biaya penuh dari fakta bahwa penyimpanan sebenarnya pada disk tiba-tiba menjadi relevan.

115
2017-07-20 13:58



Anda tidak memiliki blok memori berkelanjutan untuk mengalokasikan 762MB, memori Anda terpecah-pecah dan pengalokasi tidak dapat menemukan lubang yang cukup besar untuk mengalokasikan memori yang diperlukan.

  1. Anda dapat mencoba bekerja dengan / 3GB (seperti yang disarankan orang lain)
  2. Atau beralih ke OS 64 bit.
  3. Atau modifikasi algoritme sehingga tidak perlu banyak memori. mungkin mengalokasikan beberapa bagian memori yang lebih kecil (relatif).

23
2017-07-20 13:59



Periksa bahwa Anda sedang membangun proses 64-bit, dan bukan yang 32-bit, yang merupakan mode kompilasi default Visual Studio. Untuk melakukan ini, klik kanan pada proyek Anda, Properties -> Build -> target platform: x64. Seperti halnya proses 32-bit, aplikasi Visual Studio yang dikompilasi dalam 32-bit memiliki batas memori virtual 2GB.

Proses 64-bit tidak memiliki keterbatasan ini, karena mereka menggunakan pointer 64-bit, sehingga ruang alamat maksimum teoritis mereka (ukuran memori virtual mereka) adalah 16 exabytes (2 ^ 64). Pada kenyataannya, Windows x64 membatasi memori virtual proses menjadi 8TB. Solusi untuk masalah batas memori kemudian dikompilasi dalam 64-bit.

Namun, ukuran objek di Visual Studio masih terbatas 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 lebih besar dari 2GB, Anda dapat melakukannya dengan menambahkan kode berikut ke file app.config Anda:

<configuration>
  <runtime>
    <gcAllowVeryLargeObjects enabled="true" />
  </runtime>
</configuration>

18
2018-06-26 13:56



Seperti yang Anda ketahui, masalahnya adalah Anda mencoba mengalokasikan satu blok memori besar 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-nilai dalam rangka mungkin untuk digunakan konstruktor yang kelebihan beban untuk menentukan benihnya. Dengan cara ini Anda akan mendapatkan nomor semi acak (seperti DateTime.Now.Ticks) menyimpannya dalam variabel, kemudian ketika Anda mulai melalui daftar, Anda akan membuat instance Random baru menggunakan benih 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 sementara blog yang terhubung dengan jawaban Fredrik Mörk menunjukkan bahwa masalah ini biasanya karena kurangnya ruang alamat itu tidak mencantumkan sejumlah masalah lain, seperti batasan ukuran objek 2GB CLR (disebutkan dalam komentar dari ShuggyCoUk di blog yang sama), glosses atas fragmentasi memori, dan gagal menyebutkan dampak ukuran file halaman (dan bagaimana itu bisa ditangani dengan penggunaan CreateFileMapping fungsi).

Batasan 2GB berarti itu randomNumbers  harus kurang dari 2GB. Karena array adalah kelas dan memiliki beberapa overhead mereka sendiri ini berarti array double harus lebih kecil dari 2 ^ 31. Saya tidak yakin berapa yang lebih kecil dari 2 ^ 31 Panjangnya, tapi Overhead dari .NET array? menunjukkan 12 - 16 byte.

Fragmentasi memori sangat mirip dengan fragmentasi HDD. Anda mungkin memiliki ruang alamat 2GB, tetapi ketika Anda membuat dan menghancurkan objek, akan ada celah di antara nilai-nilai. Jika celah ini terlalu kecil untuk objek besar Anda, dan ruang tambahan tidak dapat diminta, maka 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 .6GB memori, tetapi akan tersebar di seluruh ruang alamat dengan blok terbuka 2024 di antaranya. Jika Anda perlu membuat objek yang .2GB Anda tidak akan dapat melakukannya karena tidak ada blok yang cukup besar untuk menyesuaikannya 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 dalam memori, atau menggunakan algoritma manajemen memori untuk membatasi / mencegah fragmentasi memori. Perlu dicatat bahwa kecuali Anda mengembangkan program besar yang menggunakan sejumlah besar memori ini tidak akan menjadi masalah. Selain itu, masalah ini dapat muncul pada sistem 64 bit karena sebagian besar jendela dibatasi oleh ukuran file halaman dan jumlah RAM pada sistem.

Karena sebagian besar program meminta memori kerja dari OS dan tidak meminta pemetaan file, mereka 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 yang dikelola seperti C # Anda terjebak pada batasan file RAM / halaman dan ruang alamat dari sistem operasi.


Itu lebih lama dari yang diharapkan. Semoga itu membantu seseorang. Saya mempostingnya karena saya berlari ke System.OutOfMemoryException menjalankan program x64 pada sistem dengan 24GB RAM meskipun array saya hanya memegang 2GB barang.


7
2017-12-24 23:15



Saya akan menyarankan opsi boot windows / 3GB. Terlepas dari yang lainnya (terlalu berlebihan untuk melakukan ini satu aplikasi berperilaku buruk, dan itu mungkin tidak akan menyelesaikan masalah Anda juga), itu dapat menyebabkan banyak ketidakstabilan.

Banyak driver Windows tidak diuji dengan opsi ini, jadi beberapa dari mereka menganggap bahwa pointer mode pengguna selalu mengarah ke ruang alamat 2GB yang lebih rendah. Yang berarti mereka dapat putus dengan / 3GB.

Namun, Windows biasanya membatasi proses 32-bit ke ruang alamat 2GB. Tapi itu tidak berarti Anda harus berharap dapat mengalokasikan 2GB!

Ruang alamat sudah dikotori dengan segala macam data yang dialokasikan. Ada tumpukan, dan semua rakitan yang dimuat, variabel statis, dan sebagainya. Tidak ada jaminan bahwa akan ada 800MB memori yang tidak dapat dialokasikan di mana saja.

Mengalokasikan 2 potongan 400MB mungkin akan lebih baik. Atau 4 potongan 200MB. Alokasi lebih kecil jauh lebih mudah untuk menemukan ruang untuk ruang memori yang terfragmentasi.

Bagaimanapun, jika Anda akan menyebarkan ini ke mesin 12GB, Anda akan ingin menjalankan ini sebagai aplikasi 64-bit, yang harus menyelesaikan semua masalah.


5
2017-07-20 14:05



Mengubah dari 32 menjadi 64 bit bekerja untuk saya - patut dicoba jika Anda menggunakan komputer 64 bit dan tidak perlu port.


3
2017-08-31 21:04



Jika Anda membutuhkan struktur besar seperti itu, mungkin Anda bisa menggunakan File Memory Mapped. Artikel ini dapat membantu: http://www.codeproject.com/KB/recipes/MemoryMappedGenericArray.aspx

LP, Dejan


2
2017-07-20 14:09