Re: как в С++ отследить где выделяется память

max87544

Проблема в следующем :
Есть в проге функция : //она конечно не такая, но с этой тоже самое.
double **dists1;
double **dists2;
long i1,j1,s=scheme.size;
dists1= new (double*)[s+1];
dists2= new (double*)[s+1];
for (long it=0;it!=s+1;it++)
dists1[it]= new double[s+1];
for (long it=0;it!=s+1;it++)
dists2[it]= new double[s+1];
for (i1=0;i1!=s;i1++)
{
for (j1=0;j1!=s;j1++)
{
dists1[1][1]=dists1[i1][j1]=2.;
}
}
for (long it=0;it!=s+1;it++)
delete [](dists1[it]);
delete []dists1;
for (long it=0;it!=s+1;it++)
delete [](dists2[it]);
delete []dists2;
// прогонялось при s == 3600
// прога под линух
// linux-2.4.22-ac2
// gcc version 3.3.1 20030903 (Red Hat Linux 3.3.1-3)
Так вот, в процессе ее выполнении прогой отжирается память -- порядка 200 мегов, причем память отжирается не сразу, а постепенно, в процессе выполнения цикла.
Как компилировать - пофиг , что с O4 что с O0 что с --no-inline -- без разницы -- все равно память жрет.
с gdb тоже ничего хорошего не вышло -- память отжирается нерегулярно, и при прогонке по шагам я задолбался ждать, пока память выделится,
так что может быть уто и посоветует как с помощью gdb это отловить?
Если кто знает и возможно сталкивался -- в чем проблема, почему так происходит,
заранее спасибо за ответы

Dasar

Smart-поинтеры заюзать?

Dasar

Скомпилируй под Visual Studio-ей, и натрави Bounds Checker

yuda

Дык эта : 3600*3600*8 (размер матрицы* размер double) получили 100 мегов.
Ну и две их у тебя.
В чем вопрос?

max87544

а поподробнее можно...
дело в том, что даже если в ассемблер компилять, то
из этого цикла получается кусок безо всяких call-ов , и кроме mov-ов и jmp-ов внутри куска ничего нету ,
единственное (на мой взгляд) то что может расти -- это стек, но ПОЧЕМУ?

max87544

Вопрос в том, что почему не в начале? -- не на выделении памяти, а именно в цикле, и почему уже после выделения памяти
операторами new[] она растет еще мегов на 90 в цикле?

yuda

В каком цикле-то?

max87544

в двойном --
сорри что не сказал.
сутки уже ебусь с этим делом

Dasar

не знаю, как в Linux-е, но под Windows-ом при операции new реального выделения памяти нет, память только резервируется. Реально память начинает выделяться только при первом обращение.

Dasar

Сколько памяти прога, вообще, отжирает?

max87544

Дык ежли б ето было бы так, то разве в
ассемблерном коде этого двойного цикла
не было бы к-нибудь левого колла или джампа, который бы выделял эту память?
всеб завтра повешусь

yuda

Маза, прав.
Оптимизация...
Тебе не по фигу, как работает, если работает?

yuda

Дык это свойство не программы, а ОС.

max87544

Не пофиг, т.к при определенных обстоятельствах она отжирала 700 мегов памяти плюс 300 свопа,
так что левая захваченная память -- не особенно хорошо,
а если это особенности системы, кто-нибудь может что-нибудь грамотно сказать по этому поводу --
типа реально в линухе дело
-- типа это нормально, то что для выполнения цикла 3600х3600 надо жрать 90 метров памяти?
-- тем более что известно, что если тупо эту функцию написать в мэйне, то никакой такой лажи не будет, и что
вся эта лажа происходит по какой-то причине, а точнее дело в остальной программе ,
так что вопрос -- какова причина, что может вызвать такую работу программы?

shlyumper

разместить в оперативной памяти произвольную матрицу из 3600 x 3600 элементов по 8 байт каждый отожрав меньше 100 мегов невозможно.

freezer

видимо дело обстоит так: ты выделяешь много памяти под массивы, либся запрашивает у оси память под кучу, потом массивы ты юзаешь и память освобождаешь, но размер кучи так и остается прежним. Проверить это можно вызвав этот код несколько раз. Если памяти "утечет" все те же 200 М - то я прав, если 200М * число вызовов, то бага где-то еще

max87544

да в курсе я пр эти 100 мегов....
сории и спасибо всем
я все понял -- все оказалось, как и говорили --
сначала память захватывается не вся
s=3967
3967*3967*8 /1024/1024 = 120M
-- до вхождения в функцию -- 420K
-- после вызова всех new -- 35 M
-- после прогона цикла -- 139M // всего на 19 больше -- ну и забить на них
-- после вызова всех delete -- 420 K
Эх ... все я понял, еще раз спасибо за советы и сорри за непробиваемость.

yuda

Был бы ты не анонимус, получил бы 5...

SCIF32

Все, теперь не анонимус

ppplva

правильно говорит, при маллоке реально памяти не выделяется, а ставится отображение в специальную страницу памяти с copy-on-write. Выделение физической памяти происходит при первой же операции с ней.

freezer

не надо путать HeapAlloc и malloc. Вот помнится под досом malloc работал самым нормальным образом

ppplva

Оговорился. Под маллоком я понимаю выделение памяти ( средствами линукса).

freezer

т.е. под линуксом все так же, как и под виндой?

ppplva

Вообще-то я писал про линукс. Насчет винды не уверен, но скорее всего так же. Там ведь есть copy-on-write ?

abrek

вообще-то copy-on-write не имеет отношения к обсуждаемому вопросу

ppplva

да, возможна реализация и без него

ppplva

К calloc'у точно имеет. А malloc обходится без него ?

freezer

смотря как библиотеку соберешь... там есть такой препроцессорный макрос (или переменная ) WINHEAP, если ее не объявлять, то куча реализовывается ручками, а не через CreateHeap/HeapAlloc. Вообще, copy-on-write (точнее, прерывание при обращении к определенным адресам) - это скорее фича процессора и осей которые на нем крутятся. Она может быть, а может не быть (или не использоваться стандартной библиотекой С++)

abrek

в переводе на русский "copy-on-write" означает "копирование при записи"
но в твоём случае, похоже, это скорее означает "каша в голове"
поясни, что и при какой записи нужно копировать и как это связано с описанным поведением?

freezer

значит это вот что: когда одна и та же DLL грузится в разные процессы, она размещается в одной и той же физической памяти, но как только меняется состояние этой DLL (например, меняется глобальная переменная аллоцируется блок в физической памяти и туда все копируется. К alloc'ам это конечно отношения не имеет, то механизм в них похожий

ppplva

Ты в мою голову не заглядывал, так что с кашей пока повремени.
Я говорил про реализацию calloc, который инициализирует выделенную память нулями (казалось бы). Вместо этого он ставит отображение в некую специальную страницу памяти, забитую нулями, так что для чтения все ок. При записи происходит копирование.
Сорцов по этому поводу не читал, так что могу заблуждаться.

abrek

> Ты в мою голову не заглядывал, так что с кашей пока повремени.
А и не нужно заглядывать, достаточно посмотреть, что оттуда само выходит
> При записи происходит копирование.
Реализация жизнеспособная, но надуманная, так как заполнить страницу нулями эффективнее просто записав туда нули (странно, да? а вовсе не скопировав их откуда-то ещё

Papazyan

Ты слишком высокого мнения о malloc. COW работает только на уровне страниц, и вообще это идиотизм тратить прерывание на копирование области размер, которой может быть 1 байт.

VitMix

Copy-on-write --- это механизм, позволяющий сделать всякие forkи более эффективными. Это когда при порождении нового процесса не происходит полного копирования его адресного пространства. Вместо этого адресное пространство дочернего процесса частично или полностью собирается из тех же страниц, из которых собрано адресное пространство родительского процесса. Фактическое деблирование страниц происходит только тогда, когда один из процессов (родительский или дочерний) пытается что-то в этой странице изменить. Мне кажется что к mallocам и выделению памяти это отношения не имеет.
При выделении памяти используется другой механизм. Когда malloc понимает, что в куче места нет, он пытается расширить адресное пространство процесса (раньше для этого использовался системный вызов brk(2 который в современных системах скорее всего считается устаревшим). При этом вновь выделенные страницы не отображается ни в ОЗУ ни в swap, а просто помечаются как выделенные, но ещё не использованные. Фактическое отображение этих страниц происходит при первом обращении. Таким образом память действительно не выделяется до момента фактического обращения. При этом прерывание вызывается не для каждого байта, а для каждой страницы.

ppplva

> Реализация жизнеспособная, но надуманная, так как заполнить страницу нулями эффективнее просто записав туда нули (странно, да? а вовсе не скопировав их откуда-то ещё
Напиши простую программу, вызывающую calloc и убедись, что память выделяется не сразу (что имело бы место, если бы она просто заполнялась нулями а по мере использования.

ale-nikono

>Напиши простую программу, вызывающую calloc и убедись, что память выделяется не сразу (что имело бы место, если бы она просто заполнялась нулями а по мере использования.
Наличие применения copy-on-write это не докажет
Ничто не мешает выделять страницу и заполнять её нулями по факту возникновения исключения при записи (либо вообще брать её из пула заранее занулённых, если есть возможность такой пул поддерживать).

abrek

По существу ответил, тебе наверное эмоции помешали прочитать.
Суть моей претензии в том, что COW не исчерпывает набор техник, используемый подсистемой виртуальной памяти, хотя и называется красиво.
В частности, в данном случае он не используется, и вовсе не случайно, а потому, что просто не имеет отношения к этой задаче.
А по твоим постам создаётся впечатление, что ты выучил красивое слово, но не его значение. Успокойся и попробуй систематизироваить свои знания, и всё получится.

alex-kras

>Успокойся и попробуй систематизироваить свои знания, и всё получится.
Это надо повесить в рамочке над столом...

ale-nikono

>Это надо повесить в рамочке над столом...
Точно! Поверх этого дурацкого стеклянного экрана.

alex-kras

Поверх портрета научника

SCIF32

Может уже хватит? -- на мой взгляд тема исчерпана --
как это не назавайте, а все равно -- один хуй --
память выделяется при первом обращении к области данных
на этом, на мой взгляд и следует завершить дискуссию, а не переливать из пустого в порожнее.
я на свой вопрос получил ответ, а если у кого остались вопросы -- делайте новый тред.
ну или голование устройте -- кто типа самый клевый программер

ppplva

С эмоциями все в порядке.
Один человек, которого я весьма уважаю как программиста, когда-то сказал мне это, и я не удосужился проверить.
Однако.
Посмотрим в код glibc. calloc реализуется как malloc и mmap(/dev/zero).
Посмотрим в код ядра, а именно drivers/char/mem.c, там реализован /dev/zero. Грепни его, например, по слову "COW". Посмотри, например, функцию do_wp_page (Шурик, спасибо, без тебя я бы на это полдня потратил! ).
Теперь глубоко вдохни и попробуй ответить без эмоций и ненужной распальцовки.

Aleksei66

неверное утверждение. Верное - физическая память может выделиться при первом обращении. А может и не выделиться, если уже выделена.

ppplva

подколол

Aleksei66

Приведи, пожалуйста, man по calloc здесь.

ppplva

MALLOC(3) Linux Programmer's Manual MALLOC(3)
NAME
calloc, malloc, free, realloc - Allocate and free dynamic memory
SYNOPSIS
#include <stdlib.h>
void *calloc(size_t nmemb, size_t size);
void *malloc(size_t size);
void free(void *ptr);
void *realloc(void *ptr, size_t size);
DESCRIPTION
calloc allocates memory for an array of nmemb elements of size bytes each and returns a pointer to the
allocated memory. The memory is set to zero.
malloc allocates size bytes and returns a pointer to the allocated memory. The memory is not cleared.
free frees the memory space pointed to by ptr, which must have been returned by a previous call to mal-
loc calloc or realloc. Otherwise, or if free(ptr) has already been called before, undefined
behaviour occurs. If ptr is NULL, no operation is performed.
realloc changes the size of the memory block pointed to by ptr to size bytes. The contents will be
unchanged to the minimum of the old and new sizes; newly allocated memory will be uninitialized. If ptr
is NULL, the call is equivalent to malloc(size); if size is equal to zero, the call is equivalent to
free(ptr). Unless ptr is NULL, it must have been returned by an earlier call to malloc calloc or
realloc.
RETURN VALUE
For calloc and malloc the value returned is a pointer to the allocated memory, which is suitably
aligned for any kind of variable, or NULL if the request fails.
free returns no value.
realloc returns a pointer to the newly allocated memory, which is suitably aligned for any kind of vari-
able and may be different from ptr, or NULL if the request fails. If size was equal to 0, either NULL or a
pointer suitable to be passed to free is returned. If realloc fails the original block is left
untouched - it is not freed or moved.
CONFORMING TO
ANSI-C
SEE ALSO
brk(2 posix_memalign(3)
NOTES
The Unix98 standard requires malloc calloc and realloc to set errno to ENOMEM upon failure. Glibc
assumes that this is done (and the glibc versions of these routines do this); if you use a private malloc
implementation that does not set errno, then certain library routines may fail without having a reason in
errno.
Crashes in malloc free or realloc are almost always related to heap corruption, such as overflowing
an allocated chunk or freeing the same pointer twice.
Recent versions of Linux libc (later than 5.4.23) and GNU libc (2.x) include a malloc implementation which
is tunable via environment variables. When MALLOC_CHECK_ is set, a special (less efficient) implementa-
tion is used which is designed to be tolerant against simple errors, such as double calls of free with
the same argument, or overruns of a single byte (off-by-one bugs). Not all such errors can be protected
against, however, and memory leaks can result. If MALLOC_CHECK_ is set to 0, any detected heap corruption
is silently ignored; if set to 1, a diagnostic is printed on stderr; if set to 2, abort is called imme-
diately. This can be useful because otherwise a crash may happen much later, and the true cause for the
problem is then very hard to track down.
Linux follows an optimistic memory allocation strategy. This means that when malloc returns non-NULL
there is no guarantee that the memory really is available. In case it turns out that the system is out of
memory, one or more processes will be killed by the infamous OOM killer.
GNU 1993-04-04 MALLOC(3)

Aleksei66

Мнда. Ничего конретного. Не мог бы ты запостить еще реализацию calloc, если она небольшая, или хотя бы вызовы malloc и mmap оттуда. Я никак не могу понять, зачем ему вызывать их одновременно. Чтобы выделить память достаточно mmap.

ppplva

Там несколько страниц, и довольно тяжелый код. Я его очень слабо понимаю. Ясно только, что mmap реально делается. Тоже самое говорит strace. mmap делается не для /dev/zero, а анонимный (MAP_ANONYMOUS). Но реально это одно и то же.
ftp://kai.local/pub/temp/malloc.c - это из glibc-2.3.2

ppplva

Судя по комментариям, mmap делается для достаточно больших кусков памяти, начиная с 1Мб. Иначе - sbrk. Вроде так.

Aleksei66

Ну тогда понятно. calloc выделяет очень большие запросы через mmap и число таких запросов ограничено. Маленькие куски памяти зануляются вручную в calloc, большие зануляются автоматически по требованию. Т.е. все логично и понятно - при выделении небольших участков памяти никакого Copy on write не производится, не считая случая, когда куча разрослась и вылезла за свои прежние границы. И даже в этом случае этот COW 1) не имеет значения и 2) произойдет при выделении памяти, а не потом, когда пользователь будет писать. Когда выделяется большой кусок памяти, то используется mmap, а это значит, что указатель будет выровнен по размеру страницы и будет выделено количество памяти кратное размеру страницы (если не кратно, то округлят). После этого, если в не присутствующую в памяти страницу из этой области ты попытаешься что-нибудь записать, то произойдет прерывание, эта страница будет выделена и заполнена нулями. Кстати, /dev/zero звучит гордо, но по сути любая страница выделенная процессу будет перезаписана нулями, иначе было бы не секьюрно.

ppplva

Объясни пункт 2.

Papazyan

2 - участки памяти, выделенные без mmap, зануляются в calloc. Поэтому даже если физическая память не была выделена, она будет выделена при занулении.

ppplva

А, понял, это относилось к маленьким кускам памяти.

ppplva

Вообще-то, strace на вот такую программу
#include <stdlib.h>
main {
void *p = calloc(6000, 1000);
sleep(10);
free(p);
p = calloc(3000, 1000);
sleep(10);
free(p);
}
показывает два равнозначных mmap'а.

Papazyan

Какие же они равнозначные? Один на 6 мегов, а другой на 3.

ppplva

А смайлик где ?
Если бы оба были на 6, я бы сказал "одинаковые". А так - равнозначные

Papazyan

Тогда я не понял, что ты хотел сказать
Оставить комментарий
Имя или ник:
Комментарий: