Форум: "WinAPI";
Текущий архив: 2006.08.20;
Скачать: [xml.tar.bz2];
ВнизПостоянное разбухание памяти, занимаемой программой Найти похожие ветки
← →
EarlVadim © (2006-04-24 12:22) [0]Суть такова.
Раз в минуту программа создает массив из потоков с FreeOnTerminate=true. В самих потоках используются
stfl:=TStringList.Create и FileStream :=TFileStream.Create. В равном им количестве присутствуют stfl.free и FileStream.free.
Результат - каждую минуту увеличение памяти на 50-100кБ.
Но что интересно, если программу свернуть в трей (для чего используется компонента TCoolTrayIcon и развернуть, то размер занимаемой памяти резко уменьшается до уровня первоначального запуска программы.
Подскажите где я мог накосорезить?
← →
Rouse_ © (2006-04-24 12:30) [1]Нужно код смотреть...
← →
EarlVadim © (2006-04-24 12:40) [2]да я в общем-то не против проект выслать, но это довольно большой код.
← →
Сергей М. © (2006-04-24 12:45) [3]Синхронизация (метод TThread.Synchronize) используется ?
← →
EarlVadim © (2006-04-24 13:01) [4]нет не используется.
← →
Сергей М. © (2006-04-24 13:04) [5]А что у тебя творится в основном потоке все то время, пока дополнительные что-то там делают в фоне ?
← →
Игорь Шевченко © (2006-04-24 13:06) [6]MemProof
← →
EarlVadim © (2006-04-24 13:08) [7]цикл по таймеру раз в 3000 опрашивает наличие готовых данных в property потоков. В конце каждого потока - Suspend;
Как только property READY всех потоков будет TRUE, всем потокам передается Resume, тем самым потоки заканчивают своё существование (должны) поскольку они FreeOnTerminate=true.
← →
EarlVadim © (2006-04-24 13:09) [8]
> MemProo
Я искал в сети но я так понял что она не для Винды. Или плохо искал...
может ссылку?
← →
Сергей М. © (2006-04-24 13:15) [9]
> цикл по таймеру раз в 3000 опрашивает наличие готовых данных
> в property потоков
Таймер-то зачем ? Вместе с suspend"ом/resum"ом ?
Доп.поток в состоянии сам известить осн.поток о завершении своей функциональной "деятельности". На то есть событие OnTerminate и вирт.метод DoTerminate.
← →
EarlVadim © (2006-04-24 14:06) [10]Так мне же все-равно писать эти данные в форму... или я чего-то не понимаю.
Я раз в 3 сек. переписываю Label.Caption"ы например, пока все потоки не отработают.
← →
Сергей М. © (2006-04-24 14:14) [11]Поток отработав возбудит OnTerminate, обработчик которого будет вызван в контексте осн.потока, где при этом можно "переписать" все что угодно - хоть Label.Caption"ы хоть Конституцию
← →
EarlVadim © (2006-04-24 14:22) [12]боюсь квалификации не хватит, но может попробую. Засада для меня в том,
что все элементы в форме создаются динамически (Labels : array of TLabel и т.д.), потоки тоже. и как связать всех их - сразу как-то теряюсь.
Но в любом случае - это просто более грамотный метод чтения данных потока, а проблему-то он вряд ли решит.
← →
Сергей М. © (2006-04-24 14:41) [13]
> как связать всех их
На то и существует упомянутый метод синхронизации.
> проблему-то он вряд ли решит
Начни с минимальной "грамотности" - пролблем ощутимо поубавится.
← →
EarlVadim © (2006-04-24 15:46) [14]Начать? Ну думаю адо начать с того, что есть.
Очень упрощая код такой..........
MAINUNIT
type
TMainForm = class(TForm)
procedure FormShow(Sender: TObject);
Procedure ReadData(Sender: TObject);
procedure RxTimerEvent1Timer(Sender: TObject);
procedure RxTimerEvent3Timer(Sender: TObject);
private
protected
public
MThr : MyThread;
Thread : array of MyThread;
Labels: array of TsLabel;
MyLabel : TsLabel;
Gauges: array of TsGauge;
MyGauge : TsGauge;
var
MainForm: TMainForm;
implementation
procedure TMainForm.FormShow(Sender: TObject);
var I : integer;
begin
{ HOSTS : integer - читаю из реестра }
setlength(Labels, Hosts);
setlength(Gauges, Hosts);
setlength(Thread, Hosts);
For i:= 0 to HOSTS-1 do begin
MyLabel := TsLabel.Create(self);
........................
Labels[I] := MyLabel;
MyGauge := TsGauge.Create(self);
........................
Gauges[I] := MyGauge;
MThr := MyThread.Create(HostPath[i]); // передаю UNC-путь файла
MThr.Priority := tpLowest;
MThr.FreeOnTerminate := true;
MThr.Resume;
Thread[i]:= MThr;
end;
Procedure TMainForm.ReadData(Sender: TObject);
Var i1 : integer;
Cycle : boolean;
Begin
Cycle:= true;
For i1:= 0 to Hosts-1 do
IF Thread[i1].Ready THEN BEGIN
Gauges[i1].Progress:= Thread[i1].Progress;
Labels[i1].Caption:= "Имя - "+Thread[i1].Name;
END else Cycle:= false;
// если все потоки отработали, то флаг=true если нет=false
End;
procedure TMainForm.RxTimerEvent1Timer(Sender: TObject);
Var It : integer;
begin
RxTimerEvent1.Enabled:= false;
RxTimerEvent3.Enabled:= true;
For It:= 0 to Hosts-1 do Thread[it].Resume;
end;
procedure TMainForm.RxTimerEvent3Timer(Sender: TObject);
var I1, Ix : integer;
begin
ReadData(Sender);
end;
MyThreadUnit
interface
MyThread = class(TThread)
private
CF : String;
FName : String;
FProg : integer;
FReady : boolean;
FCheck : boolean;
protected
procedure Execute; override;
public
constructor Create(InPar : String);
function GetReady: Boolean;
function GetName: string;
function GetProg: integer;
property Check: Boolean Read FCheck Write FCheck;
property Ready: Boolean Read GetReady;
property Name: string Read GetName;
property Progress: integer Read GetProg;
end;
implementation
constructor MyThread.Create(InPar : String);
begin
CF:= InPar;
FReady:= false;
FCheck:= true;
inherited Create(true);
end;
function MyThread.GetReady: boolean;
begin
Result:= FReady;
end;
function MyThread.GetName: string;
begin
Result:= FName;
end;
function MyThread.GetProg: integer;
begin
Result:= FProg;
end;
procedure MyThread.Execute;
begin
While FCheck do BEGIN
FName:= .....;
FProg:= ......;
FReady:= true;
Suspend;
FReady:= false;
END;
end;
← →
Сергей М. © (2006-04-24 16:05) [15]Код никуда не годится.
Очень много вопросов к нему.
Радикальной переделки требует практически все.
← →
EarlVadim © (2006-04-24 16:16) [16]Ну, любой код можно написать минимум 3-мя способами.
Мой, согласен, не займет призовых мест, но он работает.
Вот к вопросу о разбухании памяти какие-то конкретные замечания по коду есть?
Я кстати сильно урезал метод чтения файлов в MyThreadUnit.
Именно там используется
stfl:=TStringList.Create;
FileStream :=TFileStream.Create;
..................
stfl.free;
FileStream.free;
← →
Сергей М. © (2006-04-24 16:18) [17]
> к вопросу о разбухании памяти какие-то конкретные замечания
> по коду есть?
Нет кода - нет и замечаний.
> Именно там используется
Именно "там" и иллюстрируй в коде.
← →
EarlVadim © (2006-04-24 16:44) [18]2Сергей М. ©
Нет желания сказать что-то путное, так и писать не надо.
Кроме общих фраз и "фи" пока знаний предмета не увидел.
Кто-нибудь все-таки по-существу что поправит?
← →
Сергей М. © (2006-04-24 17:08) [19]
> знаний предмета не увидел
Ты тут "экзаменатора" не строй из себя. Рано еще, судя по алгоритму.
Хочешь "что-то путное" ?
Выеладывай ключевые моменты алгоритма, скрытые под "..................." в [16].
Про крит.секции, которые ты напрочь игнорируешь, потом поговорим.
← →
EarlVadim © (2006-04-24 18:00) [20]
procedure MyThread.Execute;
begin
While FCheck do BEGIN
STFL:= TStringList.Create;
If FileExists(CF) then begin
try
FileStream := TFileStream.Create(PChar(CF),
fmOpenRead or fmShareDenyRead);
finally
try
STFL.LoadFromStream(FileStream);
finally
FName:= STFL.Strings[0];
FProg:= StrToIntDef(STFL.Strings[1],0);
end;
FileStream.Free;
end;
end;
STFL.Free;
FReady:= true;
Suspend;
FReady:= false;
END;
end;
← →
sniknik © (2006-04-24 18:31) [21]> Suspend;
????? внутри Execute, а как же сработает FreeOnTerminate=true ?
и несущественное, нафига создавать STFL еще не зная понадобится он или нет?
и посущественнее, обработки ошибок нет.
← →
EarlVadim © (2006-04-24 18:53) [22]
> а как же сработает FreeOnTerminate=true
Он сработает один раз, когда программа завершится.
В OnClose передав в потоки
Thread[i].Check:= false;
Thread[i].Resume;
> нафига создавать STFL
Логично, не задумывался. Изменю.
> обработки ошибок нет
Так случаев фатальных ошибок с вылетом пока небыло в этом месте.
Если чтение из файла не состоялось, принимаются значения по умолчанию.
Обе Free отрабатывют, а память растёт.
← →
sniknik © (2006-04-24 21:42) [23]> Он сработает один раз, когда программа завершится.
ну и что ты тогда хочеш в процессе работы? жди пока программа завершится... и осводит потоки.
и надейся что нет путаници, что не делаеш Resume какому нибудь потоку раньше чем он отработал и установил Suspend...
кстати зачем цикл? если поток строго только 1 раз отрабатывает. зачем дополнительная переменная выхода из цикла, во первых все одно один раз работает, на Suspend застопорится, во вторых есть стандартная Terminated и метод установки Terminate. и зачем вообще ждать пока отработают все потоки перед завершением? пусть бы себе отрабатывали да закрывались...
не пойму, с логикой чтото запутано/замудрено.
> Так случаев фатальных ошибок с вылетом пока небыло в этом месте.
обработка должна быть на тот случай когда будут... сэмулируй, закрой права на чтение/открой в другом месте эксклюзивно и помосмотри что будет... обработка как раз чтобы этого не было.
← →
Slym © (2006-04-25 04:53) [24]EarlVadim © (24.04.06 18:00) [20]
1. STFL.Strings[0]; - Outofbounds :)
2. sniknik © (24.04.06 18:31) [21]
нафига создавать STFL еще не зная понадобится он или нет?
И это верно
3. зачем If FileExists(CF) then begin если всеравно в try оборачиваешь?
4. Вложенные finally сложновато...
5. STFL.Free; не защищен finally...
6. Suspend внутри не есть гуд
← →
Slym © (2006-04-25 05:05) [25]7. Ты конечно об этом не думал, но твой файл может оказать ся не "твоим", а каким-нибудь "c:\films\porno.avi" 700Мб, а ты его в TStringList! памяти покушает :) лучше смотреть в сторону ReadLn
function ReadLn(Stream:TStream):string;
const cCR = #13; cLF = #10; cEOF = #26;
var Ch:Char;
begin
result:="";
while Stream.Read(Ch,1)>0 do
begin
if Ch=cLF then break;
if Ch=cEOF then break;
if Ch<>cCR then
begin
result:=result+Ch;
continue;
end;
if Stream.Read(Ch,1)=0 then break;
if Ch=cLF then break;
if Ch=cEOF then break;
result:=result+cCR+Ch;
end;
end;
procedure Execute;
var
FileStream:TFileStream;
begin
While FCheck do
try
FileStream:=TFileStream.Create(PChar(CF),fmOpenRead or fmShareDenyRead);
try
FName:=ReadLn(FileStream);
FProg:=StrToIntDef(ReadLn(FileStream),0);
finally
FileStream.Free;
end;
FReady:= true;
except
FReady:= false;
END;
Suspend;
end;
← →
EarlVadim © (2006-04-25 11:04) [26]Поясню ещё немного.
Поток, отработав засыпает - Suspend.
Но через минуту, по таймеру основного потока получает Resume и отрабатывает полностью с самого начала.
В этот момент в основном потоке таймер Enabled=false.
Когда основной поток увидит, что все дочерние потоки отработали и заснули, он снова запустит таймер на 1 минуту.
Это ответ на вопрос о логике. Что касается эмуляции ошибки, то до них ещё просто не добрался. Все критические моменты программы я довольно чётко понимаю. Но на некоторые я временно "забил".
Что касается длины файлов, то я точно знаю, что это за файлы. И точно знаю в какой момент их можно читать. Так что здесь мин нет.
А вообще я впервые остановился и посмотрел на свой код немного со стороны и вынужден согласится с товарищем, который ничего конкретного не посоветовал, зато прав оказался в главном. Код буду переписывать с нуля.
Основной смысл - потоки должны умирать. а через минуту буду создавать новые и так далее.
← →
evvcom © (2006-04-25 11:25) [27]
> Все критические моменты программы я довольно чётко понимаю.
Ой ли?
> Основной смысл - потоки должны умирать. а через минуту буду
> создавать новые и так далее.
Неправильно ты понял основной смысл. Более основным смыслом было желание тебе сказать, что поток может сам информировать основной поток о завершении части задачи ему предназначенной (см. Synchronize, Post/SendMessage, другие пока не надо). И совсем не обязательно, что после этого он должен умереть. Может и уснуть. А вот использование таймеров здесь неоправдано. И постоянные циклы (хоть и происходящие с паузами) по проверке состояний ни к чему.
← →
EarlVadim © (2006-04-25 12:41) [28]С Synchronize попробую.
В инете встречал, даже пробовал в варианте со статичной парой поток-элемент. работало. Почему не пошёл дальше даже уже не помню.
Убиванием потока я попытаюсь освобождать память занимаемую им вместе со всеми его переменным, потому что точно знаю, что жрут память у меня именно потоки.
А вот без таймера никак.
Хотя можно в конце цикла ставить не Suspend а Sleep(60000).
← →
Slym © (2006-04-25 12:53) [29]EarlVadim © (25.04.06 12:41) [28]
Чушь! и gосле закрытия программы, эта самая программа будет висет в поцессах 60 сек... с опцией "Не отвичает"
Если надо уснуть то Event.WaitFor(60000) и если нужно досрочно разбудить... Event.PulseEvent
← →
Сергей М. © (2006-04-25 12:53) [30]
> EarlVadim © (25.04.06 12:41) [28]
> потому что точно знаю, что жрут память у меня именно потоки.
Это ты обнаружил под отладчиком ?
Или это твои "умозаключения" ?
Ты вообще пользуешься ли отладчиком для решения своих проблем ?
← →
EarlVadim © (2006-04-25 14:07) [31]почему не пользуюсь, под отладчиком все шоколадно.
Перед Suspend все динамические переменные потоков в nil.
А память тогда где?
← →
Сергей М. © (2006-04-26 08:26) [32]
> EarlVadim © (25.04.06 14:07) [31]
> память тогда где?
Ты хоть раз получил Out of memory ?
Если утечка действительно имеет место быть, то при наблюдаемой тобой динамике наращивания размера потребляемых ресурсов памяти исключение произойдет довольно скоро ...
← →
EarlVadim © (2006-04-26 09:29) [33]До Out of memory не доходит. Завершаю раньше.
Когда я утром прихожу на работу и вижу что моя программа вместо 10Мб при старте занимает 250Мб, то не надо быть семи пядей во лбу чтобы понять что это ненормально.
← →
Сергей М. © (2006-04-26 09:51) [34]
> вместо 10Мб при старте занимает 250Мб
И все это время программа была "свернута в трей" ?
← →
Slym © (2006-04-27 06:17) [35]Кстати кроме памяти (физической), TaskManager умеет показывать другие показатели которые иногда более информативны: виртуальная память, счетчик потоков/дескрипторов/объектов
судя по всему у тебя пухнет счетчик потоков
Страницы: 1 вся ветка
Форум: "WinAPI";
Текущий архив: 2006.08.20;
Скачать: [xml.tar.bz2];
Память: 0.56 MB
Время: 0.038 c