![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
Не хватает памяти. [D5, D6, Win95/98, Win2k]
opoloXAI © (2004-07-12 12:02:00)Добрый день.
На Celeron400 с 65МГбт свободной памяти я динамически увеличиваю размер массива следующего типа:
type
TMyMassiv = record
v1: String;
v2: String;
v3: Integer;
end;{record}
var
mas: array of TMyMassiv;
Увеличиваю количество элементов массива на один и добавляю новый элемент. При достижении числа элементов массива около 4000..5000 сбодной памяти остается 0 Кбт. Дальше работа программы превращается в полные "тормоза".
Вероятнее всего я нерационально отнесся к памяти, наверное стоило воспользоваться TStream. Но я не знаю как пользоваться структурой TStream of TMyMassiv. Подскажите примерчиком кода.
Заранее спасибо.
Суслик (2004-07-16 14:05:00) [1]
> Не знаю какой тут конкретно.
здесь 1 б = 1с + накладные расходы.
Pavelkq (2004-07-16 13:54:00) [2]Я про конкретный пример
Path: String;Не знаю какой тут конкретно.
Суслик (2004-07-16 13:25:00) [3]
> 1байт = 1символ?
смотря какая строка - ansisting или widestring
Pavelkq (2004-07-16 13:17:00) [4]В Гб, не ошибаешься, а сколько это конкретно символов? 1байт = 1символ?
Суслик (2004-07-16 13:03:00) [5]
> Pavelkq (16.07.04 12:54) [46]
2гб если не ошибаюсь
Pavelkq (2004-07-16 12:54:00) [6]А сколько символов может содержать переменная стринг неопределенной длинны?
Суслик (2004-07-16 11:56:00) [7]
> Суслик © (16.07.04 11:55) [44]
чтобы понять неверность вашего подхода сделайте в конце showmessage(inttostr(allocmemsize)). При многократном выполнении операции вы увидите, что будет выводиться постоянно увеличивющееся значение.
Суслик (2004-07-16 11:55:00) [8]
> opoloXAI (16.07.04 11:44) [43]
> Мне только интересно, я с Dispose(Lst[i]) сделал все правилно?
Сейчас на меня будут кричать (застарелый спор), но вы сделали не верно.
Т.к. запись TmySearchRec содержит одно поле с управляемым временем жизни, т.е. типа string, то для того, чтобы был все корректно нужно, чтобы не только очисталась память из под записи, но и из под строки.
В вашем случае будет чиститься только из под записи.
надо делать так
var
p: pmySearchRec
begin
for ......
begin
p := list[i];
dispose(p);
end;
end;
Моя правота легко проверяется: достаточно посмотреть cpu - в вашем случае будет просто вызов freemem, в моем dispose.
opoloXAI (2004-07-16 11:44:00) [9]Мне только интересно, я с Dispose(Lst[i]) сделал все правилно?
opoloXAI (2004-07-16 11:41:00) [10]Добрый день. Я ркшил сделать так:
type
TmySearchRec = record
Size: Integer;
Name: TFileName;
Path: String;
end;
PmySearchRec = ^TmySearchRec;
TSameForm = class(TForm)
...
private
{ Private declarations }
kolFls: Int64;
GetPamat: Int64;
ListOfFiles: TList;
procedure ScanDir(StartDir: string);
procedure AddFile(Lst: TList; SchRec: TSearchRec; Dir: String);
procedure KillAllFiles(Lst: TList);
public
{ Public declarations }
end;
procedure TSameForm.FormCreate(Sender: TObject);
begin
ListOfFiles:=Tlist.Create;
end;
procedure TSameForm.FormDestroy(Sender: TObject);
begin
ListOfFiles.Free;
end;
procedure TSameForm.AddFile(Lst: TList; SchRec: TSearchRec; Dir: String);
p: PmySearchRec;
begin
kolFls:=kolFls+1;
New(P);
P^.Size:=SchRec.Size;
P^.Name:=AnsiUpperCase(SchRec.Name);
P^.Path:=Dir;
Lst.Add(P);
GetPamat:=GetPamat+SizeOf(SchRec.Size)
+length(SchRec.Name)+18
+length(Dir)+18;
end;
procedure TSameForm.KillAllFiles(Lst: TList);
var i: Integer;
begin
Lst.First;
For i:=0 to Lst.Count-1 do Dispose(Lst[i]);
Lst.Clear;
end;
procedure TSameForm.ScanDir(StartDir: string);
var SearchRec: TSearchRec;
begin
If StartDir[Length(StartDir)]<>'\' then StartDir:=StartDir+'\';
If FindFirst(StartDir+'*.*',faAnyFile,SearchRec)=0 then begin
repeat If (SearchRec.Attr and faDirectory)<>faDirectory then AddFile(ListOfFiles,SearchRec,StartDir)
else If (SearchRec.Name<>'..')and(SearchRec.Name<>'.') then ScanDir(StartDir+SearchRec.Name+'\');
until FindNext(SearchRec)<>0;
FindClose(SearchRec);
end;
StBar.Panels[2].Text:=' '+StartDir;
StBar.Panels[1].Text:=' Найдено : '+IntToStr(kolFls)+' файла(ов)';
StBar.Panels[0].Text:=' Память: '+myFileSize(GetPamat);
Application.ProcessMessages;
end;
procedure TSameForm.ItemFindClick(Sender: TObject);
var i: Integer;
p: PmySearchRec;
begin
kolFls:=0;
GetPamat:=0;
try ScanDir(DirectoryLB.Directory);
except Beep;
MessageDLG('Ошибка обращения к диску !',mtWarning,[mbYes],0);
end;
StBar.Panels[2].Text:=' Анализ данных ...';
ProgressBar.MinValue:=0;
ProgressBar.MaxValue:=ListOfFiles.Count-1;
ProgressBar.Progress:=0;
Application.ProcessMessages;
For i:=0 to ListOfFiles.Count-1 do begin
p:=ListOfFiles.Items[i];
{p^.Size}
{p^.Name}
{p^.Path}
ProgressBar.Progress:=i;
Application.ProcessMessages;
end;
KillAllFiles(ListOfFiles);
ProgressBar.Progress:=0;
end;
Теперь все работает отлично. Память совсем (ну почти совсем) не потребляется. Относительно первого варианта - это "небо и земля".
Суслик (2004-07-15 16:47:00) [11]ага, расходов будет в раз в 5 в среднем больше :)))
MacroDenS (2004-07-15 16:44:00) [12]А на мой взгляд,
автор при объявлении:
type
TMyMassiv = record
v1: String;
v2: String;
v3: Integer;
end;{record}
использует переменные типа стринг неопределенной длины!
а такая строка может занимать и до 2Гбт.
Если у тебя строки не очень длинные ( в пределах 255 символов), то лучше задай так:
TMyMassiv = record
v1: String[255];
v2: String[255];
v3: Integer[255];
end;{record}
и тогда ты с точностью до байта сможешь узнать размер одного элемента массива и проще будет отследить расходы памяти.
Pavelkq (2004-07-15 16:13:00) [13]Можно использовать TStringList для добавления, а потом сразу опредлив размер дин.массива, как TStringList.Count-1, в цикле перебросить данные. Получится 2 прохода, но все равно экономно и быстро.
Тимохов (2004-07-14 17:25:00) [14]я тут еще покапался.
если убрать строчки
mas[kolFls-1].Size:=123456;
mas[kolFls-1].Name:='proba.txt';
mas[kolFls-1].Path:='C:\Work\Delphi';
то нет никаких 120мб, только 2мб.
ясно, что эти строчки приводят к физическому копированию строки (это показал cpu). Но тогда не ясно почему же такая разница.
АВТОРУ.
К сожалению, нет времени глубже копать.
Поэтому совет однозначный:
1. Либо не пользоваться дин. массивом - использовать tlist.
2. Либо пользщоваться, но реализовать для дин. массива тот же подход, что и в TList: count + capacity (см. в исходниках tlist)
Тимохов (2004-07-14 17:01:00) [15]вообще говоря к концу цикла по данным диспетчера задач у меня занимает 120мб памяти.
при этом если ПОСЛЕ отработки цикла сделать minimize/maximaize окна, то по данным того же диспетчера приложение займет 2мб. Т.е. объединит и освободит лишние фрагменты.
При этом если добавлять не по 1, а выделять сразу все (или хотябы по многу, например по 1000 штук), то такого не будет.
Посмотрите в TList count+capacity - сделате так.
Готов признать, что в данном вопросе han_malign © скорее прав, чем не прав. Прошу прощения.
ЗЫ. Все-таки, чтобы немного оправдаться перед han_malign © скажу, что если внутри цикла вставить активную и независимую работу с манагером (строки, дин массивы, классы и т.д.), то такого эффекта не наблюдается. Т.е. мое отверждение про то, что манагер умеет объединять блоки и не всегда можно утвержадать про фрагментацию не лишено смысла.
Но еще раз в данном случае han_malign © прав.
opoloXAI (2004-07-14 16:33:00) [16]Добрый день.
Может ли кто-нибудь у себя откомпилировать (я делал на Delphi6) и проверить на Win98 следующий код:
procedure TForm1.Button1Click(Sender: TObject);
type TmySearchRec = record
Size: Integer;
Name: TFileName;
Path: String;
end;
var i: Integer;
kolFls,GetPamat: Int64;
mas: array of TmySearchRec;
begin
kolFls:=0;
GetPamat:=0;
SetLength(mas,kolFls);
For i:=0 to 10000 do begin
kolFls:=kolFls+1;
SetLength(mas,kolFls);
mas[kolFls-1].Size:=123456;
mas[kolFls-1].Name:='proba.txt';
mas[kolFls-1].Path:='C:\Work\Delphi';
GetPamat:=GetPamat+SizeOf(mas[kolFls-1].Size)
+length(mas[kolFls-1].Name)+18
+length(mas[kolFls-1].Path)+18;
Label1.Caption:=IntToStr(i);
Label2.Caption:=IntToStr(GetPamat);
Application.ProcessMessages;
end;
SetLength(mas,0);
end;
При работе программы запустить "Системеный монитор" и посмотреть "Свободная физическая память" и "Занято в файле подкачки". Посмотреть, что твориться. На моей здешней (на работе) машине по окончании работы программы свободной памяти 0 МГбт, подкачка - 60 МГбт. Может у меня на самом деле проблемы с этой машиной а не с Delphi? А как у Вас?
Тимохов (2004-07-13 14:55:00) [17]
> han_malign © (13.07.04 14:45) [32]
И вообще не обижайтесь.
Просто ваша формала
length(s)+1 = 2*length(s) + 1 весьма поспешна.
Дело в том, что setlength идут не подряд. Они перемижаются с работой со строками, которые тоже идут из манагера памяти. Поэтому, что там творится в манагере никому неизвестно. Может там полно огромных свободных блоков?
Да, согласен, что опыт показывает, что при последовательном увеличении памяти памяти жрестя больше чем нужно. Но согласитель, что не в 100 раз и это вряд ли может служить причиной ошибки, заявленной автором.
Тимохов (2004-07-13 14:51:00) [18]Хочу еще раз дополнить информацию о том как считать сколько занимает памяти непустая строка.
Как я сейчас выяснил - в д5 и д6 разная реализация длинных строк: в д5 длинна служебного блока строки равна 12 байт, в д6 - 8.
Поэтому в д5 накладных расходов на хранение строки + 22
В д6 + 18.
Тимохов (2004-07-13 14:49:00) [19]
> han_malign © (13.07.04 14:45) [32]
ладно, не возмущайтесь :)))
при сильно глубойкой рекурсии в стеке может быть и 4 байта под строку, а сама строка будет в штатном манагере памяти. Если вы возьмете строку длинной 100000 и будете ещее передаваь и в рекутсии и менять внутри, то получите именно конец пасмяти, а не стек оверфлов.
> стоит забывать про двусвязный список информации о непрерывных
> блоках.
Думаю его не надо брать во внимание. Т.к. свободные блоки имеют обыкновение объекдиняться и часто их не так много.
ЗЫ. Все вышесказанное про штатный манагер памяти - getmem.int.
han_malign (2004-07-13 14:45:00) [20]> А вы знаете? Вы знаете как работает с фрагментами манагер памяти дельфи?
- я то как раз знаю...
> Тогда может быть все дело в том, что сильно глубокая рукурсия?
- а вы знаете как работает рекурсия?
При "сильно глубокой рекурсии", будет Stack overflow. И уж стек никогда не свопируется и тормозов, сверх обычных, давать не может.
> +4{служебная инфа блока менеджера памяти дельфи }
- тогда выровнять на размер 4, и еще +4, поскольку флаг/размер повторяется на конце блока, со смещением кратным 4. Еще, тогда уж, не стоит забывать про двусвязный список информации о непрерывных блоках.
> потребляемую память на строку надо оценивать так
- если учитывать, что размер пути обычно(а для не NTFS всегда) не превышает 260(Windows.MAX_PATH), то 12000 записей это - 12000*(6+260+2*21(возьмем цифру от AP))=3696000 - так что считай не считай, а 141MБ не получится.