Форум программистов, компьютерный форум, киберфорум
C++ Builder
Войти
Регистрация
Восстановить пароль
 
Рейтинг 4.96/56: Рейтинг темы: голосов - 56, средняя оценка - 4.96
3 / 5 / 0
Регистрация: 09.12.2012
Сообщений: 97
1

Выделение большого объема оперативной памяти

19.06.2014, 17:59. Показов 11397. Ответов 7
Метки нет (Все метки)

Необходимо выделить много памяти под массивы (динамически). Памяти в железе 8 Гб. Свободно 4 Гб при тестировании программы. Выделяю массив длинной 50 000 и шириной 40 из long double.

C++
1
2
3
4
5
6
7
        GMS_ld=new long double *[size_a];
        i=0;
        while (i<size_a)
        {
            GMS_ld[i]=new long double [size_b];
            i++;
        }
Вылетает ошибка "External exception EEFFACE". Вроде бы памяти достаточно, однако ...! Надо выделять память с вероятностью успеха в 100%. Как быть?

Добавлено через 20 минут
Провел тесты на машине с 32 Гб оперативной памяти. Результат положительный даже при массиве 600000 на 300.
__________________
Помощь в написании контрольных, курсовых и дипломных работ здесь
0
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
19.06.2014, 17:59
Ответы с готовыми решениями:

Выделение памяти (CodeGear RAD Studio 2009 - C++ builder). Выделение памяти - консоль vs SDIApp
Есть определенный класс - длинная арифметика. Не идеальный - хранение данных исполнено в виде...

Чтение большого объема данных
Доброго времени суток. У меня есть файлик (.тхт), в нем очень много числовых значений, которые...

Считывание большого объема текста с мемо
Программа написана не самым лучшим образом, увы. но в ней нужно поменять считывание так чтобы она...

Выделение большого объема памяти
необходимо открыть большой файл (около 1 гб) и разместить его в памяти. Какой функция выделить...

7
4033 / 2323 / 292
Регистрация: 03.02.2011
Сообщений: 5,066
Записей в блоге: 10
20.06.2014, 22:14 2
Цитата Сообщение от Gendalf147 Посмотреть сообщение
Вылетает ошибка "External exception EEFFACE". Вроде бы памяти достаточно, однако ...! Надо выделять память с вероятностью успеха в 100%. Как быть?
Отловить исключение и сообщить юзверю, что нужно увеличить либо оперативку, либо своп.

Кстати, что есть size_a и что есть size_b?
0
3 / 5 / 0
Регистрация: 09.12.2012
Сообщений: 97
06.07.2014, 17:07  [ТС] 3
size_a и size_b - интовские переменные ... .

Теперь вот какая проблема возникает:
Я создаю динамический массив элементов типа double (матрицу). В одном случае она размерностью n x n, во втором она n x f(x), т.е. число столбцов одно а число строк в каждом столбце разное. При выделении небольшого количества столбцов (300 например), все работает - память освобождается в конце концов, а вот если взять 3000 столбцов, то программа что-то шалит.

Короче. Вот код:

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
    if ((set_1==1)||(set_1==2))
    {
        Solvera->GMS_MKE=new double *[size];
        i=0;
        while (i<size)
        {
            Solvera->GMS_MKE[i]=new double [size];
            i++;
        }
    }
    if (set_1==3)
    {        
        dmax=0;
        temp_Ar1=Project->Areases;
        while (temp_Ar1)
        {            
            temp_ArArElem1=temp_Ar1->areaEls;
            while (temp_ArArElem1)
            {
                temp_ArElem1=temp_ArArElem1->areaEl;               
                temp_lnna=temp_ArElem1->nodes;
                temp_noda=temp_lnna->node;
                i=0;
                while (i<size)
                {
                    if (Solvera->NodesList[i]==temp_noda->n)
                    {
                        break;
                    }
                    i++;
                }
                n1=i;
                temp_lnna=temp_lnna->next;
                temp_nodb=temp_lnna->node;
                i=0;
                while (i<size)
                {
                    if (Solvera->NodesList[i]==temp_nodb->n)
                    {
                        break;
                    }
                    i++;
                }
                n2=i;
                temp_lnna=temp_lnna->next;
                temp_nodc=temp_lnna->node;
                i=0;
                while (i<size)
                {
                    if (Solvera->NodesList[i]==temp_nodc->n)
                    {
                        break;
                    }
                    i++;
                }
                n3=i;
                d1=n1-n2;
                d2=n1-n3;
                d3=n2-n3;             
                if (d1<0)
                {
                    d1=-d1;
                }
                if (d2<0)
                {
                    d2=-d2;
                }
                if (d3<0)
                {
                    d3=-d3;
                }             
                if (d1>d2)
                {
                    if (d1>d3)
                    {
                        d0=d1;
                    }
                    else
                    {
                        d0=d3;
                    }
                }
                else
                {
                    if (d2>d3)
                    {
                        d0=d2;
                    }
                    else
                    {
                        d0=d3;
                    }
                }
                if (dmax<d0)
                {
                    dmax=d0;
                }
                temp_ArArElem1=temp_ArArElem1->next;
            }
            temp_Ar1=temp_Ar1->next;
        }
        Solvera->h=dmax;     
        Solvera->GMS_MKE=new double *[size];
// Выделяю память для ГМЖ:
        i=0;
        while (i<dmax)
        {
            Solvera->GMS_MKE[i]=new double [dmax+i];
            i++;
        }
        while (i<size-dmax)
        {
            Solvera->GMS_MKE[i]=new double [2*dmax-1];
            i++;
        }
        while (i<size)
        {
            Solvera->GMS_MKE[i]=new double [dmax+size-1-i];
            i++;
        }
    }
Ошибка возникает тут (Где то по середине):

C++
1
2
3
4
5
6
            bufi1=0;
            while (bufi1<Solvera->SizeMKE)
            {
                delete [] Solvera->GMS_MKE[bufi1];
                bufi1++;
            }
Добавлено через 2 минуты
Не понял насчет
Цитата Сообщение от BRcr Посмотреть сообщение
своп
- что за зверь такой?
0
4033 / 2323 / 292
Регистрация: 03.02.2011
Сообщений: 5,066
Записей в блоге: 10
06.07.2014, 21:35 4
Цитата Сообщение от Gendalf147 Посмотреть сообщение
что за зверь такой?
Подкачка страниц

А что за ошибка теперь? Та же?
0
3 / 5 / 0
Регистрация: 09.12.2012
Сообщений: 97
10.07.2014, 12:40  [ТС] 5
Да, та же ...
0
4033 / 2323 / 292
Регистрация: 03.02.2011
Сообщений: 5,066
Записей в блоге: 10
13.07.2014, 14:45 6
Можешь попробовать через File Mapping. Создавать mapping в этом случае не с существующего файла, а через файл подкачки. И выделять, естественно, не одним большим куском, а небольшими отдельными блоками, т.к. оперативка фрагментирована системными dll и большие непрерывные куски памяти могут не найтись.

Creating a File Mapping Object

Добавлено через 4 минуты
Вот еще интересная цитатка отыскалась у меня в загашниках...
У NT есть множество классных возможностей, которые далеко не очевидны, если только вы не читаете документациюДЕЙСТВИТЕЛЬНО внимательно.

Одна из моих любимых - то, что я называю “временные” временные файлы.

“Временный” временный файл - это такой файл, содержимое которого никогда не сбрасывается на диск (при отсутствии нехваток памяти, конечно же). Он ведёт себя точно как файл (потому что это и есть файл), но менеджер кэша отключает отложенную запись файла, а файловая система в курсе, что страницы, содержащие метаданные файла, никогда не нужно сбрасывать на диск.

Чтобы создать “временный” файл, вы должны вызвать*CreateFile, указывая в параметре dwFlagsAndAttributes комбинацию FILE_ATTRIBUTE_TEMPORARY or FILE_FLAG_DELETE_ON_CLOSE. Эта комбинация битов работает как подсказка файловой системе, что данные файла никогда не должны сбрасываться на диск. Другими словами, такой файл может быть создан, в него могут писаться данные, из него можно читать, а система при этом ни разу даже не коснётся диска.

Видите ли, другим большим плюсом “временных” временных файлов по сравнению с буферами в памяти является то, что их размер ограничен доступным свободным местом на диске, а НЕ в памяти. Поэтому, если рендеринг в памяти может обломаться с данными в 1 Гб (потому что вы не смогли выделить непрерывный кусок памяти в 1 Гб), то у рендеринга во “временный” временный файл таких проблем нет (в предположении, что у вас есть свободное дисковое пространство). Если у вас закончится память - менеджер памяти будет сбрасывать ваш файл на диск. Конечно же, это повлияет на производительность, но, по-крайней мере, ваша операция будет успешна.
Источник - http://www.transl-gunsmoker.ru... st_11.html
2
4033 / 2323 / 292
Регистрация: 03.02.2011
Сообщений: 5,066
Записей в блоге: 10
13.07.2014, 16:10 7
И еще до кучи в тему. Правда, не знаю, насколько это окажется актуально...
Как malloc память ест


Нет, здесь не будет ничего из серии «Аааа, я сделал malloc (new), и забыл сделать free (delete)!»
Здесь будет нечто изощренное: мы будем отрезать кусочки памяти по чуть-чуть, прятать их в укромное место… А когда операционная система заплатит выкуп скажет «Хватит!», мы попробуем вернуть все обратно. Казалось бы, простейшая операция выделения и освобождения памяти — ничего не предвещает беды.
Тем кому интересно как уничтожить забить память — прошу под хабракат


Немножко предыстории

По долгу службы приходится много работать с большими буфферами памяти (представьте себе изображение 5000x40000 пиксел). Порой (из-за фрагментации) не получается выделять непрерывный кусок памяти для всего. Поэтому был написан некоторый менеджер памяти, который выделял сколько есть, возможно, несколькими кусками. Естественно, менеджер памяти должен как выделять, так и удалять. Тогда была обнаружена следующая интересная вещь: Task Manager после освобождения показывает уровень использования памяти такой же как и до выделения блока. Однако никакой новый блок памяти в программе не может быть выделен. Использование средств анализа виртуальной памяти (VMMap от Марка Русиновича) показывает, что память остается занята несмотря на ее освобождение в коде и несмотря на показания TM.

Анализ

Напишем быстренько какую-нибудь программку, которая выделяет и освобождает память. Что нибудь такое, сродни «Hello, World!»:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
int main(void)
{
  const int blockCount = 1024;
  const int blockSize = 1024*1024;
  char **buf;
  printf("Hit something...\n");
  getchar();
  buf = (char**)malloc(blockCount*sizeof(char*));
  for (int i=0; i<blockCount; i++)
  {
    buf[i] = (char*)malloc(blockSize*sizeof(char));
  }
  printf("Memory allocated\n");
  printf("Hit something...\n");
  getchar();
  for (int i=0; i<blockCount; i++)
  {
    free(buf[i]);
  }
  free(buf);
  printf("Hit something...\n");
  printf("Memory freed\n");
  getchar();
  return 0;
}
Несложными подсчетами можно убедиться, что программа должна выделить 1 ГБ памяти, а затем все освободить. После запуска и проверки вся память освобождается. Хм, кажется, система шантажу не поддается. Впрочем, мы резали большие куски.

Теперь возьмем и немножко поправим исходный код:
const int blockSize = 520133 //К примеру...;

В этом случае мы получим, что память выделилась, но не освободилась:
До «Memory freed»:
Выделение большого объема оперативной памяти

После «Memory freed»:
Выделение большого объема оперативной памяти


Пытливый ум программиста не остановился на достигнутом! Я начал искать пороговое значение, при котором возникает такой эффект. После недолгого бинарного подбора выяснилось, что при размере равном
520168 байт и выше — освобождение проходит нормально
520167 байт и ниже — имеем описанную проблему

Забегая вперед скажу, что никаким образом подобное значение порога я объяснить не смог. Оно не делится даже на 1024!

Возможное объяснение

После длительных бдений за гуглом и изучения форумов я пришел к следующим выводам.
Оказывается что после выделения памяти с помощью функций malloc/new в том случае если выделяется маленький кусок, то память не освобождается функциями free/delete, а переходит из разряда committed в разряд reserved. И если мы обращаемся к данной памяти тут же после удаления (по всей видимости в рамках одного хипа), то она может быть выделена повторно. Однако при попытке выделить память из другого класса (либо статической функции) мы получим исключение — не достаточно памяти. По всей видимости при выделении памяти из статической функции память выделяется не в том же хипе, что и при обычном выделении изнутри класса приложения.
В результате после создания большого блока памяти (из маленьких кусочков) мы исчерпываем память и не можем в дальнейшем выделить себе еще немножко ну хоть чуть-чуть! памяти.

Неправильное решение

Использование функций VirtualAlloc/VirtualFree (MSDN) решает данную проблему, память полностью возвращается процессу после использования (ключ MEM_RELEASE), однако при использовании VirtualAlloc происходит сильная фрагментация памяти, и где-то 800Мб памяти не доступно для использования, т.к. максимальный размер свободного блока — 28Кб. Классический malloc в этом плане работает лучше, т.к. там есть некоторый дефрагментатор.

Окончательное решение

Нашел стороннюю реализацию malloc и free (как выясняется, широко известную в узких кругах), которая имеет классический недостаток дефрагментации памяти, но в месте с тем освобождает полностью память после использования. Плюс еще и заметно быстрее работает.
Для любопытствующих и жаждущих имеется ссылка

Ремарки

Под ОС *NIX (Ubuntu, Debian, CentOS) повторить проблему не удалось)
Под ОС Windows проблема была воспроизведена на Windows Server 2003 x64, Windows 7 x64, Windows XP x32.
Не стоит прямо так сразу доверять давно проверенным функциям, в них может крыться подвох.

UPD: Для компиляции на Windows используется MS VS 2010
Источник - http://habrahabr.ru/post/158347/
1
3 / 5 / 0
Регистрация: 09.12.2012
Сообщений: 97
13.07.2014, 18:42  [ТС] 8
Разбираюсь
0
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
13.07.2014, 18:42

Выделение большого объема памяти
Надо выделить 250,000,000 байт. Делаю char m; Вылетает с ошибкой сегментации (SIGSEGV). Система -...

Выделение большого объема памяти ExAllocatePool
Добрый вечер, Пытаюсь выделить память методом ExAllocatePool (PagedPool) для использования далее...

Выделение большого количества оперативной памяти
Я уже год разрабатываю свою игру на C# и только спустя это время, когда программа уже имеет...

Увеличение объёма оперативной памяти
Всем привет, имеется компьютер Sony Vaio SVE171A11V, хотелось бы увеличить объём оперативной памяти...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
8
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2021, vBulletin Solutions, Inc.