我声明全局变量动态数组 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

解决方案 »

  1.   

    ClearMemory 少用, 这种东西是设定私有内存工作空间的, 这段代码的意思就是说尽量把内存抽取出来给别人用, 关于内存释放, 你写的太笼统了, 不好看出来, 最好把代码贴出来, 一般不会这么写代码的
      

  2.   

    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); 注释的话,就报错了
      

  3.   

    glb_IndexConfig 的定义类型再发上来看看
      

  4.   

    好的,    glb_IndexConfig: array of array of string;
    这就是他的类型,二维动态数组。
      

  5.   

    这个东西不需要freeandnil的, 这个叫动态数组, 直接SetLength(glb_IndexConfig,0) 就已经释放内存了
      

  6.   

    动态数组是生存期自管理的,离开了作用域他们就会被释放,所以不用手动去释放
    有时候动态数组占用内存比较大时,想在离开了作用域前释放的(比如一些全局的动态数组变量),赋值为nil就行了
      

  7.   

    我不点击子窗体,然后关闭,按照代码说法,应该是吧子窗体free了,但是内存一直在增加。我就纳闷了。刚开始运行程序,才是17M多点,现在都30M了。。TTTTTTTTTT
    刚才那个清理内存的代码我不用了,看来不好用
    其实我每个函数和过程都把能释放的都是放了,比如TADOQUERY还有,哈希表,动态数组等等,结果内存一直在上涨TTTTTTTTTT,总得有个上限吧TTTTTTTTTTT
      

  8.   

    //获取指标实时值
    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的释放,不管释放是否啰嗦,我都完全做到释放了吧
      

  9.   

    装一个Eurakalog检测一下是不是内存泄漏了
      

  10.   


    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;
    这样可以吗。如果排除代码啰嗦,冗余(多余),原来的代码是不是也没有错误呢
      

  11.   

    //获取指标实时值
    procedure TfrmMain.GetCurrentData;这种设计模式不太好,这种习惯在大项目时容易出现隐蔽性的问题,
    过程不需要提供参数,也没有设计为带返回值的函数,而且又使用了非静态变量!建议1:建立一个独立执行的类,在类中定义要静态使用的变量,又类来负责创建和释放,提供更有意义和明确的函数,
    此函数带GET开头却连基本返回值都不存在!建议2:在频繁调用的方法中尽量设计少的创建和释放动作,
    当多线程模式时,应考虑创建和释放时的线程安全,其次,本人最近在D7下多线程内存申请操作出现枯竭,加入同步控制后,问题得到解决(但资料显示D7本身申请内存是线程安全的),到目前还找不出原因,只能说,线程安全应该更多的把主动权把握在自己手中!
        频繁的申请和释放容易导致内存碎片增多,运行时间长了,这些碎片也会占据不少空间!
    建议3:安全的创建和释放:
      try
        //资源申请代码放在这里,如果资源申请时出现异常,就不会执行不该再执行的释放动作
        try
          //关键代码段
          //有需要时,这里放 表示执行成功的返回值赋值语句
        finally
          //这里释放,哪怕过程没有异常或者逻辑需求性质的离开,都能释放资源
        end;
      except
        //这里处理异常,并且又自己控制异常时的行为
        //有需要时,这里放表示执行失败的返回值复制语句
      end;建议4:
      SetProcessWorkingSetSize(GetCurrentProcess,   $FFFFFFFF,   $FFFFFFFF);
      是把常驻内存将符合条件的转移到虚拟内存,这个操作能缓解系统需求,但降低了程序的效率!
      因为当代码需要操作的内存在虚拟地址空间时,就必须先把内存转到常驻内存中!建议5:
      资源的使用,最好就是谁用得最多就由谁去管理,很多时候应该根据项目的需要来规划!                                                   建议完毕,不当之处望指正!