C#对象数组 = null,不释放内存

3
在我制作的项目中出现了内存泄露问题。我重新编写了所有函数来修复一些问题,但仍有一个问题没有解决:
该程序具有一个面板对象数组,每次添加新面板时都会增长。当它达到 400 个面板时,它会删除最旧的面板以释放一些内存。
我不明白的是:
tempPanels = new Panel[panels.Length];
Array.Copy(panels, 1, tempPanels, 0, panels.Length - 1);//delete the oldest history log (the first of the array)
panels = null; //empty object array
panels = new Panel[tempPanels.Length + 1]; //set new length
tempPanels.CopyTo(panels, 0);//restore panels

当我使用上述代码时,内存使用仍然不断增加......有人可以解释一下为什么我必须先处理面板再将面板设置为空吗?
tempPanels = new Panel[panels.Length];
Array.Copy(panels, 1, tempPanels, 0, panels.Length - 1);//delete the oldest history log (the first of the array)
panels[0].Dispose();
panels = null; //empty object array
panels = new Panel[tempPanels.Length + 1]; //set new length
tempPanels.CopyTo(panels, 0);//restore panels

谢谢您的提前帮助!

编辑 @ Steve B:
该程序创建了一个新的面板: panel currentPanel;
当有一个新的面板时,我声明currentPanel: currentPanel = new Panel();
之后我调用这个函数:setCurrentPanelConfiguration:

public void setCurrentPanel()
{
  currentPanel.Name = "panel" + panels.Length;
  currentPanel.Size = new System.Drawing.Size(485, 75);
  currentPanel.BackColor = Color.Transparent;
}

为了解决滚动 bug,我使用了一个面板 HistoryPanel,在其中放置了当前的面板:
HistoryPanel.Controls.Add(currentPanel);

然后我添加了所有的控件:用户名、当前时间和头像。

为了保存面板,我在创建空间后将其添加到面板数组中,如上所示:
panels[panels.Length-1] = currentPanel;

我使用数组是因为历史记录会显示最新的记录在顶部。为了做到这一点,每次我都必须将所有面板向下移动80个像素。


1
你可以使用垃圾回收器释放内存:GC.GetTotalMemory(true) - Tim Schmelter
1
如果我没记错的话,你必须先调用面板上的dispose方法。 否则,它们将不会被释放。 - Christian Sauer
@quispie:你到底想要实现什么?我没有看到你在控件层次结构中插入新面板的任何代码。话虽如此,为什么不使用大小是动态的集合而不是重新创建数组呢?(例如最简单的List<Panel> - Steve B
1
据我所知,垃圾回收器不应该被显式调用,除非你知道需要调用它(处理非托管资源?)。顺便说一下,如果你将“panels”作为一个集合(即List<>),它会很好地适应你的代码:panels.RemoveAt(0);,然后你就完成了。 - Alex
@SteveB: 我试着用了List,但它不起作用,所以我改成了数组,现在正常运行。我知道用List会更好,但我还在学习中,以后会尝试将其改为List的 ;)。 - Quispie
显示剩余3条评论
2个回答

11

因为将某物设置为null并不会处理它,它仅仅是取消了对该物体的引用-垃圾回收器没有监视你对null引用的赋值,它仅在需要时(其它一切相等)或被明确告知时进行处理。

简而言之,因为null和内存管理是不同的事情。


4
想象一下你有一个盒子,你只是把里面的东西拿出来了,但盒子还在。这就是你现在要做的事情——删除整个节点。 - soniccool

3
除了Grant Thomas所说的,为什么不使用一个List<Panel>,这样更容易管理呢?
代码应该像这样(假设panels已声明为List<Panel>):
Panel p = panels[0];
panels.RemoveAt(0);  // This removes the first element

p.Dispose(); // This disposes of the element

如果你想保留你的代码,应该按照以下方式编写:

tempPanels = new Panel[panels.Length];
Array.Copy(panels, 1, tempPanels, 0, panels.Length - 1);//delete the oldest history log (the first of the array)

// Dispose of every element in the array
for (int i = 0; i < panels.Length; i++)
    panels[i].Dispose();

// The following line is unneccessary, as the variable is re-assigned anyway
// panels = null; //empty object array

panels = new Panel[tempPanels.Length + 1]; //set new length
tempPanels.CopyTo(panels, 0);//restore panels

你说得对!我之前想用Java里的List,但总是无法运行,所以我就用了这个解决方案,它很好地解决了我的问题。变量“panels”被声明为一个普通的数组。 - Quispie

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接