Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2002.08.19;
Скачать: CL | DM;

Вниз

Глюки, связанные с TListView и памятью   Найти похожие ветки 

 
Smok_er   (2002-05-29 13:10) [0]

Уважаемые мастера!
Помогите пожалуйста.
Я пишу дипломную работу - анализ логфайлов Apache, результаты анализа вывожу в сабже. После вывода очередной статистики сильно заполняется память, причем по какой-то непонятной мне причине память от предыдушего просмотра совершенно другой статистики не освобождается (использую метод Clear).
В результате, после 3-4 просмотров появляется ошибка Out of resources и винда начинает конкретно глючить.
В принципе, не пойму еще и то, почему не освобождается память TStringList"a. Т.е., при использовании что метода Free, что более жесткого Destroy - память, используемая программой не уменьшается :(
Может быть кто-то знает, как с этим бороться?


 
Digitman ©   (2002-05-29 13:35) [1]

Ну и как ты себе мыслишь это - без оригинального кода сказать тебе, что и где ты там натворил некорректно ?


 
Smok_er   (2002-05-29 13:56) [2]

ОК!
Вот код вывода в ListView статистики, например по User-Agent"ам.
Вся остальная выводится также.
Причем основной глюк приходится на тот момент, когда я прокручиваю scroll bar.
Т.е. память под программу резко увеличивается, а после перехода на другую статистику не уменьшается.
В данном примере в ListView выводится порядка 400 записей.
PrepareListView;
NewColumn := frMain.lvReports.Columns.Add;
NewColumn.Caption := "User-Agent";
NewColumn := frMain.lvReports.Columns.Add;
NewColumn.Caption := "Hits";
frMain.lvReports.Columns.Items[0].Width:= frMain.lvReports.Width-60-20;
frMain.lvReports.Columns.Items[1].Width:= 60;
frMain.lvReports.Items.BeginUpdate;
for i:=Low(UserAgent) to High(UserAgent) do
begin
ListItem := frMain.lvReports.Items.Add;
ListItem.Caption := UserAgent[i].Name;
ListItem.SubItems.Add(IntToStr(UserAgent[i].Hits));
end;
frMain.lvReports.Items.EndUpdate;


 
NailS ©   (2002-05-29 16:48) [3]

А ты уверен, что именно здесь собака порылась?
Может просто UserAgent не освобождаются после использования?


 
Smok_er   (2002-05-29 17:05) [4]

Так UserAgent постоянно сидит в памяти.
Он сформировался после обработки лога.

Вот в другом я точно уверен. В данном примере str не освобождается почему-то :(
str:= TStringList.Create;
...
... Операции над этим листом
...
str.Free;

И еще одна непонятка - на форме установлен Splitter. Так вот почему то при его движении постоянно идет прибавление памяти, расходуемой программой. Т.е., например, если его дернуть раз 50, то память прибавляется метров на 5 до тех пор, пока не вылезет мессага "Out of Resources"

Господа, выручайте, как бороться с этим недугом?


 
NailS ©   (2002-05-29 17:54) [5]


> Вот в другом я точно уверен. В данном примере str не освобождается
> почему-то :(
> str:= TStringList.Create;
> ...
> ... Операции над этим листом
> ...
> str.Free;


Бред какой-то. Чудес не бывает. Попробуй мемпруфом посмотреть утечки памяти.
http://www.automatedqa.com/downloads/memproof.asp


> Так вот почему то при его движении постоянно идет прибавление
> памяти, расходуемой программой. Т.е., например, если его
> дернуть раз 50, то память прибавляется метров на 5 до тех
> пор, пока не вылезет мессага "Out of Resources"

А случаем на OnCustomDraw... ничего не делаешь?


 
Digitman ©   (2002-05-29 18:23) [6]

1.какие события сплиттера и ListView обрабатываешь ? Где код ?
Опять гадать будем ?

2.
str.Free выполняется ? ты ловил брейкпойнт на этой строчке ?


 
Smok_er   (2002-05-29 23:11) [7]

2 Nails
Уже качаю...
Нет, абсолютно ничего
просто treeview и listview со сплиттером.
Сам в шоке.
Причем память растет только в том случае, если в листвью есть какие-то данные (либо они там были вообще). Т.е. методом clear происходит только видимое очищение, а на самом деле память не высвобождается.

2Digitman
Обрабатываю только одно событие от тривью
но мне кажется, что дело не в этом.
вот код:
procedure TfrMain.ReportsClick(Sender: TObject);
begin
case Reports.Selected.ImageIndex of
101: Report.BasicStatistics;
103: Report.MostActiveDays;
104: Report.MostActiveHours;
106: Report.MostActiveMonths;
107: Report.DetailDailyActivity;

123: Report.MostCommonReferrerSites;
124: Report.MostCommonReferrerURLs;
128: Report.SearchPhrases;

134: Report.MostUserBrowsers;
136: Report.OnlineBrowsers;
137: Report.OfflineBrowsers;

138: Report.DownloadManagers;
142: Report.UserAgents;
end;
end;


Для удобства последующей русcификации для наших профессоров я использую imageindex для определения, куда кликнул.
Сам код одного из таких отчетов приведен выше.
str.Free выполняется, никаких ошибок не выдает.
пробовал и чистить перед удалением - ничего не помогло.


 
Smok_er   (2002-05-30 00:54) [8]

Блин, ну я тут намутил воду.
А Nails верно задал резонный вопрос, на который я опрометчиво ответил нет :(
Да, я совсем забыл
У тривью я раскрашиваю ячейки в разные цвета как раз методом OnCustomDraw
Но отчего тогда такое происходит при использовании этого метода? Или это очередная корявость инпрайза?


 
int64 ©   (2002-05-30 05:44) [9]

А ты уверен, что и на этот раз правильно локализовал ошибку?
Отключи OnCustomDraw и посмотри: есть утечка.
Если есть, код в студию.

В TListView проблем с утечкой нет - всё коректно удаляется.
Это ты что-то намутил; с UserAgent например.


 
NailS ©   (2002-05-30 11:33) [10]

Когда смотришь за памятью следи в Диспетчере задач, то наблюдай за виртуальной памятью, этот параметр более информативный, чем используемая память при ловле утечки


 
[aka]   (2002-05-30 13:05) [11]

Почти наверняка "текущий" OnCustomDraw. Если выделяешь и хранишь объекты в TTreeNode.Data, то TTreeNodes.Clear не вызывает OnDelete для нод, так что там утечка тожа гарантирована.


 
Smok_er   (2002-05-30 13:29) [12]


procedure TfrMain.lvReportsAdvancedCustomDrawItem(Sender: TCustomListView;
Item: TListItem; State: TCustomDrawState; Stage: TCustomDrawStage;
var DefaultDraw: Boolean);
begin
if isURL(Item.Caption) then
begin
(Sender as TListView).Canvas.Font.Color:= $683E3E;
(Sender as TListView).Canvas.Font.Style:= [fsUnderline];
end;

if (Item.Index mod 2) = 0 then
(Sender as TListView).Canvas.Brush.Color:= $EAFFFF
else
(Sender as TListView).Canvas.Brush.Color:= clWhite;
end;

procedure TfrMain.lvReportsAdvancedCustomDrawSubItem(
Sender: TCustomListView; Item: TListItem; SubItem: Integer;
State: TCustomDrawState; Stage: TCustomDrawStage;
var DefaultDraw: Boolean);
begin
(Sender as TListView).Canvas.Font.Color:= clBlack;
(Sender as TListView).Canvas.Font.Style:= [];
if (Item.Index mod 2) = 0 then
(Sender as TListView).Canvas.Brush.Color:= $EAFFFF else
(Sender as TListView).Canvas.Brush.Color:= clWhite;
end;



Все дело именно в customdraw
Как только я закомментировал эти 2 события, то глюки сразу прекратились.
Кстати, был вынужден использовать именно 2 события из-за невозможности (во всяком случае у меня не получилось) не выделять всю строку листвью, а только отдельные ячейки, удовлетворяющие какому-то условию.
В данном случае у меня идет проверка на URL и в случае варианта, если текст в ячейки - ссылка, то выделение среди остальных ячеек.
Причем остальные ячейки в этой строке не должны выделяться


 
NailS ©   (2002-05-30 13:51) [13]

Не разу не видел, чтобы простое присвоение колора и фонта приводило к MemoryLeak, запости сюда функцию isURL, меня терзают смутные сомнения на ее счет.


 
AlexSV   (2002-05-30 14:34) [14]

В свое время в конференции обсуждался аналогичный вопрос.

Собственно вопрос:
> Есть у меня ListView у него например 3-и колонки и свойство
> ViewStyle=vsReport. Запихиваем туда порядка 200 пунктов среди которых
> бальшинство пунктов с русским названием. В обработчике OnCustomDrawItem
> пишем одну строку
> Sender.Canvas.Font.Color:=clred
> Запускаем приложение. Естественно все 200 пунктов у список не помещаются,
> автоматом возникает полоса прокрутки. Выбираем первый пункт и затем раз
> 20-30 нажимаем Ctrl+End Ctrl+Home
> т.е. туда сюда. Так вот после очередного нажатия Ctrl+End Ctrl+Home пункты
> перерисовываются очень медленно и вместо названий пунктов появляются
> квадратики, будто шрифт не русский. Самое интересное, что иконки с русскими
> названиями, на рабочем столе тоже становятся не читабельны.
> После выхода из программы иконки на рабочем столе восстанавливают свой
> прежний вид.

Привожу ответ, может поможет:

Это ошибка в обработчике CN_Notify:
ComCtrls.pas: TCustomListView.CNNotify

Найдите это место:

if GetObject(FCanvas.Font.Handle, SizeOf(LogFont), @LogFont) <> 0 then
begin
FCanvas.Handle := 0; // disconnect from hdc
// don"t delete the stock font
SelectObject(hdc, CreateFontIndirect(LogFont));
Result := Result or CDRF_NEWFONT;
end;

Всякий раз при изменении TListView.Canvas будет расходоваться память
Можно это поправить примерно так:

if GetObject(FCanvas.Font.Handle, SizeOf(LogFont), @LogFont) <> 0 then
begin
FCanvas.Handle := 0; // disconnect from hdc
// don"t delete the stock font
if FNewFont <> 0 then DeleteObject(FNewFont);
FNewFont := CreateFontIndirect(LogFont);
SelectObject(hdc, FNewFont);
Result := Result or CDRF_NEWFONT;
end;
Для этого потребуется в описании класса TCustomListView добавить
переменную FNewFont: HGDIObj

То же самое касается TCustomTreeView - та же функция, та же ошибка.

Скопируйте измененный ComCtrls.pas в каталог с проектом.

----
© Андрей А. Лобанов <loba@nersa.ee>
NERSA Limited


 
Smok_er   (2002-05-30 15:37) [15]

Спасибо большое за ответы, но только что опробовал - не помогло. Дергаю прокруткой, сплиттером - и происходит бешенная накрутка памяти до тех пор, пока не начинаются конкретные глюки.
Вот функция isURL:

function IsURL(StringForCheck: String): Boolean;
begin
if Pos("http://",StringForCheck)=1 then
Result:=True
else Result:= False;
end;


Проще не бывает. Здесь я вижу 2 подхода - либо как-то оптимизировать обработчик события, либо отказаться от него вообще.
Не думаю, что на сдаче дипломной ко мне придерутся из-за того, что ячейки не раскрашиваются в разные цвета.
Разве что самому интересно найти правильное решение.


 
NailS ©   (2002-05-30 15:45) [16]

Попробуй сменить описание функции на
function IsURL(const StringForCheck: String): Boolean;

У меня был случай, когда это помогло. Хотя здесь ...незнаю.


 
Smok_er   (2002-05-30 16:14) [17]

Нет, не помогло.
Здесь на 100% видно, что причина - CustomDraw
Почему-то не освобождается память после использования, даже сделав так, как советует AlexSV


 
NailS ©   (2002-05-30 16:43) [18]

Собрал тестовый проект с твоими обработчиками, загнал 40000 записей, все работает. Память вроде не жрет.
Что говорит MemProof?
И что под всеми этими системами схожая ситуация?
>Win95/98, WinME, NT4, Win2k, WinXP


 
Smok_er   (2002-05-30 17:44) [19]

Нет, глючит под 98,2000 и ХР - это проверено
Остальные, догадываюсь, тоже самое
А можно глянуть на этот проект?
Желательно попробовать его запустить у себя

А по MemProof не нашел документацию.
Видно, что память сжирается, а как посмотреть куда - так и не узнал. Могу только сказать, что на указатели (Pointers)


 
NailS ©   (2002-05-30 18:08) [20]

MemProof показывает даже место где память была выделена и собрать проект надо с TD32 Info пути к исходникам настраиваются в Options/Search Directories

Сам проект

unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, ComCtrls, ExtCtrls;

type
TForm1 = class(TForm)
Panel1: TPanel;
Panel2: TPanel;
lv: TListView;
Button1: TButton;
Button2: TButton;
Splitter1: TSplitter;
Panel3: TPanel;
procedure lvAdvancedCustomDrawItem(Sender: TCustomListView;
Item: TListItem; State: TCustomDrawState; Stage: TCustomDrawStage;
var DefaultDraw: Boolean);
procedure lvAdvancedCustomDrawSubItem(Sender: TCustomListView;
Item: TListItem; SubItem: Integer; State: TCustomDrawState;
Stage: TCustomDrawStage; var DefaultDraw: Boolean);
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;

var
Form1: TForm1;

function IsURL(StringForCheck: String): Boolean;

implementation

{$R *.DFM}

procedure TForm1.lvAdvancedCustomDrawItem(Sender: TCustomListView;
Item: TListItem; State: TCustomDrawState; Stage: TCustomDrawStage;
var DefaultDraw: Boolean);
begin
if isURL(Item.Caption) then
begin
(Sender as TListView).Canvas.Font.Color:= $683E3E;
(Sender as TListView).Canvas.Font.Style:= [fsUnderline];
end;

if (Item.Index mod 2) = 0 then
(Sender as TListView).Canvas.Brush.Color:= $EAFFFF
else
(Sender as TListView).Canvas.Brush.Color:= clWhite;

end;

procedure TForm1.lvAdvancedCustomDrawSubItem(Sender: TCustomListView;
Item: TListItem; SubItem: Integer; State: TCustomDrawState;
Stage: TCustomDrawStage; var DefaultDraw: Boolean);
begin
(Sender as TListView).Canvas.Font.Color:= clBlack;
(Sender as TListView).Canvas.Font.Style:= [];
if (Item.Index mod 2) = 0 then
(Sender as TListView).Canvas.Brush.Color:= $EAFFFF else
(Sender as TListView).Canvas.Brush.Color:= clWhite;
end;


function IsURL(StringForCheck: String): Boolean;
begin
if Pos("http://",StringForCheck)=1 then
Result:=True
else Result:= False;
end;


procedure TForm1.Button1Click(Sender: TObject);
var
i : integer;
ListItem : TListItem;
begin
i := 0;
lv.Items.BeginUpdate;
while i < 40000 do
begin
ListItem := lv.Items.Add;
ListItem.Caption := " http://www.123.com";
ListItem.SubItems.Add(Format("test %d",[i]));
Inc(i);
end;
lv.Items.EndUpdate;

end;

procedure TForm1.Button2Click(Sender: TObject);
begin
lv.Items.BeginUpdate;
lv.Items.Clear;
lv.Items.EndUpdate;
end;

end.


 
Smok_er   (2002-05-31 01:01) [21]

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

Да, MemProof это сила!
Попробую объяснить свою проблему.
Дело в том, что я использую TStringList для более удобного добавления записей, например рефереров или useragent"ов.
Причем ввиду неэффективности использования метода IndexOf приходится сначала заполнять стринглист записями (некторые реферереры занимают по 512 символов). Получается, что я сначала заполняю его, а только потом сортирую с использованием сортировки и прохода с начала до конца на соответствие подряд идущих записей. В связи с этим резонный вопрос - есть ли более эффективный и самое главное как можно быстрый способ определения наличия соответствующей записи?
И встречный вопрос - если сразу после создания стринглиста его отсортировать, то новые записи будут добавляться с учетом сортировки или в самый конец?


 
Smok_er   (2002-06-06 00:11) [22]

Nails, извини что так долго не отвечал, были проблемы с инетом.
Получил твое письмо, большое спасибо!
Скомпилировал, долго ждал результата заполнения листвью, и в принципе опятьже появились проблемы с увеличением памяти, только не в таких пределах. Я думаю, что в моем случае проблема в том, что данные не вмещаются в ячейки...
Кстати, еще одна особенность - при достижении определенного объема памяти, расходуемого программой, последующее наращивание не происходит, как ни дергать скроллбары.
Я в конце концов отказался от всякой красоты, но все равно очень интересно, почему память не освобождается после вызова метода CustomDraw. И опять же... В твоем примере после нескольких прокруток память увеличилась примерно на 4 Мб, и мне кажется, что это многовато.


 
NailS ©   (2002-06-06 11:05) [23]


> твоем примере после нескольких прокруток память увеличилась
> примерно на 4 Мб, и мне кажется, что это многовато.

Честно говоря, такого эффекта не наблюдал.
Ты как за памятью смотришь?
Если в TaskManagere параметр Память, то просто сверни приложение и будешь приятно удивлен ;)



Страницы: 1 вся ветка

Текущий архив: 2002.08.19;
Скачать: CL | DM;

Наверх




Память: 0.55 MB
Время: 0.01 c
1-60704
delta
2002-08-06 16:12
2002.08.19
Потоки


3-60502
alexvan
2002-07-28 18:32
2002.08.19
Просто, но чего-то нигде не могу найти.


6-60764
neodiX
2002-06-04 12:28
2002.08.19
Это действительно проблема!


6-60774
Новеньки в Делфи
2002-06-06 16:22
2002.08.19
в дельфе Cgi


1-60586
Nikoss
2002-08-06 00:10
2002.08.19
Подскажите, как узнать из какой процедуры была вызванна процедура