为什么垃圾收集在C#/ XNA不会出售的自动渲染目标? [英] Why does garbage collection in c# / xna not dispose of render targets automatically?

查看:164
本文介绍了为什么垃圾收集在C#/ XNA不会出售的自动渲染目标?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我发现,呈现在C#/ XNA目标没有自动处理的,你必须调用.dispose()成员来摆脱他们。



我认为垃圾收集是假设自动摆脱的事情后,他们的所有引用都消失了,怎么办?



还有什么是不能自动处理?


解决方案

我以为垃圾收集是假设自动摆脱的事情后,他们的所有引用都消失了,怎么办?




这是两种方式不正确。



垃圾回收收集的管理内存,它是安全的收集,并且是必要的。这就是它。



想想垃圾收集,以此来模拟无限的堆内存。既然我们可以假装我们有无限的记忆中,我们从来没有叫任何事情,我们已经使用了,因为我们为什么要保持一种无限的资源完成的空闲内存*?



最简单的一种为GC来模拟无限堆,就是什么也不做。这工作时,例如该方法具有的存储器4GiB可用,它使用50MiB。收集从未出现。如果一个应用程序是足够小,收藏永远不会发生这确实发生了。 (尽管它不是那么懒才能让你用兆没有一个集合,它会尝试收集它试图询问OS为应用程序内存之前,它仍然可以当你想知道为什么不的GC是有益的。 ..这样做没有什么可以是一个有效的方法GC在某些时候,一旦你得到考虑这种可能性,有很多其他的问题消失)。



另一种方法是热切清理一切的时刻,可以清理。这种情况与引用计数垃圾收集器;这无关使用.NET但值得一提的,因为它会紧密地匹配后的所有引用都消失了?你的问题。



另一个原因是,当内存是必要的,并且已经可用内存的存储不可用,则GC停止所有线程,标识根(静态变量,每个线程的堆栈中的本地引用),标识一切由根引用,一切都被那些引用,等,直到它的发现,仍然可行由应用程序使用,并且然后考虑由其他都自由采取的存储器的一切。然后,它压实那些没有自己的内存释放的所有对象,这让当更多的可用内存,并且还经常在内存中(其中有轻微的性能优势)的物体保持紧密联系起来既避免了碎片。



如果也推出对象并没有删除,因为机会是,如果它是不正确删除它的第一次,也不会是今后一段时间,所以看起来。在存活较少这一过程的那些对象



两件事情在这一点要注意:



<醇>
  • 我们无法预测何时,如果有的话,GC将释放一个给定对象的内存。

  • 的GC做的唯一一件事是免费的托管内存。它不会做任何事情(它与finalisers,我们会来以后帮助)。



  • 获取,然后释放托管内存当然只是一种情况,我们不妨先做些什么,然后再撤销它。其他例子是:




    1. 获取文件句柄,并将其释放

    2. 获得一个窗口办理。 ,然后释放它。

    3. 获得一个GDI手柄,然后释放它。

    4. 打开网络连接,然后关闭它。

    5. 发送通过网络连接的协议定义的握手,然后在关闭之前发送的协议定义签收了它。

    6. 获取一些非托管内存,然后释放它。

    7. 获取一个对象(其中有一些开销超出分配给它的创作,或学习,通过使用)从池中,然后它返回到池中。



    GC正如我们所描述的这么远不会帮助任何这些。



    不过,他们都有两个共同的特点。他们已经起动操作,和一个结束操作



    起始动作要么映射以及对象的创建,或某些方法调用



    的结束动作可以匹配到关闭()结束()免费()发布()方法调用,但在定义 IDisposable.Dispose()我们可以给他们全部通用接口。语言也可以使用添加一些帮助,然后通过 †。



    (A类可能同时具有关闭()的Dispose()。在这种情况下,我们必须关闭一些我们将在后面的两个选项重新打开或在关闭状态下以其它方式使用,并且还保证清理后,我们正在与完对象)的方法。



    因此,在这种方式, IDisposable.Dispose()的存在是为了清理所有需要清理后的除了的用于管理内存的东西。



    现在,在这种情况下,有三种类型的类的需要实现 IDisposable的



    <醇>
  • 那些持有那种非托管资源的像一个把手,他们自己。

  • 那些我们使用的是带有某种池或其他的前/我们自己设计的方案后(或别人的制定,但仍然全部在.NET本身)。

  • 那些具有领域反过来落实的IDisposable ,因此当我们处理这个类的对象,它会调用的Dispose()在这些领域。



  • 让我们想想如果GC释放这样一个对象的内存会发生什么,而的Dispose()并没有被调用。



    在第三种情况下,它实际上并不重要,该对象未设置。真正重要的是,该场没有处置(或者说无所谓,但有些领域的路线事项)。



    在第二种情况下,怎么样太多它的事项要看池是多么的重要。它可能是次优的,但世界没有结束



    在第一种情况下,这是一场灾难 - 我们已经未发行的资源,我们不可能释放,直到应用程序结束,甚至之后(取决于资源的性质)。



    有关这个原因,对象可以有finalisers



    当GC即将释放对象的内存,如果它有一个finaliser,如果这finaliser未受到抑制(的Dispose(),通常会做到这一点表明该对象是很好的清理,也没有更多的工作需要就可以了),然后,而不是从释放对象的内存,它将把它放到终结队列。当然,这意味着不仅该对象没有其内存收集,但也没有任何可到达的通过它的领域。



    一个finaliser线程工作的方式,通过此队列,调用。他们每个人的finaliser方法



    有这个情况发生两个坏的东西:




    1. 我们不知道什么时候会发生。也许我们会耗尽资源,或者无法打开一个文件写它了。

    2. 这意味着,应该有它的内存释放一个对象来代替促进,将活的不只是一个周期长于它应该有,但许多周期更长



    3. 编辑:请注意,我们没有finaliser对第三类类,也许不是第二类。在这种情况下不需要finaliser,因为它有一个领域的真正关键的对象将呼吁其finaliser,它执行重要工作。它也很容易,如果你尝试从finaliser内处理一个finalisable场一个穷凶极恶的错误就结了。如果你写一个包装一个或它拥有更多可支配的领域,具有清理,然后实现的IDisposable 责任一次性类,但做的添加一个finaliser



      在所有的,一个finaliser被称为是指两种情况之一:




      1. 应用程序被关闭,所有finalisers正在运行(盛大,一切都很好,世界第一)。

      2. 有人搞砸了,并没有收拾东西时,他们应该有。



      所以,虽然有GC和比通过finalisers管理的内存等资源之间的互动,这是不得已的相互作用而绝不是任何可靠的它。你不应该认为finalisers作为一种方法,使GC做清理,但作为GC不使清理不可能的,如果一个缺陷意味着它没有发生(和办法有干净的方法 - 最多的应用程序停机)。



      *当然,如果你认为你有一种无限的资源(鱼,水牛,废物处理海洋的能力),它原来你不这样做,那么事情会导致混乱,所以也许并不适用该规则生活中的一切。



      使用使得调用的Dispose()在简单

       使用(someDisposableObject)
      {
      //做的东西
      }

      等同于:

       
      {
      //做的东西
      $} b $ b终于
      {
      如果(someDisposableObject!= NULL)
      ((IDisposable接口)someDisposableObject).Dispose();
      }

       使用(VAR someDisposableObject = someMethodCallOrCallToNew())
      {
      //做的东西
      }

      等同于:

        VAR someDisposableObject = someMethodCallOrCallToNew() ; 

      {
      //做的东西
      }
      终于
      {
      如果(someDisposableObject!= NULL)
      (( IDisposable接口)someDisposableObject).Dispose();
      }



      空检查可能的情况下被移除,其中,编译器可确定,它的不可能someDisposableObject为空,作为优化。


      I found out that render targets in c# / xna are not automatically disposed of, and you have to call the .dispose() member to get rid of them.

      I thought garbage collection was suppose to automatically get rid of things after all their references are gone, what gives?

      Is there anything else that is not disposed of automatically?

      解决方案

      I thought garbage collection was suppose to automatically get rid of things after all their references are gone, what gives?

      This is incorrect in two ways.

      Garbage collection collects managed memory that it is safe to collect, and is needed. That's it.

      Think of garbage collection as a way to simulate infinite heap memory. Since we can pretend we have infinite memory, we never have to call anything to free memory we have finished using, because why would we preserve an infinite resource?*

      The simplest way for the GC to simulate infinite heap, is to do nothing. This works when e.g. the process has 4GiB of memory available, and it uses 50MiB. Collecting never comes up. This does indeed happen if an app is small enough that collections never happen. (Though it's not so lazy as to let you use megs without a collection, it'll try collecting before it tries asking the OS for more memory for the app, still it can be useful when you are wondering "why didnt' the GC..." that doing nothing can be a valid GC approach some of the time, once you've got that possibility in mind, a lot of those other questions go away).

      Another approach is to eagerly clean up everything the moment it can be cleaned up. This happens with reference-counting garbage collectors; which has nothing to do with .NET but is worth mentioning because it would closely match the "after all their references are gone" of your question.

      Another is that when memory is needed, and not available in the store of already free memory, then the GC stops all threads, identifies roots (static variables and the local references within the stack of each thread), identifies everything referenced by a root, and everything referenced by those, and so on until it's found everything that could still feasibly be used by the application, and then considers the memory taken by everything else as free. It then compacts all the objects that didn't have their memory freed, which both avoids fragmentation when giving out more free memory, and also often keeps objects closer together in memory (which has minor performance benefits).

      If also "promotes" the objects it didn't delete, as chances are if it wasn't correct to delete it the first time, it won't be the next time, so it will look at those objects that survived this process less often.

      Two things to note at this point:

      1. We cannot predict when, if ever, the GC will free the memory of a given object.
      2. The only thing the GC does is free managed memory. It doesn't do anything else (it does help with finalisers, which we'll come to later).

      Obtaining and then freeing managed memory is of course only one case where we may wish to first do something, and then undo it. Other examples are:

      1. Obtaining a file handle, and releasing it.
      2. Obtaining a windows handle, and then releasing it.
      3. Obtaining a GDI handle, and then releasing it.
      4. Opening a network connection, and then closing it.
      5. Sending a protocol-defined handshake over a network connection, and then sending a protocol-defined sign-off over it before it is closed.
      6. Obtaining some unmanaged memory, and then freeing it.
      7. Obtaining an object (for which there is some overhead beyond allocation to its creation, or it "learns" through being used) from a pool, and then returning it to the pool.

      GC as we have described it so-far will not help any of these.

      Still, they all have two features in common. They have a starting operation, and a ending operation.

      The starting operation will either map well to object creation, or to some method call.

      The ending operation can match to a Close(), End(), Free(), Release() method call, but in defining IDisposable.Dispose() we can give them all a common interface. The language can also add some help then through using†.

      (A class may have both a Close() and a Dispose(). In this case we have both the option of closing something we will later re-open or otherwise use in its closed state, and also the method of guaranteeing clean-up after we are finished with the object).

      So in this way, IDisposable.Dispose() exists to clean-up all the stuff that needs cleaned-up except for managed memory.

      Now, in this case, there are three types of class that need to implement IDisposable:

      1. Those which hold the sort of unmanaged resource like a handle, themselves.
      2. Those we are using with some sort of pooling or other before/after scenario of our own devising (or someone else's devising, but still all within .NET itself).
      3. Those which have fields which in turn implement IDisposable and therefore when we dispose the object of this class, it calls Dispose() on those fields.

      Let's think about what happens if the GC frees the memory of such an object, and Dispose() hadn't been called.

      In the third case, it doesn't actually matter, that the object wasn't disposed. What really matters is that the fields weren't disposed (or perhaps that doesn't matter, but some field down the line matters).

      In the second case, how much it matters depends on how important the pooling was. It's probably sub-optimal, but not the end of the world.

      In the first case, it's a disaster - we've an unreleased resource that we cannot possibly release until the application ends, and perhaps even after that (depending on the nature of the resource).

      For this reason, objects can have finalisers.

      When the GC is about to free the memory of an object, if it has a finaliser, and if that finaliser hasn't been suppressed (Dispose() will normally do this to show that the object is nicely cleaned up and no more work is needed on it), then instead of freeing the memory from the object, it will put it into the finalisation queue. This of course means not only does that object not have its memory collected, but nor does any reachable through its fields.

      A finaliser thread works its way through this queue, calling the finaliser method on each of them.

      There are two bad things about this happening:

      1. We don't know when it will happen. Maybe we'll run out of a resource or be unable to open a file for writing before it does.
      2. It means that an object that should have had its memory freed is promoted instead, and will live not just one cycle longer than it should have, but many cycles longer.

      Edit: Note that we do not have a finaliser on classes of the third kind, and maybe not of the second kind. In this case the finaliser isn't needed because the really crucial object it had as a field will have its finaliser called, which does the important work. It's also really easy to end up with an atrocious bug if you try to deal with a finalisable field from within a finaliser. If you write a disposable class that wraps one or more disposable fields it "owns" and has responsibility for cleaning up, then implement IDisposable, but do not add a finaliser.

      In all, a finaliser being called means one of two things:

      1. The application is shutting down and all finalisers are being run (grand, all is well with the world).
      2. Someone messed up and didn't clean up something when they should have.

      So, while there is an interaction between GC and resources other than managed memory via finalisers, it's an interaction of last resort and in no way whatsoever is it dependable. You shouldn't think of finalisers as a way to make the GC do clean-up, but as a way for the GC to not make clean-up impossible if a flaw meant it didn't happen (and for a way to have clean-up on application shut-down).

      *Of course, if you think you have an infinite resource (fish, buffalo, waste-processing capacity of oceans) and it turns out you don't, then things can get messy, so maybe don't apply that rule to everything in life.

      using makes calling Dispose() simpler in that

      using(someDisposableObject)
      {
        //Do Stuff
      }
      

      Is equivalent to:

      try
      {
        //Do Stuff
      }
      finally
      {
        if(someDisposableObject != null)
          ((IDisposable)someDisposableObject).Dispose();
      }
      

      And:

      using(var someDisposableObject = someMethodCallOrCallToNew())
      {
        //Do Stuff
      }
      

      Is equivalent to:

      var someDisposableObject = someMethodCallOrCallToNew();
      try
      {
        //Do Stuff
      }
      finally
      {
        if(someDisposableObject != null)
          ((IDisposable)someDisposableObject).Dispose();
      }
      

      The null-check may be removed in cases where the compiler can determine that it's impossible for someDisposableObject to be null, as an optimisation.

      这篇关于为什么垃圾收集在C#/ XNA不会出售的自动渲染目标?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆