注意:关于内存泄漏。(50分)

  • 主题发起人 主题发起人 shenloqi
  • 开始时间 开始时间
S

shenloqi

Unregistered / Unconfirmed
GUEST, unregistred user!
这是一个简单的程序,但是可能会对关心内存泄漏的人有所启示。
program Project1;

uses
ShenNewMemMgr,//我的监测内存的单元
Windows;

{$R *.RES}

var
s: string;

begin
s := '100';
messagebox(0,Pchar(s),'',0);
s := '';//如果没有这一行,就会有内存泄漏。
end.

因为在本单元中string类型的变量s被强制转换为PChar类型了,在这之后如果你不把PChar
类型的s转换为String就会出现内存泄漏。
这个道理很浅显,一些书中也有所提及,但是我想可能会有一些FW们在应用的时候会忘记
这一规则,所以贴出来提示大家。
如果关于内存泄漏还有什么大家不大容易注意到的情形,希望大家跟贴。(其实Interface
等自动内存管理在使用的时候都要对引用计数有所了解才可以)
 
你那个监视内存的单元是自己写的吗?
 
从D5编程人员指南上面扩充的。
 
你是怎么发现的?
MemProof?
 
D5技术手册上的或者是D5编程人员指南上的就已经足够发现了,但是不能知道类型信息。
 
有没有什么好的检测工具?
我从来都不注意这些的!
 
那这样会不会造成内存泄露呢?
program Project1;
uses
ShenNewMemMgr,//我的监测内存的单元
Windows;
{$R *.RES}
begin
messagebox(0,Pchar('这样会不会造成内存泄露呢?),'',0);
end.

 
其实这是错的,你有没有发现,你后面加的这一行,Delphi根本就不会编译进EXE,
而且会提示你给s赋值了但是没有使用,除了你用专们的分配内存的函数来给PChar类型变量分配内存,
否则Delphi都会自动管理这些。
比如下面的代码:

var s:String;
begin
setlength(s,10);
For i:=1 to 10 do
s:='0';
showmessage(s);
end;

这样会不会造存内存泄漏?回答出是不会,Delphi编译器会自动生成相应的代码来处里相应的需要。
 
其实PChar(s)会生成一个新的变量(暂且可以这样理解),
但是你的代码里没有给这个变量取名,
这个变量所指向的内存空间与字符串s所指向的内存空间是一样的,
当释放s时(这是编译器做的事),
这个新的变量会所指向的内存空间也被释放了(因为是指向的同一内存地址),
比如你把代码改成如下结果也是一样的:


program Project1;

uses
ShenNewMemMgr,//我的监测内存的单元
Windows;

{$R *.RES}

var
s: string;
p:PChar;

begin
s := '100';
p:=PChar(s);
messagebox(0,p,'',0);
//同样不会造成内存泄漏。
end.

 
什么是内存泄露?
 
aizb您用的是什么版本的delphi呀?
在d5下上述代码也许会造成内存泄漏的。 因为这些语句是直接写在dpr的begin和end.中间的,而不是子过程中。
在d5下打开cpu窗口就可以发觉, 加不加s:=''生成的代码是两样的。 加了s := ''会多生成一句
CALL @LStrClr
而其他代码都一样。 很显然LStrClr就是释放string的, 而没有s := ''的话就不会生成这句。这是否会造成内存泄漏很难说,因为结束前程序最后调用的是
CALL @Halt0
至于这个@Halt0过程干了点什么我没兴趣跟踪了,哪位有兴趣的话可以自己跟跟看,看看是否会释放s占用的内存。
宁可信其有吧,反正加一句s := ''也不费事。
而且上面说到的这种内存泄漏情况几乎不可能出现。
因为一般编程不大会修改dpr中的内容,即使修改也不会直接在dpr中定义一个全局变量(我一般使用的全局变量都定义在pas中,最多在dpr中use一下,这样做可以肯定不会出现内存泄漏的)

如果在过程或函数中使用上述代码则s := ''加不加结果都一样。优化程序会自动优化掉这句而不编译进exe中的。
 
Pearl分析得有道理,但是事实是:在dpr中加入的s:='';是被编译了,
因为这里的s是全局变量,全局编译编译器是无法判断程序在其他地方用到年赋的值,
无论是项目全局变量还是单元内全局变量,Delphi编译器都不会检查给这个变量所赋的值是否被用到,
而函数内局部变量则会检查,并进行优化,我没看清上面的代码所以才说那一句不会编译,
但是即便没有这一行,编译器也一样会处理字符串的内存释放过程的,
这就是Delphi封装的String类型比PChar类型的优点。
总之,一般情况下,除非我们用特定的函数给字符串分配内存,都无须考虑释放内存的,
而且即使用s:='';也不见得就释放了内存资源, 请看以下的代码:

program Project1;

Uses Dialogs;
var
s,s1:String;
p:PChar;

{$R *.res}

begin
s:='abc';
p:=PChar(s);
s:='';
Inc(p);
s1:=s1+p^;
Inc(p);
s1:=s1+p^;
ShowMessage(s1);
end.

按说如果s:=''释放了内存,那么p^或Inc(p)应该会出错,但是没有,而且Showmessage显示出来的是"bc",也应是说内存中依然有这两个字符串。
 
对不起上面的代码有点小问题,s1没有初始化,不过这问题不大,
Delphi编译器自动为我们做了全局变量的初始化,当然一个好的编译风格是:

program Project1;

Uses Dialogs;
var
s,s1:String;
p:PChar;

{$R *.res}

begin
s:='abc';
p:=PChar(s);
s:='';
Inc(p);
s1:=p^;//原来这一行没有s1初始化。
Inc(p);
s1:=s1+p^;
ShowMessage(s1);
end.
 
shenloqi,那代码不会内存泄漏吧?应该是你多虑了
 
aizb:
你误会了我说的意思了:)
我说的s := '',并没有说这一行就是释放了内存。(恐怕不会有人这么认为的)
你说
program Project1;
uses
ShenNewMemMgr,//我的监测内存的单元
Windows;
{$R *.RES}
var
s: string;
p:PChar;
begin
s := '100';
p:=PChar(s);
messagebox(0,p,'',0);
//同样不会造成内存泄漏。(这是你认为的,下面的pearl分析得很有道理,实际上会有内存泄漏的)
end.
后来的
program Project1;
Uses Dialogs;
var s,s1:String
p:PChar;
begin
s:='abc'
p:=PChar(s)
s:=''
Inc(p);
s1:=p^;//原来这一行没有s1初始化。
Inc(p);
s1:=s1+p^;
ShowMessage(s1);
end.
还是有内存泄漏的,当然,正如pearl所说这是在程序主体里面的代码而不是子过程。
因为我最近写的程序中在程序的主体中需要调用另外一个dll,所以就测试了一下:)

truecat:
你这样没有显式的分配变量,所以Delphi会处理一切的,自然不会出现内存泄漏了。
 
我想也可以靠参数来保证被正常清除,代码如下:
function PromptUser(Msg: string
Title: string
Flags: DWORD);
begin
Result := Application.MessageBox(PChar(Msg), PChar(Title), Flags);
end;
调用的时候:
Msg := '这样会造成内存泄漏吗?';
if PromptUser(Msg, '问题', MB_YESNO or MB_ICONQUESTION) = IDYES then
...
 
本来想看看有没有别人的经验的的,算了,结账。
 
真猪!
给我一分还不如不给我分!!
丢人!!!
 
truecat,你要是想多一些分,我开一个帖子给你就是了,因为本贴你没有太多的参与,
所以就抱歉了。
 
不是这个意思,我并不缺分。
我的意思是,你不应该给1分,如果你不给我,我不会介意的。我只是来学习的。
但给了1分,太。。。。。。唉,你自己体会下吧。
 
后退
顶部