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