- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
GC.KeepAlive()
References the specified object, which makes it ineligible for garbage collection from the start of the current routine to the point where this method is called.
除了简单地存储引用以便垃圾收集器不收集对象之外,不太确定 GC.KeepAlive 做了什么。 但是在对象上调用 GC.KeepAlive() 是否会永久阻止对象被收集?或者您是否必须每隔一段时间重新调用一次 GC.KeepAlive()(如果需要,多久一次)?我想让我的键盘 Hook 保持事件状态。
最佳答案
当您为 Release 目标编译 .NET 代码时,垃圾收集器真的很激进,也就是说,它有可能成为。
举个例子:
public void Test()
{
FileStream stream = new FileStream(....);
stream.Write(...);
SomeOtherMethod();
}
在此示例中,一旦对 Stream.Write
的调用返回,此方法就不再使用 stream
变量,因此被认为超出范围,这意味着 FileStream
对象现在可以收集了。如果垃圾收集器在调用 SomeOtherMethod
期间运行,则该流对象可能会被收集。
编辑:作为@Greg Beech在评论中指出,即使当前正在执行实例方法,也可以收集对象。
例如,假设 FileStream.Write
中的代码如下所示:
public void Write(byte[] buffer, ...)
{
IntPtr unmanagedHandle = _InternalHandleField;
SomeWindowsDll.WriteBuffer(unmanagedHandle, ...);
...
这里,该方法首先获取包含非托管句柄的内部字段。在此之前不会收集该对象。但是,如果这是在 Write 中访问的对象的最后一个字段,或者对象的最后一个实例方法,在转换为仅 P/Invoke 调用之前,那么该对象现在符合收集条件。
而且由于 FileStream 可能有一个非托管文件句柄(我说可能,我不知道),它可能也有一个终结器,所以非托管文件句柄有在 WriteBuffer 调用完成之前被关闭的风险.也就是说,如果 .Write
的假定实现如上,则很可能不是。
如果您希望防止这种情况发生,无论出于何种原因,都需要在调用 SomeOtherMethod
期间使流对象处于事件状态,您需要在某些“使用”该对象的调用之后插入代码方法。如果不想调用方法或读取对象的属性,可以使用 GC.KeepAlive
的人为“用法”方法来完成:
public void Test()
{
FileStream stream = new FileStream(....);
stream.Write(...);
SomeOtherMethod();
GC.KeepAlive(stream);
}
该方法不执行任何操作,但已使用属性对其进行标记,因此不会被优化掉。这意味着流变量现在在调用 SomeOtherMethod
期间使用,并且存储在其中的 FileStream
对象在此期间不会被收集。
这就是它所做的一切。 GC.KeepAlive
不会在任何东西上留下永久性标记,它不会在调用后改变对象的生命周期,它只是添加在某个时刻“使用”变量的代码,从而防止垃圾收集器收集对象。
我了解了这种方法,或者更确切地说,了解了这种方法的要点,这是一些看起来像这样的代码的困难方法:
public void Test()
{
SomeObjectThatCanBeDisposed d = new SomeObjectThatCanBeDisposed();
SomeInternalObjectThatWillBeDisposed i = d.InternalObject();
CallSomeMethod(i);
}
请注意,我构造了一个实现 IDisposable
的对象实例,并且在最初的情况下,它还实现了一个终结器。然后我“捞出”一个它跟踪的对象,一次性对象的设置方式(错误地)是一旦终结器运行,它也会处理内部对象(这是不好的)。在上面的代码中,一旦提取了内部对象,d 就符合收集条件,一旦对象被收集,它就会通过处理我提取的对象来完成自身。这意味着在调用 CallSomeMethod
期间,传递给该方法的对象死亡,我不明白为什么。
当然,上述代码的修复不是使用GC.KeepAlive
,而是修复不正确的终结器,但如果“内部对象”是非托管资源,事情可能不一样了。
现在,对于您的键盘 Hook ,如果您将引用存储在根中,例如静态字段、对象的成员字段也保持事件状态等,那么它不应该被突然收集。
关于c# - 垃圾邮件 GC.KeepAlive(KeyboardHookPointer) 有什么问题吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2619252/
GC.KeepAlive() References the specified object, which makes it ineligible for garbage collection fro
我是一名优秀的程序员,十分优秀!