如何强制运行垃圾回收器?

186

面试官今天问了我这个问题...有答案吗?


6
这个问题提供了关于为什么不应该这样做的好信息:https://dev59.com/13VD5IYBdhLWcg3wAWoO - Hans Olsson
3
@Jason - 也许面试官并不想听解决方案,而是想了解Andrew的知识和反对做法的理由,这样这个问题才有意义。 - martinstoeckli
3
这仍然是一个糟糕的问题。在紧张的面试中,具有相关知识的人可能没有意识到这是个诡计问题,或者考虑提供额外信息。那么这个问题就无助于面试官区分具有相关知识和没有相关知识的人。因此,这个问题仍然很糟糕。 - James
1
请参阅 https://dev59.com/RXRB5IYBdhLWcg3w4bKv#1472554。 - Ian Ringrose
9
“你什么时候会强制进行垃圾回收?”听起来像是一个不错的面试问题(“怎么”并不是那么好)。初学者:我不知道。中级者:从不。专家:当你为内存泄漏编写测试时。 - mikebridge
显示剩余2条评论
10个回答

228

System.GC.Collect() 强制运行垃圾回收器。虽然这不被推荐使用,但在特定情况下可以使用。


23
为什么不建议这样做? - Luke T O'Brien
10
运行GC.Collect()的成本很高,过于频繁地运行不可取。判断何时调用它的代码编写得很好。通常只有在专门的边缘情况下才需要自我收集。 - Brandon Barkley

224

不建议显式调用gc,但如果您调用了

GC.Collect();
GC.WaitForPendingFinalizers();

在代码中会明确调用GC,别忘记在GC.Collect()之后调用GC.WaitForPendingFinalizers();


16
WaitForPendingFinalizers 并不一定能提供 "更好的性能":它只是简单地阻塞直到终结队列中的所有对象都被终结(这些对象可能是由你之前调用 Collect 放置在那里的)。如果你希望有机会收集这些对象,那么你需要在调用 WaitForPendingFinalizers 后再次调用 Collect - LukeH
2
由于调用gc.Collect的少数好理由之一是最后一招,以释放未正确处理的资源,因此WaitForPendingFinalizers似乎是必要的步骤。不确定在这种情况下避免过度调用gc.Collect的最佳实践是什么。顺便问一下,我想知道是否有任何技术原因,使gc.Collect“必须”提升代?除非在一个代中分配了足够的东西来证明它的进展,否则保持代不变似乎更有用。 - supercat
3
针对C#初学者(比如我)的提示:强制调用GC并不能保证从内存中移除某些具体未使用的对象。一种解决方法是使用“using语句”(https://msdn.microsoft.com/ru-ru/library/yh598w02(v=vs.80).aspx)。 - user1234567
2
为什么调用WaitForPendingFinalizers被认为很重要? - Christian

21
GC.Collect() 

来自MDSN

使用此方法尝试回收所有不可访问的内存。

所有对象,无论它们在内存中存在多长时间,都将被考虑进行回收;但是,在托管代码中引用的对象不会被回收。使用此方法强制系统尝试回收最大量的可用内存。



13

GC.Collect();

不过要记住,垃圾回收器可能不会始终清理您所期望的内容...


6
你是什么意思?它可能意外地清洁什么? - Saturn
6
@Voldemort - 它不会意外地清理任何东西。但它可能无法清理您期望它清理的所有东西。 - Justin Niessner

6

您不希望强制垃圾回收器运行。

但是,如果您真的这样做了(当然只是作为一种纯学术练习):

GC.Collect()

4
我更喜欢@Karthik的答案,有一些情况下应用程序可以合理地调用GC.Collect()。虽然这种情况非常罕见。 - H H
2
假设您有一个弱引用数组,并且想要检查是否存在任何“活”引用。在检查之前调用GC.Collect()是有意义的。只是这么说。 - Rich Ehmer
2
因为开头陈述事实不正确而被踩了;是的,我确实想强制运行垃圾收集器。 - Syndog

5

我认为 .Net Framework 会自动完成这个操作,但是为了保险起见,首先确保选择要删除的内容,然后调用垃圾回收器:

randomClass object1 = new randomClass
...
...
// Give a null value to the code you want to delete
object1 = null;
// Then call the garbage collector to erase what you gave the null value
GC.Collect();

我想就是这样。希望我能帮到某些人。

4

由于我的声望太低,无法评论,所以我将其发布为答案,因为它在我苦苦挣扎数小时后帮助了我,并且可能会帮助其他人:

正如大多数人所说,通常不建议使用GC.Collect()进行垃圾回收,除非是在极端情况下。作为这样一个示例,运行垃圾回收正是我场景的解决方案。

我的程序在线程中对文件执行长时间运行的操作,然后从主线程中删除该文件。但是:当文件操作引发异常时,.NET直到垃圾实际上被收集之前都不会释放文件锁,即使长时间运行的任务封装在using语句中也不会释放。因此,在尝试删除文件之前,程序必须强制执行垃圾回收。

代码如下:

var returnvalue = 0;
using (var t = Task.Run(() => TheTask(args, returnvalue)))
{
  // TheTask() opens a file and then throws an exception. The exception itself is handled within the task so it does return a result (the errorcode)
  returnvalue = t.Result;
}

//Even though at this point the Thread is closed the file is not released untill garbage is collected
System.GC.Collect();
DeleteLockedFile();

0

显式调用GC.Collect()方法可能无法清除您想要的堆内存中的对象。它在内部管理以保持应用程序可用的内存,并通过处理一段时间未使用的对象来分配对象的内存。只有在应用程序或代码在执行过程中需要更多内存时才这样做。


1
这更像是一条评论,而不是答案,应该以评论的形式发布。 - Mark Gjøl

0

这里有一个替代建议。像您已经做的那样保留服务器GC。然后基于用户定义的设置,如果应用程序内存超过您确定为关键级别的某个阈值,则强制运行GC。

请注意,如果您按照此路径进行,实际上您表明您比CLR更了解垃圾收集应该何时运行。大多数情况下,我发现CLR单独工作时比我们干预它时执行得更好。

代码以检查内存使用情况并跨所有代或特定代运行GC

    long UsedMemory;
//UsedMemory = GC.GetTotalMemory(false); // Not as reliable
UsedMemory = System.Diagnostics.Process.GetCurrentProcess().PagedMemorySize64;
if (UsedMemory > 1073741824) // One GB in bytes 1 X 1024 X 1024 X 1024
{
    GC.Collect(); // Collect all generations
    //GC.Collect(2,GCCollectionMode.Forced);; Or collect a specific generation and force it to run now
}

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