主要源码如下
type
  TDIcon = packed record // packed record
    Pserial: Integer;
    PID: Integer;
    PIDSort: Integer;
    PFUllName: ShortString;
    PNickName: ShortString;
    PAttr: boolean;
    PPara: ShortString;
    PIcon: TMemoryStream;
  end;
  PDA = ^TDIcon;procedure Tform1.LoadListViewIcon; //ListView 刷新图标
var
  i: integer;
  LI: TListItem;
  p:PDA;
begin
  listview1.items.beginupdate;
  for i := listview1.items.Count - 1 downto 0 do //释放Point 
  begin
    if listview1.items.item[i].Data <> nil then Dispose(PDA(listview1.items.item[i].Data)); //这句关键
    //ListView1.Items.Item[i].Data := nil;
  end;
  ListView1.Items.Clear; //ListView1.Items.Clear; 触发 OnDeletion 事件
  if Icon_Kind then //大图标
  begin //ListView1.IconOptions.Arrangement := iaTop;
    TImage.Width := 32;
    TImage.Height := 32;
    ListView1.ViewStyle := vsIcon;
  end
  else
  begin //ListView1.IconOptions.Arrangement := iaLeft;
    TImage.Width := 16;
    TImage.Height := 16;
    ListView1.ViewStyle := vsSmallIcon;
  end;
  for i := 0 to length(P_Icon) - 1 do  //P_Icon 就是  TDIcon 的数组
  begin
    if P_Icon[i].PID = Item1 then
    begin
      new(P);
      P.PFullName := P_Icon[i].PFUllName;
      P.PSerial := P_Icon[i].Pserial;
      P.Pid := P_Icon[i].PID;
      P.PIDSort := P_Icon[i].PIDSort;
      P.PAttr := P_Icon[i].PAttr;
      P.PPara := P_Icon[i].PPara;
      P.PIcon := P_Icon[i].PIcon;
      Li := ListView1.Items.Add;
      Li.Caption := P_Icon[i].PNickName;
      Li.Data := PDA;
    end;
  end;
  ListView1.Invalidate;
  listview1.items.endupdate;
end;实际应用中 LoadListViewIcon 这一过程是经常调用的,所以ListView里的ICON是经常Clear后再载入,
但在释放ListView的item.Data指针这一句Dispose(PDA(listview1.items.item[i].Data)),也是采用了很多人认同的方式,就是采用Dispose
释放指针时,声明指针的类型.但使用(任务管理器)的内存实时监视程序运行中,不断反复执行LoadListViewIcon 这一过程时,程序占用内存还是不停以
4K的容量跳动上升的,这说明还是有内存没有释放,但经过跟踪,指针所占用的内存数据的确是已经被释放了.那么,问题就是:还有什么没有被释放?要怎样才能
释放这些内存?

解决方案 »

  1.   

    Li.Data := PDA;//应该是p
    从代码看,记录的数据是从P_Icon数据复制过来的,shortstring的成员不会导致 写复制,整型的也不会导致此问题, 记录成员 PIcon保存的是指针,指向P_Icon的成员,只会增加引用计数, 也不会有导致内存的增加可能。
      

  2.   

    用fastmm看看到底有没有内存泄漏,是什么东西造成的泄漏
    按照4k增长也可能是因为内存管理器太差,产生太多碎片所以每次都要申请新的空间
      

  3.   

    Li.Data := PDA;//应该是p是的,这里是我笔误
    Li.Data := P 是正确的,但不管怎样,反复执行LoadListViewIcon 这一过程(不断清除和加载ICON),在任务管理器中,内存的占有量
    的确是不断上升的.一个看上去既简单又复杂的问题,不知有大侠能深入指点一下吗?
      

  4.   

    你光贴出来些看不出来问题的代码有啥用,再高的高手也猜不出来你到底做什么了,去下个fastmm
    sourceforge.net/projects/fastmm
    看看使用说明,在ide里程序结束的时候会告诉你都有哪些东西没释放btw,1楼说的引用计数不知道哪里来的,他的代码里没有任何涉及引用计数的东西
      

  5.   

    Li := ListView1.Items.Add;生成的 Li 释放没?
      

  6.   

    ============================
        TImage.Width := 32;
        TImage.Height := 32;
        ListView1.ViewStyle := vsIcon;
    ============================这里的 TImage 是啥东西?
    测试以下代码,大量点击 Button1,内存占用很稳定。未发现楼主所说以4K增加的现象。
    type
      TDIcon = packed record // packed record
        Pserial: Integer;
        PID: Integer;
        PIDSort: Integer;
        PFUllName: ShortString;
        PNickName: ShortString;
        PAttr: boolean;
        PPara: ShortString;
        PIcon: TMemoryStream;
      end;
      PDA = ^TDIcon;procedure Tform1.LoadListViewIcon(); //ListView 刷新图标
    var
      i: integer;
      LI: TListItem;
      p:PDA;
    begin
      listview1.items.beginupdate;
      for i := listview1.items.Count - 1 downto 0 do //释放Point
      begin
        if listview1.items.item[i].Data <> nil then
          Dispose(PDA(listview1.items.item[i].Data)); //这句关键
        //ListView1.Items.Item[i].Data := nil;
      end;
      ListView1.Items.Clear; //ListView1.Items.Clear; 触发 OnDeletion 事件
    {  if Icon_Kind then //大图标
      begin //ListView1.IconOptions.Arrangement := iaTop;
        TImage.Width := 32;
        TImage.Height := 32;
        ListView1.ViewStyle := vsIcon;
      end
      else
      begin //ListView1.IconOptions.Arrangement := iaLeft;
        TImage.Width := 16;
        TImage.Height := 16;
        ListView1.ViewStyle := vsSmallIcon;
      end;}
      for i := 1 to 500 do  //P_Icon 就是  TDIcon 的数组
      begin
          new(P);
          P.PFullName := 'TEST Full';
          P.PSerial := 10000;
          P.Pid := 10000;
          P.PIDSort := 10000;
          P.PAttr := True;
          P.PPara := 'TEST Data';
          P.PIcon := nil;
          Li := ListView1.Items.Add();
          Li.Caption := 'TEST ' + IntToStr(i);
          Li.Data := p;
      end;
      ListView1.Invalidate;
      listview1.items.endupdate;
    end;procedure TForm1.Button1Click(Sender: TObject);
    begin
      LoadListViewIcon();
    end;
      

  7.   

    type
      TDIcon = packed record // packed record
        Pserial: Integer;
        PID: Integer;
        PIDSort: Integer;
        PFUllName: ShortString;
        PNickName: ShortString;
        PAttr: boolean;
        PPara: ShortString;
        PIcon: TMemoryStream;
      end;
      PDA = ^TDIcon;procedure Tform1.LoadListViewIcon; //ListView 刷新图标
    var
      i: integer;
      LI: TListItem;
      p:PDA;
    begin
      listview1.items.beginupdate;
    try //加入保护
      for i := listview1.items.Count - 1 downto 0 do //释放Point
      begin
        if listview1.items.item[i].Data <> nil then Dispose(PDA(listview1.items.item[i].Data)); //这句关键
        //ListView1.Items.Item[i].Data := nil;
      end;
      ListView1.Items.Clear; //ListView1.Items.Clear; 触发 OnDeletion 事件
      if Icon_Kind then //大图标
      begin //ListView1.IconOptions.Arrangement := iaTop;
        TImage.Width := 32;
        TImage.Height := 32;
        ListView1.ViewStyle := vsIcon;
      end
      else
      begin //ListView1.IconOptions.Arrangement := iaLeft;
        TImage.Width := 16;
        TImage.Height := 16;
        ListView1.ViewStyle := vsSmallIcon;
      end;
      for i := 0 to length(P_Icon) - 1 do  //P_Icon 就是  TDIcon 的数组
      begin
        if P_Icon[i].PID = Item1 then
        begin
          new(P);
          P.PFullName := P_Icon[i].PFUllName;
          P.PSerial := P_Icon[i].Pserial;
          P.Pid := P_Icon[i].PID;
          P.PIDSort := P_Icon[i].PIDSort;
          P.PAttr := P_Icon[i].PAttr;
          P.PPara := P_Icon[i].PPara;
          P.PIcon := P_Icon[i].PIcon;
          Li := ListView1.Items.Add;
          Li.Caption := P_Icon[i].PNickName;
          Li.Data := PDA;这句是明显错误
    应该为:
     Li.Data := P;
        end;
      end;
      ListView1.Invalidate; //无须使用
    finally
      listview1.items.endupdate;
    end;
    end; 
      

  8.   

    谢谢大家的支持,连续研究了48小时,终于有一点点的头绪了... 根本的原因是以下这 一段代码
    procedure TForm1.ListView1CustomDrawItem(Sender: TCustomListView; Item: TListItem; State: TCustomDrawState; var DefaultDraw: Boolean);
    var
      Cvs: TCanvas; //Bmp: TBitmap;
      icon: TIcon;
    begin
      if Item.Data <> nil then
      begin
        Cvs := TCanvas.Create; //Bmp := TBitmap.Create;
        icon := TIcon.Create;
        Cvs.Handle := GetDC(Item.Handle);   ------>>>就是这句导致内存不断增加
        PDA(Item.Data).PIcon.Position := 0;
        icon.LoadFromStream(PDA(Item.Data).PIcon);
        Cvs.Draw(Item.Position.X, Item.Position.y, Icon);
        Cvs.Free;
        icon.Free;
      end;
    end;By the way: 下载安装了FastMM4,现在关闭程序时没有提示内存汇漏..( 但内存还是一直增加) . 以上这个过程是因为,加载ListView的图标我没有从ImageList中加载,而是
    采用了在CustomDrawItem中,自已画ICON的方式,所以才需要 ListView1.Invalidate; 
      现在的终极问题是 为什么 Cvs.Handle := GetDC(Item.Handle); 这一句会导致内存不断增加,就算Free了也一样?
      

  9.   

    GetDC使用了GDI资源,每次使用完毕后对应着应该进行ReleaseDC,GDI资源是由gdi32.dll负责和分配和销毁的
    不然你打开任务管理器,在“进程”页中显示“GDI对象”,会看到每次执行完毕后GDI数量都会增加
    GDI对象在整个windows系统中有数量限制的,总数是近16k,如果用光了GDI资源windows的显示就出问题了
      

  10.   


    再请教Seamour,多谢指点,因为对GDI资源不熟悉, 那么我现在应该怎样ReleaseDC才行呢? 样例代码应怎写才好呢?
      

  11.   

    ReleaseDC(Cvs.Handle, Item.Handle)
    很奇怪啊,装fastmm没啥问题那看英文应该没问题,而且知道用GetDC,那只要在msdn上看一下就该知道ReleaseDC用法了啊
      

  12.   

    procedure TForm1.ListView1CustomDrawItem(Sender: TCustomListView; Item: TListItem; State: TCustomDrawState; var DefaultDraw: Boolean); 
    var 
      Cvs: TCanvas; //Bmp: TBitmap; 
      icon: TIcon; 
    begin 
      if Item.Data <> nil then 
      begin 
        Cvs := TCanvas.Create; //Bmp := TBitmap.Create; 
        icon := TIcon.Create; 
        Cvs.Handle := GetDC(Item.Handle);  ------>>>就是这句导致内存不断增加 
         PDA(Item.Data).PIcon.Position := 0; 
        icon.LoadFromStream(PDA(Item.Data).PIcon); 
        Cvs.Draw(Item.Position.X, Item.Position.y, Icon); 
        Cvs.Free; 
        icon.Free; 
        ReleaseDC(Item.Handle,DC); // 加上這句吧,  這東西大小有限制的。
      end; 
    end;你為什麼剛開始不把這個也放出來啊,搞得上面兄弟們猜得好辛苦。
      

  13.   

     不好意思,寫錯了。
    procedure TForm1.ListView1CustomDrawItem(Sender: TCustomListView; Item: TListItem; State: TCustomDrawState; var DefaultDraw: Boolean); 
    var 
      Cvs: TCanvas; //Bmp: TBitmap; 
      icon: TIcon; 
    begin 
      if Item.Data <> nil then 
      begin 
        Cvs := TCanvas.Create; //Bmp := TBitmap.Create; 
        icon := TIcon.Create; 
        Cvs.Handle := GetDC(Item.Handle);  ------>>>就是这句导致内存不断增加 
         PDA(Item.Data).PIcon.Position := 0; 
        icon.LoadFromStream(PDA(Item.Data).PIcon); 
        Cvs.Draw(Item.Position.X, Item.Position.y, Icon); 
        Cvs.Free; 
        icon.Free; 
        ReleaseDC(Item.Handle,cvs.Handle); // 加上這句吧,  這東西大小有限制的。
      end; 
    end;
      

  14.   


    呵呵,谢谢了,因为是半路自学出家,15年前从EGA,MONO VGA,打波斯王子一代和决战中南海开始接触计算机的,那时只知玩,没有基础,后来才开始自学VFP,VB,ASP
    ,SQL,C#,VB.NET 都只是皮毛,这二年才想深入些,就选了DELPHI,一直以来都想找个能有好的学习环境和气氛的团体或组织,都找不到.谢谢您和以上各位朋友百忙中抽空指教了,如不嫌弃和打扰您的话,我把您加为好友了.   昨晚上GOOGLE Search了下,当时把代码改成如下
    procedure TForm1.ListView1CustomDrawItem(Sender: TCustomListView; Item: TListItem; State: TCustomDrawState; var DefaultDraw: Boolean);
    var
      Cvs: TCanvas; //Bmp: TBitmap;
      icon: TIcon;
      DC: HDC;
    begin
      if Item.Data <> nil then
      begin
        Cvs := TCanvas.Create; //Bmp := TBitmap.Create;
        icon := TIcon.Create;
        DC := GetDC(Item.Handle);
        PDA(Item.Data).PIcon.Position := 0;
        icon.LoadFromStream(PDA(Item.Data).PIcon);
        Cvs.Handle :=DC;
        Cvs.Draw(Item.Position.X, Item.Position.y, Icon);
        ReleaseDC(Item.Handle, DC);
        Cvs.Free;
        icon.Free;
      end;
    end;运行通过了,个人理解 HDC=Handle吧,现在这部份基本完成了,但还有几个疑团没有冰释.
    1)GDI资源没有正确释放而导致内存不断增加,但FastMM却检测不到内存泄漏,看来工具还不够人性化啊
    2)如果当初采用ImageList加载ListView图标应该就不会出现如此问题了,但是从ImageList加载的图标很不美观,和提取的图标原形是不同的从这里可以看到除了TOOLS这个文件夹图标,其他三个都存在失真,无论提取图标用什么格式保存,BMP,JPG,只要用ImageList加载到ListView,图标就会出现失真,但采用在ListView的OnCustomDrawItem事件中自绘Icon就解决了这一问题,但在VB.NET 2005中就没有这个问题.
    3)关于ListView中的图标,想用鼠标拖动某个Icon和任何其它一个Icon交换位置,这个问题我想了一个多月还没完全解决,因为Item的Index属性不能写,现在只是用了个最笨办法,在每个Icon的右键菜单中选择 上移 或 下移一个位置,这样只能一格格的移,每次也只能移动一次,有时需要移动多次才能交换2个Icon的位置.不知各位对用鼠标拖动Icon任意移动能否指点一下,这点在VB.net已解决了,但Delphi中不行. 如各位需要,可随时向我索取VB.net和Delphi的源码.
      

  15.   

    fastmm只是接管了delphi自带rtl的memory manager,只对由它分配的内存负责,别的它管不着
    再说gdi资源是由windows内核分配的,x86平台的win内核在ring0下运行,fastmm一个用户态(ring3)的程序也不可能去检视它访问不了的资源
    TImageView默认的size是16*16的,一般icon也是一个图包,里面会有好几种size。从效果上看,你没改大小ImageView的size吧,于是自动按16*16的图片载入,然后才放大成32*32的,当然难看了btw,我也只是业余玩玩编程而已
      

  16.   


    再次感谢,请问能不能介绍一些技术QQ群之类的交流环境?
    另:
    if Icon_Kind then //大图标 
      begin //ListView1.IconOptions.Arrangement := iaTop; 
        TImage.Width := 32; 
        TImage.Height := 32; 
        ListView1.ViewStyle := vsIcon; 
      end 
      else 
      begin //ListView1.IconOptions.Arrangement := iaLeft; 
        TImage.Width := 16; 
        TImage.Height := 16; 
        ListView1.ViewStyle := vsSmallIcon; 
      end;  
    这段是ListItem加入Icon时重设Icon Size的代码Icon_Small := TIcon.Create;
      Icon_Big := TIcon.Create;  SHGetFileInfo(pchar(FileInfo), 0, info, Sizeof(info), SHGFI_SMALLICON or SHGFI_ICON); //SHGFI_SMALLICON or SHGFI_ICON
      Icon_Small.Handle := info.hIcon;  SHGetFileInfo(pchar(FileInfo), 0, info, Sizeof(info), SHGFI_LARGEICON or SHGFI_ICON);
      Icon_Big.Handle := info.hIcon;
    这段是提取大小不同Icon Size的代码经这样处理 ,如果ListView加载Icon用ImageList还是会出现失真的.
    注:在保存Icon时我已修改了D7 的Graphics单元的二处地方,令到Icon提取后保存可以有65535种Color,否则D7默认的Graphics单元处理保存Icon只有256种Color.  
      

  17.   

    手误……我想说的是TImageList。TListView有好几个TImageList的属性,像LargeImages/SmallImages之类的,把它们设置好了不应该出问题的吧
      

  18.   


    哦,最初的代码是这样的
    ListView1.LargeImages := TImage_Large;
    ListView1.SmallImages := TImage_Small;
    初始化二个不同大小的TImageList,绑定到ListView,结果图标还是难看,后来不断试用了很多种方法都解决不了,最后才用Draw Icon的