在。net中,如何让垃圾收集器释放内存更容易?

时间:2022-12-23 07:30:45

I've been thinking if there's a way how to speed up freeing memory in .NET. I'm creating a game in .NET (only managed code) where no significant graphics is needed but still I would like to write it properly in order to not to lose performance for nothing.

我一直在想,有没有一种方法可以加速。net中的内存释放。我在。net中创建了一个游戏(仅管理代码),在这个游戏中不需要任何重要的图形,但是我仍然想要正确地编写它,以避免无缘无故地失去性能。

For example is it useful to assign null value to objects that are not longer needed? I see this in a few samples over Internet.

例如,将null值分配给不需要的对象是否有用?我在网上的一些例子中看到了这一点。

6 个解决方案

#1


6  

is it useful to assign null value to objects that are not longer needed?

为不再需要的对象分配空值有用吗?

Generally, no. Most of the samples you'll see online that do this are by people that came to .Net from VB6, where this was a common best practice. In .Net, it's less helpful. If you have a very long running method, it might let the garbage collector find the object a little earlier — but if your method is that long you have other problems.

一般来说,没有。您在网上看到的大多数这样做的示例都是来自VB6的。net用户提供的,这是一个常见的最佳实践。在。net中,这就没那么有用了。如果您有一个非常长时间运行的方法,它可能会让垃圾收集器更早地找到对象——但是如果您的方法很长,那么您就会有其他问题。

Instead, in .Net you should build short methods and define your variables as late as possible in the smallest scope blocks possible. Use the "natural" lifetime of the variable as determined by scope, but keep that natural lifetime short.

相反,在。net中,您应该构建简短的方法,并尽可能晚地在尽可能小的范围块中定义变量。使用由范围决定的变量的“自然”生命周期,但要保持自然生命周期的短暂。

You should not be doing your own garbage collection, as suggested by at least one other answer here. This can actually make things slower. .Net uses a generational garbage collector. By forcing a garbage collection, you might collect the target object (you also might not - there are no guarantees with garbage collection). But you'll likely also force a bunch of other objects that you can't collect yet to be stored in a higher order generation, making them harder to collect in the future. So just don't do this.

您不应该进行自己的垃圾收集,正如这里至少有一个其他答案所建议的那样。这实际上可以让事情变得更慢。. net使用了一个分代垃圾收集器。通过强制执行垃圾收集,您可能会收集目标对象(您也可能不会—垃圾收集没有保证)。但是,您可能还会强制将一些尚未收集到的其他对象存储在更高阶的生成中,使它们在未来更难收集。所以不要这样做。

#2


4  

You shouldn't worry too much and not optimize prematurely. Memory management in .NET is automatic and very efficient. If you do start to 'optimize' you need to know exactly what you are doing or you might slow it down.

你不必过于担心,也不要过早地优化。. net中的内存管理是自动的,而且非常有效。如果你真的开始“优化”,你需要确切地知道你在做什么,否则你可能会慢下来。

So finish your game first, and then if it's too slow use a profiler to find the problem. Most likely it will not be a memory problem.

所以,首先完成你的游戏,然后如果它太慢,使用剖析器来发现问题。很可能这不是一个记忆问题。

#3


3  

Most of the graphics related objects in .net (Image and its descendants, Graphics, Pen, Brush, etc) implement IDisposable. If you're going to be using an object for a specific operation, then not again, use the following pattern:

.net中的大多数图形相关对象(图像及其后代、图形、钢笔、笔刷等)实现了IDisposable。如果你要用一个对象来做一个特定的操作,那就不要再用了,使用以下模式:

using(var g = Graphics.FromBitmap(bmp))
{
    //Do some stuff with the graphics object
}

Doing it like this will ensure any unmanaged resources will get freed when they fall out of scope.

这样做将确保在任何非托管资源超出范围时释放它们。

#4


1  

I find allocating objects in .net one of the things that affects performance the most. To get around this I use the a Factory pattern where I recycle objects that I have used. Here is simple generic implementation:

我发现在。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;
    }
}

For my line routing algorithm, I need to constantly keep many values as a RouteNode which implements the following interface:

对于我的线路路由算法,我需要保持很多的值作为一个RouteNode,实现如下接口:

public interface IRecyclable
{
    void Recycle();
}

These get constantly created and destroyed. To recycle these objects, create a new factory:

它们不断地被创造和破坏。要回收这些物品,请创建一个新工厂:

nodeFactory = new ListFactory<RouteNode>();

When you need an object, call the create method:

当您需要一个对象时,调用create方法:

RouteNode start = nodeFactory.Create();
RouteNode goal = nodeFactory.Create();

When you have finished with the objects in the list, clear the list. The pointer is reset to the start of the list, but the objects themselves are not destroyed. In fact the Recycle method is only called when the object is reclaimed again. (You may want to do this earlier if you have other object references, see comments below)

当您完成列表中的对象时,清除列表。指针被重置到列表的开始,但是对象本身并没有被破坏。事实上,回收方法只在对象再次被回收时才被调用。(如果您有其他对象引用,您可以在前面做这个操作,参见下面的注释)

This is a pretty naive implementation, but its a place to start.

这是一个非常幼稚的实现,但它是一个起点。

#5


1  

It can be useful in some cases to set objects to null that are no longer needed. Often it is useless or counterproductive, but not always. Four main situations when it can be helpful:

在某些情况下,将不再需要的对象设置为null是很有用的。通常它是无用的或适得其反的,但并不总是如此。当它有用时的四个主要情况:

  • Objects containing fields declared as "WithEvents" in vb.net should implement IDisposable, and should nulled those fields in the Dispose method. I don't know why vb.net doesn't include a nice way of automatically nulling WithEvents fields, but since it doesn't they must be cleared manually.
  • 包含在vb.net中声明为“WithEvents”的字段的对象应该实现IDisposable,并且应该在Dispose方法中使那些字段无效。我不知道为什么vb.net不包含一个自动取消WithEvents字段的好方法,但是因为它不是必须手动清除的。
  • If a local variable holds a reference to an object which in fact will never be used again, but it may be awhile before the code reaches a point where the compiler knows the variable won't get used, clearing the variable may allow the reference to be freed up sooner.
  • 如果一个局部变量持有一个对象的引用,而该对象实际上永远不会被再次使用,但是在代码到达编译器知道该变量不会被使用的点之前,清理变量可能会让引用更早地被释放。
  • If at some point an array which is large or has been around for some time is known to be useless, and if references to some recently-allocated objects are stored in it, clearing those references before destroying all surviving references to the array may allow the newly-created objects to be freed much sooner than they otherwise would be.
  • 如果在某一时刻一个数组是大型或已经存在一段时间被认为是无用的,如果对一些最近分配对象的引用存储在它,清除这些引用之前摧毁所有幸存的引用数组允许创建的对象可能来得比其他被释放。
  • If an object has a Finalize routine and is in the finalization queue, all objects directly or indirectly referenced by it will be exempted from garbage collection. Consequently, finalizable objects should not reference anything that won't be needed for finalization.

  • 如果一个对象有一个Finalize例程,并且在finalization队列中,那么它直接或间接引用的所有对象都将免于垃圾收集。因此,可终结的对象不应该引用任何不需要完成的东西。
  • #6


    0  

    To do this (and I've had to do this with frequent >50mb allocations), call:

    要做到这一点(我必须经常使用>50mb的分配),请调用:

            myObj = null;
            GC.Collect();
            GC.WaitForPendingFinalizers();
    

    I've noticed that the memory footprint of the app will then greatly diminish. In theory, you shouldn't need to do this. However, in practice, with a 32 bit windows OS you might get 2 contiguous blocks of >300mb free at any given time, and having that space taken up by lots of little allocations or a series of big ones can mean that other large allocations will fail unnecessarily. The garbage collector runs in the background when it can, but if you absolutely must make large allocations right now, that set of lines helps to make that possible for me.

    我注意到应用程序的内存占用会大大减少。理论上,你不应该这么做。然而,在实践中,使用32位的windows操作系统,您可能会在任何给定的时间获得2块连续的>300mb的空闲块,而这些空间被大量的小分配或一系列的大分配占用,可能意味着其他的大分配将毫无必要地失败。垃圾收集器可以在后台运行,但是如果您现在必须进行大量的分配,那么这组行有助于使我实现这一点。

    EDIT: From what I put in the comments, for the downvoters.

    编辑:从我在评论中所写的内容来看,对于那些落选的人。

    If you read the entire post about garbage collection by Rico Mariani, you'll note that large, infrequent, non-predictable memory allocations fall into scenario #2. To whit:

    如果您阅读Rico Mariani关于垃圾收集的整个帖子,您将注意到,大的、不常见的、不可预测的内存分配属于场景#2。一点点:

    Rule #2

    规则# 2

    Consider calling GC.Collect() if some non-recurring event has just happened and this event is highly likely to have caused a lot of old objects to die.

    如果某个非循环事件刚刚发生,并且这个事件很可能导致许多旧对象死亡,请考虑调用GC.Collect()。

    A classic example of this is if you're writing a client application and you display a very large and complicated form that has a lot of data associated with it. Your user has just interacted with this form potentially creating some large objects... things like XML documents, or a large DataSet or two. When the form closes these objects are dead and so GC.Collect() will reclaim the memory associated with them.

    一个典型的例子是,如果您正在编写一个客户端应用程序,并显示一个非常大的、非常复杂的表单,其中包含大量与之相关的数据。您的用户刚刚与这个表单进行了交互,可能会创建一些大型对象……比如XML文档,或者一两大数据集。当窗体关闭时,这些对象是死的,因此GC.Collect()将回收与它们相关的内存。

    Now why would I suggest this as a possible time to call the collector? I mean, my usual advice goes something like "the collector is self-tuning so don't mess with it." Why the change of attitude you might ask?

    为什么我建议这个时间打电话给收藏家呢?我的意思是,我通常的建议是“收集器是自调的,所以不要搞砸它。”你可能会问为什么会改变态度?

    Well here is a situation where the collector's tendancy[sic] to try to predict the future based on the past is likely to be unsuccessful.

    在这种情况下,收藏家试图基于过去预测未来的倾向很可能会失败。

    If you make large allocations in your game, you will need to be careful about how memory is handled. The garbage collector works on prediction based on past events, and large blocks of memory on a 32bit machine can be devastating to future allocations if not properly managed. If you haven't done it, don't automatically assume that I'm wrong; if you have done it, I'd welcome an explanation of how to do it properly (ie, how to defrag memory to make sure I can always allocation 50-100mb of memory at a given time).

    如果你在游戏中进行了大量的分配,你需要注意内存是如何处理的。垃圾收集器基于过去的事件进行预测,如果管理不当,32位机器上的大块内存块可能会对未来的分配造成破坏性影响。如果你没有做过,不要想当然地认为我错了;如果您已经这样做了,我希望您能解释一下如何正确地完成它(例如,如何defrag内存以确保在给定的时间总是分配50-100mb内存)。

    #1


    6  

    is it useful to assign null value to objects that are not longer needed?

    为不再需要的对象分配空值有用吗?

    Generally, no. Most of the samples you'll see online that do this are by people that came to .Net from VB6, where this was a common best practice. In .Net, it's less helpful. If you have a very long running method, it might let the garbage collector find the object a little earlier — but if your method is that long you have other problems.

    一般来说,没有。您在网上看到的大多数这样做的示例都是来自VB6的。net用户提供的,这是一个常见的最佳实践。在。net中,这就没那么有用了。如果您有一个非常长时间运行的方法,它可能会让垃圾收集器更早地找到对象——但是如果您的方法很长,那么您就会有其他问题。

    Instead, in .Net you should build short methods and define your variables as late as possible in the smallest scope blocks possible. Use the "natural" lifetime of the variable as determined by scope, but keep that natural lifetime short.

    相反,在。net中,您应该构建简短的方法,并尽可能晚地在尽可能小的范围块中定义变量。使用由范围决定的变量的“自然”生命周期,但要保持自然生命周期的短暂。

    You should not be doing your own garbage collection, as suggested by at least one other answer here. This can actually make things slower. .Net uses a generational garbage collector. By forcing a garbage collection, you might collect the target object (you also might not - there are no guarantees with garbage collection). But you'll likely also force a bunch of other objects that you can't collect yet to be stored in a higher order generation, making them harder to collect in the future. So just don't do this.

    您不应该进行自己的垃圾收集,正如这里至少有一个其他答案所建议的那样。这实际上可以让事情变得更慢。. net使用了一个分代垃圾收集器。通过强制执行垃圾收集,您可能会收集目标对象(您也可能不会—垃圾收集没有保证)。但是,您可能还会强制将一些尚未收集到的其他对象存储在更高阶的生成中,使它们在未来更难收集。所以不要这样做。

    #2


    4  

    You shouldn't worry too much and not optimize prematurely. Memory management in .NET is automatic and very efficient. If you do start to 'optimize' you need to know exactly what you are doing or you might slow it down.

    你不必过于担心,也不要过早地优化。. net中的内存管理是自动的,而且非常有效。如果你真的开始“优化”,你需要确切地知道你在做什么,否则你可能会慢下来。

    So finish your game first, and then if it's too slow use a profiler to find the problem. Most likely it will not be a memory problem.

    所以,首先完成你的游戏,然后如果它太慢,使用剖析器来发现问题。很可能这不是一个记忆问题。

    #3


    3  

    Most of the graphics related objects in .net (Image and its descendants, Graphics, Pen, Brush, etc) implement IDisposable. If you're going to be using an object for a specific operation, then not again, use the following pattern:

    .net中的大多数图形相关对象(图像及其后代、图形、钢笔、笔刷等)实现了IDisposable。如果你要用一个对象来做一个特定的操作,那就不要再用了,使用以下模式:

    using(var g = Graphics.FromBitmap(bmp))
    {
        //Do some stuff with the graphics object
    }
    

    Doing it like this will ensure any unmanaged resources will get freed when they fall out of scope.

    这样做将确保在任何非托管资源超出范围时释放它们。

    #4


    1  

    I find allocating objects in .net one of the things that affects performance the most. To get around this I use the a Factory pattern where I recycle objects that I have used. Here is simple generic implementation:

    我发现在。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;
        }
    }
    

    For my line routing algorithm, I need to constantly keep many values as a RouteNode which implements the following interface:

    对于我的线路路由算法,我需要保持很多的值作为一个RouteNode,实现如下接口:

    public interface IRecyclable
    {
        void Recycle();
    }
    

    These get constantly created and destroyed. To recycle these objects, create a new factory:

    它们不断地被创造和破坏。要回收这些物品,请创建一个新工厂:

    nodeFactory = new ListFactory<RouteNode>();
    

    When you need an object, call the create method:

    当您需要一个对象时,调用create方法:

    RouteNode start = nodeFactory.Create();
    RouteNode goal = nodeFactory.Create();
    

    When you have finished with the objects in the list, clear the list. The pointer is reset to the start of the list, but the objects themselves are not destroyed. In fact the Recycle method is only called when the object is reclaimed again. (You may want to do this earlier if you have other object references, see comments below)

    当您完成列表中的对象时,清除列表。指针被重置到列表的开始,但是对象本身并没有被破坏。事实上,回收方法只在对象再次被回收时才被调用。(如果您有其他对象引用,您可以在前面做这个操作,参见下面的注释)

    This is a pretty naive implementation, but its a place to start.

    这是一个非常幼稚的实现,但它是一个起点。

    #5


    1  

    It can be useful in some cases to set objects to null that are no longer needed. Often it is useless or counterproductive, but not always. Four main situations when it can be helpful:

    在某些情况下,将不再需要的对象设置为null是很有用的。通常它是无用的或适得其反的,但并不总是如此。当它有用时的四个主要情况:

  • Objects containing fields declared as "WithEvents" in vb.net should implement IDisposable, and should nulled those fields in the Dispose method. I don't know why vb.net doesn't include a nice way of automatically nulling WithEvents fields, but since it doesn't they must be cleared manually.
  • 包含在vb.net中声明为“WithEvents”的字段的对象应该实现IDisposable,并且应该在Dispose方法中使那些字段无效。我不知道为什么vb.net不包含一个自动取消WithEvents字段的好方法,但是因为它不是必须手动清除的。
  • If a local variable holds a reference to an object which in fact will never be used again, but it may be awhile before the code reaches a point where the compiler knows the variable won't get used, clearing the variable may allow the reference to be freed up sooner.
  • 如果一个局部变量持有一个对象的引用,而该对象实际上永远不会被再次使用,但是在代码到达编译器知道该变量不会被使用的点之前,清理变量可能会让引用更早地被释放。
  • If at some point an array which is large or has been around for some time is known to be useless, and if references to some recently-allocated objects are stored in it, clearing those references before destroying all surviving references to the array may allow the newly-created objects to be freed much sooner than they otherwise would be.
  • 如果在某一时刻一个数组是大型或已经存在一段时间被认为是无用的,如果对一些最近分配对象的引用存储在它,清除这些引用之前摧毁所有幸存的引用数组允许创建的对象可能来得比其他被释放。
  • If an object has a Finalize routine and is in the finalization queue, all objects directly or indirectly referenced by it will be exempted from garbage collection. Consequently, finalizable objects should not reference anything that won't be needed for finalization.

  • 如果一个对象有一个Finalize例程,并且在finalization队列中,那么它直接或间接引用的所有对象都将免于垃圾收集。因此,可终结的对象不应该引用任何不需要完成的东西。
  • #6


    0  

    To do this (and I've had to do this with frequent >50mb allocations), call:

    要做到这一点(我必须经常使用>50mb的分配),请调用:

            myObj = null;
            GC.Collect();
            GC.WaitForPendingFinalizers();
    

    I've noticed that the memory footprint of the app will then greatly diminish. In theory, you shouldn't need to do this. However, in practice, with a 32 bit windows OS you might get 2 contiguous blocks of >300mb free at any given time, and having that space taken up by lots of little allocations or a series of big ones can mean that other large allocations will fail unnecessarily. The garbage collector runs in the background when it can, but if you absolutely must make large allocations right now, that set of lines helps to make that possible for me.

    我注意到应用程序的内存占用会大大减少。理论上,你不应该这么做。然而,在实践中,使用32位的windows操作系统,您可能会在任何给定的时间获得2块连续的>300mb的空闲块,而这些空间被大量的小分配或一系列的大分配占用,可能意味着其他的大分配将毫无必要地失败。垃圾收集器可以在后台运行,但是如果您现在必须进行大量的分配,那么这组行有助于使我实现这一点。

    EDIT: From what I put in the comments, for the downvoters.

    编辑:从我在评论中所写的内容来看,对于那些落选的人。

    If you read the entire post about garbage collection by Rico Mariani, you'll note that large, infrequent, non-predictable memory allocations fall into scenario #2. To whit:

    如果您阅读Rico Mariani关于垃圾收集的整个帖子,您将注意到,大的、不常见的、不可预测的内存分配属于场景#2。一点点:

    Rule #2

    规则# 2

    Consider calling GC.Collect() if some non-recurring event has just happened and this event is highly likely to have caused a lot of old objects to die.

    如果某个非循环事件刚刚发生,并且这个事件很可能导致许多旧对象死亡,请考虑调用GC.Collect()。

    A classic example of this is if you're writing a client application and you display a very large and complicated form that has a lot of data associated with it. Your user has just interacted with this form potentially creating some large objects... things like XML documents, or a large DataSet or two. When the form closes these objects are dead and so GC.Collect() will reclaim the memory associated with them.

    一个典型的例子是,如果您正在编写一个客户端应用程序,并显示一个非常大的、非常复杂的表单,其中包含大量与之相关的数据。您的用户刚刚与这个表单进行了交互,可能会创建一些大型对象……比如XML文档,或者一两大数据集。当窗体关闭时,这些对象是死的,因此GC.Collect()将回收与它们相关的内存。

    Now why would I suggest this as a possible time to call the collector? I mean, my usual advice goes something like "the collector is self-tuning so don't mess with it." Why the change of attitude you might ask?

    为什么我建议这个时间打电话给收藏家呢?我的意思是,我通常的建议是“收集器是自调的,所以不要搞砸它。”你可能会问为什么会改变态度?

    Well here is a situation where the collector's tendancy[sic] to try to predict the future based on the past is likely to be unsuccessful.

    在这种情况下,收藏家试图基于过去预测未来的倾向很可能会失败。

    If you make large allocations in your game, you will need to be careful about how memory is handled. The garbage collector works on prediction based on past events, and large blocks of memory on a 32bit machine can be devastating to future allocations if not properly managed. If you haven't done it, don't automatically assume that I'm wrong; if you have done it, I'd welcome an explanation of how to do it properly (ie, how to defrag memory to make sure I can always allocation 50-100mb of memory at a given time).

    如果你在游戏中进行了大量的分配,你需要注意内存是如何处理的。垃圾收集器基于过去的事件进行预测,如果管理不当,32位机器上的大块内存块可能会对未来的分配造成破坏性影响。如果你没有做过,不要想当然地认为我错了;如果您已经这样做了,我希望您能解释一下如何正确地完成它(例如,如何defrag内存以确保在给定的时间总是分配50-100mb内存)。