请教个delphi关于占用和释放内存的问题

Description of your first forum.

请教个delphi关于占用和释放内存的问题

帖子popogens » 星期五, 2007年2月2日 23:32


本人编了个用于打印的软件,由于考虑到RichEdit、RichEdit2功能不够强大,而delphi控制word方法太复杂,因而采用了折衷方法:用TMemo生成文本,用特定的命领在TMemo里读入、修入内容,另存为htm文件,再打开它。
别看方法怪,可是挺实用。
但新的问题产生了:多次调用时会产生错误,经多次检查,没发现哪里程序有错,而且在单位电脑上(配置较差)重复试3-4次会产生错,在家里电脑上,一般是第8次产生错误,我怀疑是内存被占用过多,在家里重复执行了6-7次,用内存整理工具整理一次,再执行6次,再整理……始终没发生错误。确定是占用内存过多。
PS:我还用了很多个Tinifile控件

现在我想请教一下:哪些东西比较占内存并且不被释放?这个我不太了解,看来以后得注意了,还有,Delphi里有什么方法能释放一些不被使用的内存吗?
 
 
 

请教个delphi关于占用和释放内存的问题

帖子witkey » 星期五, 2007年2月2日 23:44


一般情况你要使用对象变量,用完之后,要释放掉。
var
  Ainifile:Tinifile;
bagin
   Ainifile:=Tinifile.cerate;
{.......}

  Ainifile.free;//这个就释放掉了
end;
=======
有比如:
var
F:TForm1;
bagin
F:=TForm1.cerate(nil);
{...}
F.Free;
end;

而这个:
var
  F:TFrom1;
begin
   F:=TForm1.cerate(self);
{.........}
  //就不用F.free;
end;
 
 
 

请教个delphi关于占用和释放内存的问题

帖子liyinwei » 星期六, 2007年2月3日 00:04


要看看程序是否占用太多内存,用 任务管理器 看看程序的物理内存和虚拟内存的占用情况就会知道了。
一般来说手工 Create 的类都需要在适当的地方手工 Free 掉来释放内存。
var
  F: TIniFile;
begin
  F := TIniFile.Create('...');
  try
    ......
  finally
    F.free
  end;
end;
 
 
 

请教个delphi关于占用和释放内存的问题

帖子popogens » 星期六, 2007年2月3日 20:31


form和tinifile还没到释放的时候,接下去还要用,而按照tinifile的读写方式,我认为也直接写到了硬盘上不会占内存。
我就是想不出来为什么会出现:
"0x00402200"指令引用的"0x00e34c0c"内存。该内存不能为"written"。
我反复使用的过程大体上由:读入txt文件,其中有一些预留被找到并被替换的地方,进行大量内部的文本处理,将文本处理的结果替入,组成一个将要被另存的文本文件内容。
 
 
 

请教个delphi关于占用和释放内存的问题

帖子liyinwei » 星期六, 2007年2月3日 20:56


说老实话,我没有看明白楼主想要说明什么?
 
 
 

请教个delphi关于占用和释放内存的问题

帖子witkey » 星期六, 2007年2月3日 22:51


搂主:
我也没有弄明白你要问的问题!!!
我再提问你一句:
如果:
你反复调用某一个过程里面的
 Ainifile:= TIniFile.Create('...');
那就是在不断占用内存;
比如:
procedure A;//建立了对象的过程
var
  Ainifile:Tinifile;
bagin
   Ainifile:=Tinifile.cerate;
{.......}
end;
调用:
begin
  A;
  Ainifile.Max;
  //.....
end;
如果你调用了,5次内存就填满了。你改成:
建立一个全局变量:
var
  Ainifile:Tinifile;
在OnCreate里建立,或者OnShow事件里
begin
  Ainifile:=Tinifile.cerate;
end;
然后,在调用的时候访问属性:
begin
  A;
  Ainifile.Max;
  ...
end;
最后
在OnClose里释放掉:
begin
  Ainifile.Free;
end;
这样你调用100次1000次10000次A过程用来访问属性Ainifile.Max;那么
Ainifile:=Tinifile.cerate;的对象的建立,你只占用了一次内存。
你就可以节省许多内存。
看看你是不是我说的这种情况。
要不,把你的源码贴上来看看,分析分析。
 
 
 

请教个delphi关于占用和释放内存的问题

帖子popogens » 星期六, 2007年2月3日 23:12


我在form建立的时候把tinifile.create了(很多个Tinifile)
close的时候相信tinifile和form一起被释放掉了

我反复进行的操作是:
1、“打开”按钮:{读入一个txt文件,并从ini中读入很多内容,对txt文本进行局部修改}
2、“关闭”按钮:{把txt文本释放掉}

PS:我做的一个不小的软件,源码涉及东西太多太长了,即使你是D高手,相信看明白也得花个几天时间哦。
 
 
 

请教个delphi关于占用和释放内存的问题

帖子witkey » 星期六, 2007年2月3日 23:17


前者你要是调用5次A过程就会
Ainifile:=Tinifile.cerate;
Ainifile.Max;
二者占用5次内存。
而后者
Ainifile:=Tinifile.cerate;
只占用1次内存;
这个Ainifile.Max;占用5次内存。
看看是那个划算!!!
 
 
 

请教个delphi关于占用和释放内存的问题

帖子witkey » 星期六, 2007年2月3日 23:26


>>1、“打开”按钮:{读入一个txt文件,并从ini中读入很多内容,对txt文本进行局部修改}
1、我觉得你在用完Ainifile:=Tinifile.cerate;即Ainifile.Max(Max是我打的比方)之后,就应该释放Ainifile.Free;
2、如果每次打开按钮都用Ainifile哪个机器能吃的销?
3、如果是内存占用太多,肯定是你要优化你的代码。
4、大多数的吃掉太多内存的程序多半是代码优化不好。
 
 
 

请教个delphi关于占用和释放内存的问题

帖子popogens » 星期六, 2007年2月3日 23:26


procedure TWrits.BMakeClick(Sender: TObject);
var
  BName, SName, SFile: String;
begin
  if CurrentTemplate = '' then Showmessage('请先选择一个要制作的文书!')
  else
  begin
    BName := '';
    BName := GetBroadName(CurrentTemplate);
    if BName = '' then BName := CurrentTemplate;
    if BName = CurrentTemplate then SName := '' else SName := RightStr (CurrentTemplate, Length(CurrentTemplate) - Length(BName) - 2);
    SFile := ExePath + 'Writ\Templates\' + BName;
    if not FileExists (SFile + '.WRS') then Showmessage('该文书:“' + BName + '”模板不存在!必须先制作好模板,才能用模板制成文书。')
    else if Application.MessageBox('点击“确定”以继续制作此文书。', '制成文书', Mb_yesno + Mb_IconQuestion) = idyes then
    begin
      Broad.M.Lines.LoadFromFile(SFile + '.WRS');
      if SName = '' then MakeSpace
      else
      begin
        try Broad.IniSub.Free; except end;
        Broad.IniSub := Tinifile.Create(SFile + '.Sub');
        if not Broad.IniSub.SectionExists(SName) then
        begin
          if Application.MessageBox(PChar('    文书模板存在,但对应的子模板不存在,如需制成有对应子模板设定项目的文书,请先建立并设定该子模板。' + #10 + '    是否制成空文书?'), '子模板不存在', Mb_yesno + Mb_IconInformation) = idyes then MakeSpace;
        end
        else CreateSub(BName, SName);
      end;
    end;
  end;
end;

//我用BName表示模板文件名,SName表示子该模板的一个子模板名称,方便阅读注释一下。

这里主要调用了一个CreateSub(BName, SName);过程,也就是找到模板并存在对应子模板的情况下的过程,如下:

procedure TWrits.CreateSub(BName, SName: String);
var
  I, J, SubS: Integer;
  BooG: Boolean;
  S: String;
begin
  //打开并读取子板各属性
  try Broad.IniSubProperty.Free; except end;
  Broad.IniSubProperty := Tinifile.Create(ExePath + 'Writ\Templates\' + BName + '.Sbp');
  SubS := Broad.IniSubProperty.ReadInteger(SName, 'S', 16);
  BooG := Broad.IniSubProperty.ReadBool(SName, 'G', false);
  if BooG then SGoods := InputBox('有关财物', '该文书指向的有关财物:  ', '');//打开物窗
  if (SGoods = '') and BooG then
  begin
    Showmessage('有财物指向的文书必须输入相关财物。');
    Abort ;
  end;
  Broad.DetailCount := 0;
  for I := Broad.M.Lines.Count - 1 downto 0 do
  begin
    S := Broad.M.Lines.Strings[I];
    if LeftStr (S, 4) = '<!--' then
    begin
      for J := 4 to Length(S) do
      begin
        if MidStr (S, J, 3) = '-->' then
        Break;
      end;
      S := MidStr(S, 5, J - 5);
--------------------------------------------就是这一句---------
      S := Broad.IniSub.ReadString(SName, S, '');
---------------------------------------------------------------
      Broad.M.Lines.Strings[I] := RightStr(Broad.M.Lines.Strings[I], Length(Broad.M.Lines.Strings[I]) - J - 2);//把头注释去掉
      if (NotCaseScript(LowerCase(LeftStr(S, 4)))) or (Main.CurrentName > '')  then
      begin
        AppWantReturn := 1;
        try StrCut.ExeScript(S, '');
        finally DoScript(I, SubS, S, BName);//内容已在MValue里,要逐行处理
        end;
      end;
    end;
  end;
  I := Broad.IniBroads.ReadInteger(BName, 'allwaysdel', 0);
  if Broad.DetailCount > I then Broad.DetailCount := Broad.DetailCount - I;
  BeforeP(BName, SName, SubS);
  try Broad.IniSubProperty.Free; except end;
end;

//这里再说明一下ExeScript过程是在之前最基础的过程中在另一个StrCut窗体中建的一个专门处理特定格式的文本的过程,通过调试和在其他地方应用,应该没问题:它的功能比如将“{cm}当事人基本情况”转换为txt文件中标注“当事人基本情况”所在的整个段落。
//DoScript(I, SubS, S, BName)比较关键,很可能问题就在此,它的功能是将ExeScript处理后所得的结果比如“当事人基本情况”(在StrCut.MValue中)填到txt文本中对应位置,并打开一个完成txt文件修改之前的小窗口(该窗口在确实之前还有小部分修改工作)。
//该小窗口无论确定还是关闭都不会发生问题,而在多次点击“制作”按钮,也就是多次调用了BMakeClick(Sender: TObject);过程,就会发生错误,错误时常不同,除了上述一程错误,另一种也是关于内存的提示,还有一种严重错误就是直接退出程序。而我进行的都是一模一样的操作。
 
 
 

请教个delphi关于占用和释放内存的问题

帖子popogens » 星期六, 2007年2月3日 23:30


create我只进行了一次,很多次的都是inifile.readstring()、Tmemo.loadfrom(a.txt)等操作,个别的ini文件(记录文件属性设定的ini文件)我create后,用好都马上free掉的。

忘了帖DoScript过程了:
procedure TWrits.DoScript(CurrentLine, SubS: Integer; Str, BName: String);
begin
  Case StrCut.MValue.Lines.Count of
    0:
      if LowerCase (LeftStr(Str, 4)) = '<cm>' then
        begin
          MakeCM(RightStr(Str, Length(Str) - 4));//<cm>补处理
          GetMFromMValue(CurrentLine, SubS, BName);
        end
      else
        begin
          Broad.M.Lines.Strings[CurrentLine] := '&nbsp';
          Broad.DetailCount := Broad.DetailCount + 1;
        end;
    1:
      begin
        Broad.M.Lines.Strings[CurrentLine] := StrCut.MValue.Lines.Strings[0];
        CountLine(Length(StrCut.MValue.Lines.Strings[0]), SubS, BName);
      end;
    else
      GetMFromMValue(CurrentLine, SubS, BName);
  end;
  StrCut.MValue.Lines.Clear;
  StrCut.EValue.Text := '';
  StrCut.ECut.Text := '';
end;
 
 
 

请教个delphi关于占用和释放内存的问题

帖子witkey » 星期六, 2007年2月3日 23:37


你说的没错!确实没法看你的代码。
我觉得应当用好面向对象这个思想,用好接口这个技术,代码肯定会比你写的要精炼。不过,这个问题应当别论了。
 
 
 

请教个delphi关于占用和释放内存的问题

帖子witkey » 星期六, 2007年2月3日 23:43


我要睡觉了。明天再讨论吧!
 
 
 

请教个delphi关于占用和释放内存的问题

帖子popogens » 星期日, 2007年2月4日 15:28


我用Showmessage('执行到此处');进行测试,发现是在这一句:
 S := Broad.IniSub.ReadString(SName, S, '');执行发生错误
不可思议。

Showmessage('执行到此处');放在它前面时,跳出了提示,反之弹出错误以后,后面不执行了,没有跳出'执行到此处'。

我回到前面的帖子里用
-------------------
错误的句子。
-------------------
做个标志。
Readstring的时候怎么会出错啊?

我都改成:
try S := Broad.IniSub.ReadString(SName, S, ''); except S := ''; end;
了,结果还是照样弹出个大大错误,一直以为ini.readstring很安全的呢。

现在我改成:
      if Broad.IniSub.ValueExists(sname, s) then S := Broad.IniSub.ReadString(SName, S, '') else S := '';
照样出错else S := '';应该不会出错吧,那前面呢?Valueexists证明该值存在,可存在值为什么读成错误。
 
 
 

请教个delphi关于占用和释放内存的问题

帖子popogens » 星期日, 2007年2月4日 21:41


翻遍了大半个internet,好象普遍认为ini比较弱,容易出错,不幸的是我做的软件偏偏要委以重任,大部分功能都靠它实现,对于出错的部分,我现在把它读到ValueListEditor中,再进行读取,在读ValueListEditor过程中,由于仍发生错误:读到了00000000地址(太快了),再对它前后各加了一个Sleep(10),不管是不是只需要一个就行了,现在总算能用,只希望它不要再出错。
结帖