动态数组释放问题,delphi释放内存问题

capaa52007 2012-08-16 02:30:04
我声明全局变量动态数组 A,在释放的时候为什么要先setlength(A,0),然后freeandnil呢,不用freeannil行吗?如果不用freeandnil,会出现情况呢。
我参与的是实时监控项目,需要不断根性数据,也就是说,不断清空哈希表,然后再向里边填充数据,然后在清空。数据每1分钟更新一次,我检查了每个函数和过程,还有全局变量,该释放时候都手动释放了,尤其是局部数组和哈希表等等,我都手动释放,为什么运行一段时间后,程序占用的内存一直再涨啊?TTTTTTTT。。。。
最后我使用了网上一段代码,放到程序当中,隔一段时间运行一次,代码是
procedure TfrmMain.ClearMemory;
begin
if Win32Platform = VER_PLATFORM_WIN32_NT then
begin
SetProcessWorkingSetSize(GetCurrentProcess, $FFFFFFFF, $FFFFFFFF);
application.ProcessMessages;
end;
end;
这段代码是什么意思啊,用到这段代码后,内存瞬间减少很多。但是占用内存大小并不确定,有时候我一直看着,最大不过10M,而且马上就小了。有的时候涨到20M左右,但是不会再明显增加,维持在20M。
如果去掉这段代码,我点击子窗体,然后关闭他,再点击,再关闭,内存一直在涨,关闭之后却没有明显释放内存。我没有在dpr当中创建子窗体,只是在程序中creat(nil),然后,手动在finnaly中free他。看起来,是手动创建,手动释放,但是却一直涨内存,为什么呢。。。YTTTTTTTTTTTTTTTTTTTTTTT
...全文
822 12 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
12 条回复
切换为时间正序
请发表友善的回复…
发表回复
qq363606096 2012-10-20
  • 打赏
  • 举报
回复
//获取指标实时值
procedure TfrmMain.GetCurrentData;

这种设计模式不太好,这种习惯在大项目时容易出现隐蔽性的问题,
过程不需要提供参数,也没有设计为带返回值的函数,而且又使用了非静态变量!

建议1:建立一个独立执行的类,在类中定义要静态使用的变量,又类来负责创建和释放,提供更有意义和明确的函数,
此函数带GET开头却连基本返回值都不存在!

建议2:在频繁调用的方法中尽量设计少的创建和释放动作,
当多线程模式时,应考虑创建和释放时的线程安全,其次,本人最近在D7下多线程内存申请操作出现枯竭,加入同步控制后,问题得到解决(但资料显示D7本身申请内存是线程安全的),到目前还找不出原因,只能说,线程安全应该更多的把主动权把握在自己手中!
频繁的申请和释放容易导致内存碎片增多,运行时间长了,这些碎片也会占据不少空间!
建议3:安全的创建和释放:
try
//资源申请代码放在这里,如果资源申请时出现异常,就不会执行不该再执行的释放动作
try
//关键代码段
//有需要时,这里放 表示执行成功的返回值赋值语句
finally
//这里释放,哪怕过程没有异常或者逻辑需求性质的离开,都能释放资源
end;
except
//这里处理异常,并且又自己控制异常时的行为
//有需要时,这里放表示执行失败的返回值复制语句
end;

建议4:
SetProcessWorkingSetSize(GetCurrentProcess, $FFFFFFFF, $FFFFFFFF);
是把常驻内存将符合条件的转移到虚拟内存,这个操作能缓解系统需求,但降低了程序的效率!
因为当代码需要操作的内存在虚拟地址空间时,就必须先把内存转到常驻内存中!

建议5:
资源的使用,最好就是谁用得最多就由谁去管理,很多时候应该根据项目的需要来规划!

建议完毕,不当之处望指正!
capaa52007 2012-08-16
  • 打赏
  • 举报
回复
[Quote=引用 6 楼 的回复:]
动态数组是生存期自管理的,离开了作用域他们就会被释放,所以不用手动去释放
有时候动态数组占用内存比较大时,想在离开了作用域前释放的(比如一些全局的动态数组变量),赋值为nil就行了
[/Quote]

TTTT,内存变为16M了,激动啊。
离开作用域自动释放,意思是说机器要判断是否离开了作用域。。。那么释放内存是需要一定的时间了?

if (glb_IndexConfig= nil) or (Length(glb_IndexConfig)<> qryTmp.recordcount) then
begin
SetLength(glb_IndexConfig,0);
FreeAndNil(glb_IndexConfig);
SetLength(glb_IndexConfig,qryTmp.RecordCount,11);
end;

也就是说,我直接
if (glb_IndexConfig= nil) or (Length(glb_IndexConfig)<> qryTmp.recordcount) then
begin
glb_indexConfig:= nil;
SetLength(glb_IndexConfig,qryTmp.RecordCount,11);
end;
这样可以吗。如果排除代码啰嗦,冗余(多余),原来的代码是不是也没有错误呢

Hexpate 2012-08-16
  • 打赏
  • 举报
回复
装一个Eurakalog检测一下是不是内存泄漏了
capaa52007 2012-08-16
  • 打赏
  • 举报
回复
//获取指标实时值
procedure TfrmMain.GetCurrentData;
var
sSql: string;
qryWeb: TADOQuery;
i: Integer;
begin
if glb_CurrentDataHashed <> nil then
// glb_CurrentDataHashed.Free;
FreeAndNil(glb_CurrentDataHashed);
glb_CurrentDataHashed:=THashedStringList.Create;

sSql:= 'SELECT TAGNAME,VALUE FROM V_ANALOGLIVE';
qryWeb:= TPublic.Fnt_OpenSqlWeb(sSql);

for i:= 0 to qryWeb.RecordCount -1 do
begin
glb_CurrentDataHashed.Add(qryWeb.FieldByName('TAGNAME').AsString +'=' +qryWeb.fieldbyname('VALUE').AsString);
qryWeb.Next;
end;
// qryWeb.Free;
FreeAndNil(qryWeb);
end;
这个是哈希表和TADOQUERY的释放,不管释放是否啰嗦,我都完全做到释放了吧。。。
capaa52007 2012-08-16
  • 打赏
  • 举报
回复
应该是不断点击子窗体
capaa52007 2012-08-16
  • 打赏
  • 举报
回复
我不点击子窗体,然后关闭,按照代码说法,应该是吧子窗体free了,但是内存一直在增加。我就纳闷了。刚开始运行程序,才是17M多点,现在都30M了。。TTTTTTTTTT
刚才那个清理内存的代码我不用了,看来不好用。。。
其实我每个函数和过程都把能释放的都是放了,比如TADOQUERY还有,哈希表,动态数组等等,结果内存一直在上涨。。。TTTTTTTTTT,总得有个上限吧。。。TTTTTTTTTTT
kaikai_kk 2012-08-16
  • 打赏
  • 举报
回复
动态数组是生存期自管理的,离开了作用域他们就会被释放,所以不用手动去释放
有时候动态数组占用内存比较大时,想在离开了作用域前释放的(比如一些全局的动态数组变量),赋值为nil就行了
Hexpate 2012-08-16
  • 打赏
  • 举报
回复
这个东西不需要freeandnil的, 这个叫动态数组, 直接SetLength(glb_IndexConfig,0) 就已经释放内存了
capaa52007 2012-08-16
  • 打赏
  • 举报
回复
好的, glb_IndexConfig: array of array of string;
这就是他的类型,二维动态数组。
Hexpate 2012-08-16
  • 打赏
  • 举报
回复
glb_IndexConfig 的定义类型再发上来看看
capaa52007 2012-08-16
  • 打赏
  • 举报
回复
procedure TfrmMain.Menu_NormalRightClick(Sender: TObject);
begin
if Sender is TMenuItem then
begin
case (Sender as TMenuItem).Tag of
100: begin
with TfrmDeviceExceptQuery.Create(nil) do
try
ShowModal;
finally
Free;
end;
end;
101: begin
try
if Application.MessageBox('确定要退出应急平台系统吗?','系统提示',MB_YESNO or MB_ICONQUESTION) = mrNo then Exit ;

MyThread.DoTerminate(); //结束非主线程
Clear_MonitorTable; //避免出现Access violation报警

Application.Terminate;
except
;
end;
END;
102: begin
with TfrmFaultQryCnt.Create(nil) do
try
ShowModal;
finally
Free;

end;

end;
103: begin
if iRight= -1 then
with TfrmRightLogin.Create(nil) do
try
ShowModal;
finally //根据登录人的信息确认权限,并传入ID和姓名
iRight:= iCheckRight; //登录权限
sAccountID:= sUserID; //把登录人的账号名字传出来,发短信时候要用
Free;
end
else
if iRight<> -1 then
iRight:= -1;
prop_SetMenuDicEnable; //根据权限设置字典菜单是否可用
end;
104: begin
with TfrmSMSSend.Create(nil) do
try
sSenderID:= sAccountID; //将登录人员的信息传到发送短信界面里
ShowModal;
finally
Free;
end;

end;
105: begin
with TfrmSMSHistoryQuery.Create(nil) do
try
ShowModal;
finally
Free;
end;
end;
106: begin
with TfrmOxyNitQuery.Create(nil) do
try
ShowModal;
finally
Free;
end;
end;
107: begin
with TfrmDrawnSteel.Create(nil) do
try
ShowModal;
finally
Free;
end;

end;
end;
end;
end;
这个是创建子窗体的代码,点击右键菜单,根据每个选项的Tag值判断创建哪个,然后free哪个
手动创建 ,手动释放的。


procedure TfrmMain.GetIndexConfigeration;
var
i : Integer;
sSql : string;
qryTmp: TADOQuery;
begin
sSql:= 'SELECT F_PROCESS_ID,F_INDEX_ID,F_TAG_NAME,F_INDEX_LOWER'
+ ',F_INDEX_UPPER,F_INDEX_CONNECTED,F_INDEX_INTERVENE,F_TIME_CONTINUAL,F_ALERT_TYPE FROM VKIP_DIC_DEVICE_INDEX';
qryTmp:=TPublic.Fnt_OpenSql(dmMain.conOnTime,sSql);
if (glb_IndexConfig= nil) or (Length(glb_IndexConfig)<> qryTmp.recordcount) then
begin
SetLength(glb_IndexConfig,0);
FreeAndNil(glb_IndexConfig);
SetLength(glb_IndexConfig,qryTmp.RecordCount,11);
end;

qryTmp.First;
while not qryTmp.Eof do
for i:=0 to qryTmp.recordcount - 1 do
begin
glb_IndexConfig[i,0]:= qryTmp.fieldbyname('F_PROCESS_ID').AsString;
glb_IndexConfig[i,1]:= qryTmp.fieldbyname('F_INDEX_ID').AsString;
glb_IndexConfig[i,2]:= qryTmp.fieldbyname('F_TAG_NAME').AsString;
glb_IndexConfig[i,3]:= qryTmp.fieldbyname('F_INDEX_LOWER').AsString;
if (glb_IndexConfig[i,8]<>'3') and (glb_IndexConfig[i,8]<>'4') then
glb_IndexConfig[i,4]:= qryTmp.fieldbyname('F_INDEX_UPPER').AsString;
glb_IndexConfig[i,5]:= qryTmp.fieldbyname('F_INDEX_CONNECTED').AsString;
glb_IndexConfig[i,6]:= qryTmp.fieldbyname('F_INDEX_INTERVENE').AsString;
glb_IndexConfig[i,7]:= qryTmp.fieldbyname('F_TIME_CONTINUAL').AsString;
glb_IndexConfig[i,8]:= qryTmp.fieldbyname('F_ALERT_TYPE').AsString;
//glb_IndexConfig[i,9]:= ''; //记录本次持续异常的开始时间
glb_IndexConfig[i,10]:= '-1';//记录指标状态

qryTmp.Next;
end;
Freeandnil(qryTmp);
end;
glb_IndexConfig是一个全局变量,数组,如果直接把
SetLength(glb_IndexConfig,0); 注释的话,就报错了。。。
Hexpate 2012-08-16
  • 打赏
  • 举报
回复
ClearMemory 少用, 这种东西是设定私有内存工作空间的, 这段代码的意思就是说尽量把内存抽取出来给别人用, 关于内存释放, 你写的太笼统了, 不好看出来, 最好把代码贴出来, 一般不会这么写代码的

5,927

社区成员

发帖
与我相关
我的任务
社区描述
Delphi 开发及应用
社区管理员
  • VCL组件开发及应用社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧