我创建了服务器应用程序,它必须在开始侦听客户端之前处理一些数据(这是一个非常繁重的过程)。结果,它分配的内存(大约 30MB)只会被使用一次。
强制垃圾收集会给我带来好处吗?还是让 GC 做它的工作?
此外,我必须使用 .NET 3.5,据我所知,垃圾收集会中断线程,这就是我强制使用垃圾收集器的原因。
我创建了服务器应用程序,它必须在开始侦听客户端之前处理一些数据(这是一个非常繁重的过程)。结果,它分配的内存(大约 30MB)只会被使用一次。
强制垃圾收集会给我带来好处吗?还是让 GC 做它的工作?
此外,我必须使用 .NET 3.5,据我所知,垃圾收集会中断线程,这就是我强制使用垃圾收集器的原因。
好吧,垃圾收集仍然只是一种启发式方法,它通常会尽力做出最好的猜测,但您可能有合理的主张,即在特定时间比它知道得更好。
以拥有大量资产的游戏为例。当您切换到新区域时,您会弹出一个加载屏幕并开始加载新数据,释放对旧数据的引用。它还不会被收集,因为你还没有达到你的门槛,但你可能会在你开始每帧创建你的小矩阵来展示你的游戏时很快达到它,然后你会得到一个(可能很明显)当您卸载数百兆数据并移动其他所有内容以压缩队列时会卡顿。
现在您可以在加载新资源后强制收集,因为用户已经处于“空闲”模式,盯着您的进度条,他不会注意到小卡顿,之后您的游戏整体看起来会更流畅。
真正的诀窍是知道什么时候干涉,什么时候不干涉。如有疑问,请不要——无论如何,该收集都会发生,并且更频繁地发生只会让您的应用程序看起来更加卡顿。你需要一些东西来“隐藏”它,比如一个用进度条锁定你的应用程序的长任务,该进度条会以某种方式生成许多最终对象——实际上,游戏加载屏幕是我唯一能想到的东西,但是 YMMV。
托管语言的部分意义在于,您(大概)永远不必担心垃圾收集。只有在非常特殊的情况下手动调用垃圾收集器才会有用,而且几乎可以肯定这不是其中之一。
您可以在此处阅读有关 GC 背后的一些语义以及使其快速而不是慢的原因,这可能是很好的理论阅读,因此您了解正在发生的事情。
同时,对于您的具体问题,我认为您应该看一下IDisposable接口。如果您有一个很大的特定对象并且您很担心,那么在其上实现 IDisposable 接口将允许您将工作包装在一个using
语句中,这保证该对象将在您完成它的那一刻被释放。在该特定硬币的另一面,它保证该大对象将有一个活动的、打开的句柄,因此在 using 语句结束之前,GC 无法拾取它。
这可能会比尝试手动使用 GC 更好地解决您的问题,GC 在设计上是终结程序员的黑匣子。
你没有说你是如何分配 30MB 内存的,但这很重要。如果您正在分配非托管内存,GC 不知道它(尽管您可以使用方法GC.AddMemoryPressure告诉它)。在这种情况下,您应该在处理非托管内存的类上实现 IDisposable,并且在处理完对象后应该显式调用 Dispose。
如果您正在分配托管内存,只需让 GC 完成其工作,除非: