Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/275.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# CLR GC线程行为:意外完成SafeFileHandle_C#_.net_Garbage Collection_Safefilehandle - Fatal编程技术网

C# CLR GC线程行为:意外完成SafeFileHandle

C# CLR GC线程行为:意外完成SafeFileHandle,c#,.net,garbage-collection,safefilehandle,C#,.net,Garbage Collection,Safefilehandle,我们最近讨论了一些可能与CLR的GC行为有关的问题 我遇到的问题如下: public class ScteFileHandle { /// <summary> /// local file handle /// </summary> [NonSerialized] public FileStream FileStreamHandle; /* * Some other fields */ 我们有一个用C

我们最近讨论了一些可能与CLR的GC行为有关的问题

我遇到的问题如下:

public class ScteFileHandle
{
    /// <summary>
    /// local file handle
    /// </summary>
    [NonSerialized]
    public FileStream FileStreamHandle;

    /*
     * Some other fields
     */
我们有一个用C#编写的长期运行的压力测试应用程序,它不断打开远程SMB文件共享(即Azure文件服务)上的文件句柄,并使用这些句柄执行文件系统操作,如读/写等

通常,我们会在相当长的时间内保持这些句柄打开,因为我们会重复使用它们。但有时当我们试图访问一些打开的句柄时,我们发现这些句柄已经关闭了。从Process Monitor捕获的跟踪日志(以下一个示例):


fltmgr.sys!FltpPerformPreCallbacks+0x324
fltmgr.sys!FltpPassThroughInternal+0x8c
fltmgr.sys!FltpPassThrough+0x169
fltmgr.sys!FltpDispatch+0x9e
ntoskrnl.exeIopCloseFile+0x146
ntoskrnl.exObjpDecrementHandleCount+0x9a
ntoskrnl.exeNtClose+0x3d9
ntoskrnl.exeKiSystemServiceCopyEnd+0x13
ntdll.dll!ZwClose+0xa
KERNELBASE.dll!闭合手柄+0x17
mscorlib.ni.dll!mscorlib.ni.dll+0x566038
clr.dll!CallDescrWorkerInternal+0x83
clr.dll!CallDescrWorkerWithHandler+0x4a
clr.dll!DispatchCallSimple+0x60
clr.dll!SafeHandle::RunReleaseMethod+0x69
clr.dll!安全手柄::释放+0x152
clr.dll!SafeHandle::Dispose+0x5a
clr.dll!安全句柄::处置性+0x9b
mscorlib.ni.dll!mscorlib.ni.dll+0x48d9d1
mscorlib.ni.dll!mscorlib.ni.dll+0x504b83
clr.dll!FastCallFinalizeWorker+0x6
clr.dll!FastCallFinalize+0x55
clr.dll!MethodTable::CallFinalizer+0xac
clr.dll!WKS::CallFinalizer+0x61
clr.dll!WKS::DoOneFinalization+0x92
clr.dll!WKS::FinalizeAllObjects+0x8f
clr.dll!WKS::FinalizeAllObjects_包装器+0x18
clr.dll!ManagedThreadBase_DispatchInner+0x2d
clr.dll!ManagedThreadBase_DispatchMiddle+0x6c
clr.dll!ManagedThreadBase_DispatchOuter+0x75
clr.dll!ManagedThreadBase_DispatchInCorrectAD+0x15
clr.dll!线程::DoADCallBack+0xff
clr.dll!ManagedThreadBase\u DispatchInner+0x1d822c
clr.dll!WKS::DoOneFinalization+0x145
clr.dll!WKS::FinalizeAllObjects+0x8f
clr.dll!WKS::GCHeap::FinalizerThreadWorker+0xa1
clr.dll!ManagedThreadBase_DispatchInner+0x2d
clr.dll!ManagedThreadBase_DispatchMiddle+0x6c
clr.dll!ManagedThreadBase_DispatchOuter+0x75
clr.dll!WKS::GCHeap::FinalizerThreadStart+0xd7
clr.dll!线程::intermediateThreadProc+0x7d
内核32.dll!BaseThreadInitThunk+0x1a
ntdll.dll!RtlUserThreadStart+0x1d

在CLR GC终结器线程中,句柄似乎已关闭。但是,我们的句柄是按以下模式打开的,不应使用GC:

我们使用p/Invoke打开一个文件句柄并获取一个SafeFileHandle,然后使用该SafeFileHandle构造一个FileStream,我们将把FileStream对象保存在另一个定义如下的对象中:

public class ScteFileHandle
{
    /// <summary>
    /// local file handle
    /// </summary>
    [NonSerialized]
    public FileStream FileStreamHandle;

    /*
     * Some other fields
     */
SafeFileHandle fileHandle=Win32FileIO.CreateFile(完整文件路径、win32FileAccess、win32FileShare、IntPtr.Zero、win32FileMode、win32FileAttr、IntPtr.Zero)

FileStream FileStream=newfilestream(fileHandle、fileAccess、Constants.XSMBFileSectorSize)

我们可以肯定的是,在压力测试应用程序的整个生命周期中,我们肯定会保留对ScteFileHandle对象的引用,因此它永远不会被GC清除。然而,我们确实观察到了ScteFileHandle的文件流中引用的SafeHandle在CLR GC线程中最终确定,如上面的跟踪日志中所粘贴的

所以我想知道是什么导致SafeFileHandle被GC’ed,是否有任何方法可以避免这种情况?我不熟悉CLR GC行为,但从我的角度来看,不应该对SafeFileHandle进行GC


非常感谢您的指点或见解!请告诉我是否需要任何其他详细信息来诊断此问题:)

SafeFileHandle可以作为任何其他对象收集,这在这方面并不特别,但它有一个标志,指定对象是否拥有其持有的句柄,如果没有,则在收集对象时不会关闭句柄。虽然看起来您仍在保留句柄,但能否显示在收集时堆栈上的最后一个方法?“引用
ScteFileHandle
对象并不能保护它不受GC的影响。有一个可能在将来阅读的参考文献确实如此。因此,如果它只是一个局部变量,那么仅仅将它放在该变量中(如果您再也无法访问它)不会保存它。或者,如果它位于类的某个字段中,则范围更广,但是如果可以证明该类中不需要
,则即使它的一个或多个方法仍在运行,也可以收集它。著名的是,GC具有足够的攻击性,即使对象的构造函数仍在运行,它也可以收集对象,如果不需要其他
this
访问,则GC具有超强攻击性,这意味着除非您确保保留了保留句柄的对象,否则即使尚未使用句柄,它们也可能被收集,对象上的句柄等的一个典型问题是,在一个方法中,您从字段中取出句柄并将其放入一个局部变量,然后开始执行P/Invoke。如果GC启动并看到您不再使用实际对象,它可能会将其放入freachable队列中,以便终结器在收集它之前完成,如果这也能在您的方法期间执行,则终结器可能会关闭句柄。有点像锯掉你坐在上面的分支。GC是如此具有攻击性,它甚至可以清理你调用该方法的对象,这仅仅是因为
这个
引用与当时的任何其他引用一样。