在需要的时候垃圾收集不会发生,即使 [英] Garbage Collection not happening even when needed

查看:137
本文介绍了在需要的时候垃圾收集不会发生,即使的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我做了一个64位的WPF测试应用程序。随着我的应用程序运行和使用任务管理器中打开,我看我的系统的内存使用情况。我看到我使用的是2GB,我有6GB可用。

在我的应用程序,我单击Add按钮,一个新的1GB的字节数组添加到列表中。我看到我用1GB的系统内存使用量增加。我点击添加一共有6次,尽显内存6GB我不得不用的时候我开始了。

我点击删除按钮6次,从列表中删除每个阵列。拆除的字节数组不应该由任何其他对象在我的控制中引用。

当我删除,我没有看到我的记忆下去。 但是,这跟我没关系,因为我明白,GC是不确定性和所有。 我想根据需要GC将收集。

所以,现在有记忆望着满,但期待GC在需要的时候收集,我加入了。 我的电脑启动时打滑和缩小磁盘颠簸昏迷。 为什么没有GC收集? 如果这是没有时间去做,什么时候?

作为一个全面的检查,我有一个按钮来强制GC。当我推,我很快得到6GB回来。那岂不是证明我6阵列没有被引用,并可能已被收集过GC知道/想?

我已经读了很多,说我不应该调用GC.Collect(),但如果GC没有在这种情况下收集,我还能做什么?

 私人的ObservableCollection< byte []的> memoryChunks =新的ObservableCollection< byte []的>();
    公众的ObservableCollection< byte []的> MemoryChunks
    {
        {返回this.memoryChunks; }
    }

    私人无效AddButton_Click(对象发件人,RoutedEventArgs E)
    {
        //创建一个内存1G的块,并把它添加到收藏。
        //它不应该被垃圾,只要它是集合中收集的。

        尝试
        {
            byte []的块=新的字节[1024 * 1024 * 1024];

            //看来我需要填充的内​​存,否则它不会在任务管理器中显示出来
            的for(int i = 0; I< chunk.Length;我++)
            {
                块[I] = 100;
            }

            this.memoryChunks.Add(块);
        }
        赶上(例外前)
        {
            的MessageBox.show(的String.Format(无法​​创建另一块:{0} {1},Environment.NewLine,ex.ToString()));
        }
    }

    私人无效RemoveButton_Click(对象发件人,RoutedEventArgs E)
    {
        //通过从集合中移除的块,
        //我只是没有对象引用了它,
        //所以应该是垃圾收藏。

        如果(memoryChunks.Count大于0)
        {
            memoryChunks.RemoveAt(0);
        }
    }

    私人无效GCButton_Click(对象发件人,RoutedEventArgs E)
    {
        所以GC.Collect();
        GC.WaitForPendingFinalizers();
    }
 

解决方案
  

作为一个全面的检查,我有一个按钮来强制GC。当我推,我很快得到6GB回来。那岂不是证明我6阵列没有被引用,并可能已被收集过GC知道/想?

您可能最好要求什么时候GC自动收集的垃圾内存?。把我的头顶部:

  • 最常见的是,当第0代已满或对象分配将不适合的可用空间。 1
  • 有点通常,当分配的内存块会导致 OutOfMemoryException异常,一个完整的GC被触发,首先尝试并回收可用内存。如果收集后没有足够的连续内存可用,那么OOM会抛出异常。

当开始一个垃圾收集,由GC确定世代需要收集(0,0 + 1,或全部)的内容。每一代人都有由GC确定的大小(它可以改变为应用程序运行)。如果只有第0代会超过它的预算,这是唯一的一代人的垃圾将被收集。如果生存0代对象将导致第1代走了过来预算,那么第1代也将被收集和幸存的对象将被提升到第2代(这是最高的一代Microsoft的实现)。如果预算第2代超出还有,垃圾将被回收,但对象不能被提升到一个更高的一代,因为一个不存在的。

所以,这里存在的重要信息,在最的通用的方式,气相色谱仪开始,第2代时,才会收集,如果第0代和1代都是满的。此外,你需要知道的对象超过85,000字节并不存储在正常的GC堆与生成0,1,和2。它实际上是存储在所谓的大对象堆(LOH)。在LOH的内存全额征收(也就是,当第2代收集)时才会被释放掉;从来没有当的只有的几代0或1的收集。

  

为什么没有GC收集?如果这是没有时间去做,什么时候?

现在应该是显而易见的,为什么GC不会自动发生。你的只有的对LOH创建对象(记住, INT 类型,你用他们的方式,被分配在堆栈上并且没有被收集)。你永远填补0代,所以GC从未发生过。 1

你也以64位模式,这意味着它不太可能,你会打其他的情况下,我上面列出,其中集合发生时,有没有足够的内存运行它的整个应用程序的分配一个特定的对象。 64位应用程序有8TB的虚拟地址空间限制所以这将是一段时间,你打这种情况下之前。它更可能你会遇到的物理内存和页面文件空间了这种情况发生之前。

由于GC并没有发生,窗口开始分配内存从页面文件的可用空间,您的应用程序。

  

我已经读了很多,说我不应该调用GC.Collect(),但如果GC没有在这种情况下收集,我还能做什么?

呼叫 GC.Collect的()如果这样的code是什么,你需要写。更重要的是,不要写这样的code测试之外。

在最后,我没有做正义的自动垃圾收集的CLR的话题。我建议通过MSDN博客文章阅读关于它(它实际上是非常有趣的),或作为已经提到,杰弗里Richter的优秀图书,CLR通过C#,第21章。


1 我让你明白,.NET执行GC的是的垃圾收集器的假设。在最简单的术语,这意味着新创建的对象是在一个较低的编号代,即生成0作为垃圾收集被运行,它的发现,一个对象,该对象是在某一代具有的GC根(不是垃圾),它会被提升到下一代了。这是因为GC性能改进可能需要很长的时间,并影响性能。我们的想法是,在较高的世代的对象通常具有更长的寿命和将围绕在应用长,因此并不需要检查生成的垃圾尽可能低代。你可以阅读更多的<一个href="http://en.wikipedia.org/wiki/Garbage_collection_%28computer_science%29#Generational_GC_.28ephemeral_GC.29">this维基百科文章。你会注意到它也被称为的临时的GC。

2 如果你不相信我,你删除块的一前一后,有一个创建一大堆随机字符串或对象的功能(我建议对原语的数组本次测试),你会看到你后,达到一定的空间,会出现一个完整的GC,从而使您拨出的蕙内存。

I made a 64-bit WPF test app. With my app running and with Task Manager open, I watch my system memory usage. I see I'm using 2GB, and I have 6GB available.

In my app, I click an Add button to add a new 1GB byte array to a list. I see my system memory usage increases by 1GB. I click Add a total of 6 times, filling the 6GB of memory I had available when I started.

I click a Remove button 6 times to remove each array from the list. The removed byte arrays should not be referenced by any other object in my control.

When I Remove, I don't see my memory go down. But that's OK with me, because I understand that GC is non-deterministic and all that. I figure the GC WILL collect as needed.

So now with memory looking full, but expecting the GC to collect when needed, I Add again. My PC starts slipping in and out of a disk thrashing coma. Why didn't the GC collect? If that wasn't the time to do it, when is?

As a sanity check, I have a button to force GC. When I push that, I quickly get 6GB back. Doesn't that prove my 6 arrays were not being referenced and COULD have been collected had the GC knew/wanted to?

I've read a lot that says I shouldn't call GC.Collect() but if GC doesn't collect in this situation, what else can I do?

    private ObservableCollection<byte[]> memoryChunks = new ObservableCollection<byte[]>();
    public ObservableCollection<byte[]> MemoryChunks
    {
        get { return this.memoryChunks; }
    }

    private void AddButton_Click(object sender, RoutedEventArgs e)
    {
        // Create a 1 gig chunk of memory and add it to the collection.
        // It should not be garbage collected as long as it's in the collection.

        try
        {
            byte[] chunk = new byte[1024*1024*1024];

            // Looks like I need to populate memory otherwise it doesn't show up in task manager
            for (int i = 0; i < chunk.Length; i++)
            {
                chunk[i] = 100;
            }

            this.memoryChunks.Add(chunk);                
        }
        catch (Exception ex)
        {
            MessageBox.Show(string.Format("Could not create another chunk: {0}{1}", Environment.NewLine, ex.ToString()));
        }
    }

    private void RemoveButton_Click(object sender, RoutedEventArgs e)
    {
        // By removing the chunk from the collection, 
        // I except no object has a reference to it, 
        // so it should be garbage collectable.

        if (memoryChunks.Count > 0)
        {
            memoryChunks.RemoveAt(0);
        }
    }

    private void GCButton_Click(object sender, RoutedEventArgs e)
    {
        GC.Collect();
        GC.WaitForPendingFinalizers();
    }

解决方案

As a sanity check, I have a button to force GC. When I push that, I quickly get 6GB back. Doesn't that prove my 6 arrays were not being referenced and COULD have been collected had the GC knew/wanted to?

You are probably better off asking When does the GC automatically collect "garbage" memory?. Off the top of my head:

  • Most commonly, when generation 0 is full or an object allocation won't fit in the available free space.1
  • Somewhat commonly, when allocating a chunk of memory would cause an OutOfMemoryException, a full GC is triggered to first try and reclaim available memory. If not enough contiguous memory is available after the collection, then an OOM exception will be thrown.

When starting a garbage collection, the GC determines what generations need to be collected (0, 0+1, or all). Each generation has a size determined by the GC (it can change as the application runs). If only generation 0 will exceed its budget, that is the only generation whose garbage will be collected. If the objects that survive generation 0 will cause generation 1 to go over its budget, then generation 1 will also be collected and its surviving objects will be promoted to generation 2 (which is the highest generation in Microsoft's implementation). If the budget for generation 2 is exceeded as well, garbage will be collected, but objects can't be promoted to a higher generation, as one doesn't exist.

So, here lies the important information, in the most common way the GC is started, Generation 2 will only be collected if generation 0 and generation 1 are both full. Also, you need to know that objects over 85,000 bytes are not stored in the normal GC heap with generation 0, 1, and 2. It's actually stored in what is called the Large Object Heap (LOH). The memory in the LOH is only freed during a FULL collection (that is, when generation 2 is collected); never when only generations 0 or 1 are being collected.

Why didn't the GC collect? If that wasn't the time to do it, when is?

It should now be obvious why the GC never happened automatically. You're only creating objects on the LOH (keep in mind that int types, the way you've used them, are allocated on the stack and don't have to be collected). You are never filling up generation 0, so a GC never happens.1

You're also running it in 64-bit mode, which means it's unlikely you'll hit the other case I listed above, where a collection occurs when there's not enough memory in the entire application to allocate a certain object. 64-bit applications have a virtual address space limit of 8TB so it would be a while before you hit this case. It's more likely you'll run out of physical memory and page file space before that happens.

Since a GC hasn't happened, windows starts to allocate memory for your application from the available space in the page file.

I've read a lot that says I shouldn't call GC.Collect() but if GC doesn't collect in this situation, what else can I do?

Call GC.Collect() if this kind of code is what you need to write. Better yet, don't write this kind of code outside of testing.

In conclusion, I have not done justice to the topic of automatic garbage collection in the CLR. I recommend reading about it (it's actually very interesting) via msdn blog posts, or as has already been mentioned, Jeffery Richter's excellent book, CLR Via C#, Chapter 21.


1 I'm making the assumption that you understand that the .NET implementation of the GC is a generational garbage collector. In the simplest terms, it means that newly created objects are in a lower numbered generation, i.e. generation 0. As garbage collections are run and it's found that an object that is in a certain generation has a GC root (not "garbage"), it will be promoted to the next generation up. This is a performance improvement since GC can take a long time and hurt performance. The idea is that objects in higher generations generally have a longer life and will be around in the application longer, so it doesn't need to check that generation for garbage as much as the lower generations. You can read more in this wikipedia article. You'll notice it's also called a ephemeral GC.

2 If you don't believe me, after you remove one of the chunks, have a function that creates a whole bunch of random strings or objects (I'd recommend against arrays of primitives for this test) and you'll see after you reach a certain amount of space, a full GC will occur, freeing that memory you had allocated in the LOH.

这篇关于在需要的时候垃圾收集不会发生,即使的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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