GC.KeepAlive()
引用指定的对象,这使得它不符合从当前例程的开始到调用此方法的点的垃圾收集。
不确定GC.KeepAlive除了简单地存储引用之外还做什么,以便垃圾收集器不收集对象。 但是在对象上调用GC.KeepAlive()会永久保持对象不被收集吗?或者你必须经常重新调用GC.KeepAlive()(如果是这样,多久一次)?我想保持我的键盘钩活着。
答案 0 :(得分:10)
当您为Release目标编译.NET代码时,垃圾收集器非常具有攻击性,也就是说,它具有潜在的可能性。
举个例子:
public void Test()
{
FileStream stream = new FileStream(....);
stream.Write(...);
SomeOtherMethod();
}
在此示例中,一旦调用Stream.Write
,此方法就不再使用stream
变量,因此被视为超出范围,这意味着{{1} } object现在符合收集条件。如果垃圾收集器在调用FileStream
期间运行,则可能会收集流对象。
编辑:在注释中指出@Greg Beech,即使当前正在执行实例方法,也可以收集对象。
例如,假设SomeOtherMethod
中的代码如下所示:
FileStream.Write
这里,该方法首先获取包含非托管句柄的内部字段。在此之前不会收集该对象。但是,如果这是对象的最后一个字段,或者是在Write中访问的对象的最后一个实例方法,那么在转换到P / Invoke调用之前,该对象现在有资格进行收集。
由于FileStream可能有一个非托管文件句柄(我说可能,我不知道),它可能也有一个终结器,因此非托管文件句柄有可能在WriteBuffer调用完成之前被关闭。也就是说,如果假设public void Write(byte[] buffer, ...)
{
IntPtr unmanagedHandle = _InternalHandleField;
SomeWindowsDll.WriteBuffer(unmanagedHandle, ...);
...
的实现如上,那么它很可能不是。
如果您希望阻止这种情况,请在调用.Write
期间使流对象生效,无论出于何种原因,您需要在以某种方式“使用”对象的调用之后插入代码。如果您不想调用方法或读取对象上的属性,可以使用SomeOtherMethod
的人工“使用”方法来执行此操作:
GC.KeepAlive
该方法不执行任何操作,但已使用属性标记,因此不会对其进行优化。这意味着流变量现在在public void Test()
{
FileStream stream = new FileStream(....);
stream.Write(...);
SomeOtherMethod();
GC.KeepAlive(stream);
}
调用期间使用,并且在此期间不会收集存储在其中的SomeOtherMethod
对象。
就是这样。 FileStream
不会在任何东西上留下永久性标记,它不会在被调用后改变对象的生命周期,它只是添加在某个时刻“使用”变量的代码,这会阻止垃圾收集器收集对象。 / p>
我了解了这个方法,或者更确切地说,这个方法的重点,一些代码看起来很难的方法:
GC.KeepAlive
请注意,我构造了一个实现public void Test()
{
SomeObjectThatCanBeDisposed d = new SomeObjectThatCanBeDisposed();
SomeInternalObjectThatWillBeDisposed i = d.InternalObject();
CallSomeMethod(i);
}
的对象的实例,在原始情况下,它还实现了一个终结器。然后,我“捕获”了它跟踪的对象,并且一次性对象的设置方式(错误地)是,一旦终结器运行,它也会处理内部对象(这是坏的)。在上面的代码中,一旦提取了内部对象,d就有资格进行收集,一旦收集了对象,它就会通过处理我提取的对象来完成自身。这意味着在调用IDisposable
期间,传递给方法的对象死了,我无法理解为什么。
当然,上述代码的修复不是使用CallSomeMethod
,而是修复错误的终结器,但如果“内部对象”是非托管资源,那么事情可能会有所不同。
现在,对于键盘钩子,如果将引用存储在根中,如静态字段,也保持活动的对象的成员字段等,则不应该将其收集到蓝色中。
答案 1 :(得分:2)
GC.KeepAlive(以及GC.Collect)不应该在生产代码中使用,因为它不能解决任何问题,只会产生更多的竞争条件。它们仅用于诊断问题。
如果本机代码使用了托管对象,因此垃圾收集器无法看到它仍在使用,那么您应该使用GCHandle.Alloc。
编辑:一些支持证据:
GC.KeepAlive并不是很好用,当本地代码的使用在返回之前保证结束时肯定是好的,这不是键盘钩子的情况。