是否有一种常见的做法如何使.NET中的垃圾收集器更容易地为垃圾收集器做出释放记忆?
-
01-10-2019 - |
题
我一直在想,是否有一种方法可以加快.NET中的自由记忆。我正在用.NET(仅托管代码)创建一个游戏,其中不需要显着图形,但是我仍然想正确编写它,以免失去绩效。
例如,将null值分配给不再需要的对象是否有用?我在互联网上的几个样本中看到了这一点。
解决方案
将null值分配给不再需要的对象是否有用?
通常,没有。您将在网上看到的大多数样本都是由VB6的.NET的人进行的,这是一个普遍的最佳实践。在.net中,它的帮助不大。如果您的运行方式很长,它可能会让垃圾收集器更早地找到对象 - 但是如果您的方法是那么长,则您还有其他问题。
取而代之的是,在.NET中,您应该构建简短的方法并在可能的最小范围块中尽可能迟迟定义变量。使用范围确定的变量的“自然”寿命,但要保持自然的寿命。
你应该 不是 正如这里至少另一个答案所建议的那样,要做自己的垃圾收集。这实际上可以做事 慢点. 。 .NET使用一代垃圾收集器。通过强迫垃圾收集,您 可能 收集目标对象(您也可能不会 - 垃圾收集不保证)。但是,您可能还会强制许多其他物体,这些物体无法收集,这些物体尚未存储在高级生成中,从而使它们在将来更难收集。所以不要这样做。
其他提示
您不必担心太多,也不要过早地优化。 .NET中的内存管理是自动且非常有效的。如果您确实开始“优化”,则需要确切知道自己在做什么,否则您可能会放慢速度。
因此,首先要完成游戏,然后如果太慢使用探测器来找到问题。最有可能不是记忆问题。
.NET中的大多数相关对象(图像及其后代,图形,笔,刷子等)实现了IDisposable。如果您要使用对象进行特定操作,则不要再使用以下模式:
using(var g = Graphics.FromBitmap(bmp))
{
//Do some stuff with the graphics object
}
这样做将确保任何不受管理的资源在范围内掉落时都会被释放。
我发现在.NET中分配对象最影响性能的事物之一。为了解决这个问题,我使用了一个工厂模式,在该图案中回收了我使用过的对象。这是简单的通用实现:
internal class ListFactory<T>
where T: IRecyclable, new()
{
private List<T> _internalList;
private int _pointer;
public ListFactory()
{
_internalList = new List<T>();
_pointer = 0;
}
public void Clear()
{
_pointer = 0;
}
public int Count
{
get
{
return _pointer;
}
}
public List<T> InnerList
{
get
{
return _internalList;
}
}
//Either return T form the list or add a new one
//Clear T when it is being reused
public T Create()
{
T t;
//If the pointer is less than the object count then return a recycled object
//Else return a new object
if (_pointer < _internalList.Count )
{
t = _internalList[_pointer];
t.Recycle();
}
else
{
t = new T();
_internalList.Add(t);
}
_pointer ++;
return t;
}
}
对于我的行路由算法,我需要不断地将许多价值观作为一个 路由 哪个实现以下接口:
public interface IRecyclable
{
void Recycle();
}
这些被不断被创造和破坏。要回收这些对象,请创建一个新工厂:
nodeFactory = new ListFactory<RouteNode>();
当您需要对象时,调用创建方法:
RouteNode start = nodeFactory.Create();
RouteNode goal = nodeFactory.Create();
完成列表中的对象后,清除列表。指针重置为列表的开始,但对象本身并未被破坏。实际上,仅当对象再次回收时,回收方法才会调用。 (如果您有其他对象参考,您可能需要早些时候这样做,请参见下面的评论)
这是一个非常幼稚的实现,但它是一个起点。
在某些情况下,将对象设置为不再需要的null可能很有用。通常它是无用的或适得其反的,但并非总是如此。四个主要情况可能会有所帮助:
要做到这一点(而且我必须经常进行> 50MB分配),请致电:
myObj = null;
GC.Collect();
GC.WaitForPendingFinalizers();
我注意到该应用程序的内存足迹将大大减少。从理论上讲,您不必这样做。但是,实际上,使用32位Windows操作系统,您可能会在任何给定时间获得2个连续的块> 300MB,并且让很多很少的分配或一系列大型分配所占用的空间可能意味着其他大型分配也会不必要地失败。垃圾收集器在可能的情况下在后台运行,但是如果您现在绝对必须进行大型分配,那套线有助于使我成为可能。
编辑:根据我在评论中提出的票数。
如果您阅读了整个 关于Rico Mariani的垃圾收集的帖子, ,您会注意到,大型,不常见的,不可预测的内存分配属于方案#2。到惠特:
规则#2
如果刚刚发生了一些非重新发生的事件,并且此事件极有可能导致许多旧对象死亡,请考虑调用gc.collect()。
一个典型的例子是,如果您正在编写客户端应用程序,并且显示了一个非常复杂的表单,该表格具有许多与之相关的数据。您的用户刚刚与此表格进行了交互,可能会创建一些大型对象... XML文档或一个或两个大数据集之类的东西。当表单关闭时,这些对象已死,因此GC.Collect()将收回与之关联的内存。
现在,为什么我建议这样做可以打电话给收藏家的时间?我的意思是,我通常的建议是“收藏家是自我调整,所以不要惹它”。为什么您可能会问态度的改变?
好吧,在这种情况下,收藏家的倾向[原文如此]试图根据过去预测未来的情况可能会失败。
如果您在游戏中进行大量分配,则需要注意如何处理内存。垃圾收集器基于过去的事件进行预测工作,如果无法正确管理,32位机器上的大量内存可能会毁灭未来的分配。如果您没有这样做,请不要自动假设我错了。如果您做到了,我欢迎对如何正确执行操作的解释(即,如何分类内存以确保我可以在给定时间分配50-100MB的内存)。