Как большой массив выделяет память?

Я ищу способ сохранить большую трехмерную структуру разреженных массивов в памяти, не тратя много памяти. Здесь я провел эксперимент с массивами longs:

using System;
using System.Diagnostics;
using System.Runtime;

namespace ConsoleApp4
{
    public class Program
    {
        static Process proc = Process.GetCurrentProcess();
        const int MB = 1024 * 1024;
        const int IMAX = 5;
        const int JMAX = 100000000;
        public static void ShowTextWithMemAlloc(string text)
        {
            proc.Refresh();
            Console.WriteLine($"{text,-30}WS64:{proc.WorkingSet64/MB,5}MB  PMS64:{proc.PrivateMemorySize64/MB,5}MB");
            Console.ReadKey();
        }
        public static void Main(string[] args)
        {
            Console.Write(" ");
            ShowTextWithMemAlloc("Start.");
            long[] lArray = new long[IMAX * JMAX];
            long[] l1Array = new long[IMAX * JMAX];
            long[] l2Array = new long[IMAX * JMAX];
            long[] l3Array = new long[IMAX * JMAX];
            ShowTextWithMemAlloc("Arrays created.");
            lArray[IMAX * JMAX - 1] = 5000;
            l1Array[IMAX * JMAX - 1] = 5000;
            l2Array[IMAX * JMAX - 1] = 5000;
            l3Array[IMAX * JMAX - 1] = 5000;
            ShowTextWithMemAlloc("Last elements accessed.");
            for (var i=IMAX-1; i>= 0; i--)
            {
                for (var j=0; j<JMAX; j++)
                {
                    lArray[i * JMAX + j] = i * JMAX + j;
                }
                ShowTextWithMemAlloc($"Value for row {i} assigned.");
            }
            //lArray = new long[5];
            //l1Array = null;
            //l2Array = null;
            //l3Array = null;
            //GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce;
            //GC.Collect();
            //ShowTextWithMemAlloc($"GC.Collect done.");
            ShowTextWithMemAlloc("Stop.");
        }
    }
}

Если вы хотите проверить его, установите для переменной среды COMPlus_gcAllowVeryLargeObjects (Project Properties -> Debug) значение 1 или измените JMAX. И это вывод:

 Start.                        WS64:   14MB  PMS64:    8MB
 Arrays created.               WS64:   15MB  PMS64:15360MB
 Last elements accessed.       WS64:   15MB  PMS64:15360MB
 Value for row 4 assigned.     WS64:  779MB  PMS64:15360MB
 Value for row 3 assigned.     WS64: 1542MB  PMS64:15360MB
 Value for row 2 assigned.     WS64: 2305MB  PMS64:15361MB
 Value for row 1 assigned.     WS64: 3069MB  PMS64:15361MB
 Value for row 0 assigned.     WS64: 3832MB  PMS64:15362MB
 Stop.                         WS64: 3844MB  PMS64:15325MB

Когда я вижу потребление памяти в диспетчере задач, это похоже на Process.WorkingSet64. Что такое реальное число? Почему память выделяется при назначении? Является ли массив фактически непрерывной выделенной памятью? Является ли массив массивом? Существуют ли инопланетяне? (драматическая фоновая музыка)

Эпизод 2: Мы вносим небольшое изменение:

            //lArray[i * JMAX + j] = i * JMAX + j;
            var x= lArray[i * JMAX + j];

и ничего не изменится (на выходе). Где разница между существующим и несуществующим? (более драматичная фоновая музыка) Теперь мы ждем ответа от одного из загадочных людей (у них есть несколько цифр и маленькая буква "к" под своими именами).

Эпизод 3: Еще одно изменение:

    //lArray[IMAX * JMAX - 1] = 5000;
    //l1Array[IMAX * JMAX - 1] = 5000;
    //l2Array[IMAX * JMAX - 1] = 5000;
    //l3Array[IMAX * JMAX - 1] = 5000;
    //ShowTextWithMemAlloc("Last elements accessed.");
    long newIMAX = IMAX-3;
    long newJMAX = JMAX / 10;
    for (var i=0; i<newIMAX; i++)
    {
        for (var j=0; j<newJMAX; j++)
        {
            lArray[i * newJMAX + j] = i * newJMAX + j;
            //var x= lArray[i * JMAX + j];
        }
        //ShowTextWithMemAlloc($"Value for row {i} assigned.");
    }
    ShowTextWithMemAlloc($"{newIMAX*newJMAX} values assigned.");

Выход:

 Start.                             WS64:   14MB  PMS64:    8MB
 Arrays created.                    WS64:   15MB  PMS64:15369MB
 20000000 values assigned.          WS64:  168MB  PMS64:15369MB
 Stop.                              WS64:  168MB  PMS64:15369MB

PMS64 для одного массива (15369-8)/4 = 3840 МБ. Это не разреженный массив, а частично заполненный массив;) . Я использую этот полный 168 МБ.

Ответьте на вопрос "Почему вы не используете точный размер?". Потому что я этого не знаю? Данные могут поступать из нескольких пользовательских SQL. "Почему вы не измените размер?". Изменение размера создает новый массив и копирует значения. Это время для копирования, памяти и в конце концов злой GC приходит и ест вас.

Я потратил впустую память. (Не помню. Инопланетяне?!) А когда да, сколько? 0, (3840-168) МБ или (15369-8-168) МБ?

Эпилог:

Комментарий - это комментарий или ответ?

смежная память на самом деле смежная память?

Ответы дают ответы? Таинственный. ( больше музыки)

(Скалли: Малдер, жабы только что упали с неба!Малдер: Думаю, их парашюты не открылись.)

Спасибо вам всем!

1 ответ

Решение

Рабочий набор не является объемом выделенной памяти. Это набор страниц, которые в настоящее время доступны для процесса. Windows реализует различные политики вокруг этого, и их число, как правило, трудно интерпретировать.

Здесь память, вероятно, была запрошена как обнуленная из ОС. Первый доступ к странице фактически делает обнуленную страницу доступной.

Вы должны смотреть на частные байты.

Вы не можете редко размещать.NET массивы. Вероятно, вам стоит взглянуть на использование некоторой структуры данных, которая создает впечатление разреженного массива.

Является ли массив фактически непрерывной выделенной памятью?

Да, с точки зрения запуска CLR и кода.NET. ОС может играть хитрости, такие как ленивый сбой на страницах при первом чтении или записи.

Для "Эпизода 2" ответ таков: сбой происходит как для чтения, так и для записи. Я не совсем слежу за тем, что делает эпизод 3, но полагаю, что он затрагивает меньше страниц.

Я потратил впустую память

Это сложнее сказать. Пока страницы не тронуты, они не используются физически. Они могут использоваться, например, для файлового кэша или для других резидентных рабочих программ. Тем не менее, они рассчитывают на коммит системы. Windows гарантирует вам, что может сделать эти страницы доступными для вас. Вы не будете исчерпывать память при некотором произвольном доступе к памяти. Linux не гарантирует этого. У этого есть убийца OOM как смягчение.

В крайнем случае, если вы выделяете 1 ТБ, вам нужно, чтобы сумма ОЗУ и размер файла подкачки также превышала 1 ТБ, даже если ни одно из этого пространства может не использоваться.

Рассмотрите возможность использования файлов с отображенной памятью Здесь файл является резервным хранилищем, а оперативная память обрабатывается как кэш. Это будет вести себя точно так же.

Другие вопросы по тегам