Java .NET GC在来自终结器()的JNI调用上卡住

Java .NET GC在来自终结器()的JNI调用上卡住,java,.net,java-native-interface,clr,Java,.net,Java Native Interface,Clr,我有一个.NET应用程序,它使用JNI调用Java代码。在.NET终结器上,我们调用JNI调用来清理Java上连接的资源。但这个JNI有时会被卡住。 正如预期的那样,这卡住了整个.NET进程,从未发布过 下面您可以看到我们从.NET获得的线程转储: 网络调用堆栈 作用 .JNIEnv_.NewByteArray(JNIEnv_*, Int32) Bridge.NetToJava.JVMBridge.ExecutePBSCommand(Byte[], Int32, Byte[]) Bridge.

我有一个.NET应用程序,它使用JNI调用Java代码。在.NET终结器上,我们调用JNI调用来清理Java上连接的资源。但这个JNI有时会被卡住。 正如预期的那样,这卡住了整个.NET进程,从未发布过

下面您可以看到我们从.NET获得的线程转储:

网络调用堆栈 作用

.JNIEnv_.NewByteArray(JNIEnv_*, Int32) 
Bridge.NetToJava.JVMBridge.ExecutePBSCommand(Byte[], Int32, Byte[])
Bridge.Core.Internal.Pbs.Commands.PbsDispatcher.Execute(Bridge.Core.Internal.Pbs.PbsOutputStream, Bridge.Core.Internal.DispatcherObjectProxy) 
Bridge.Core.Internal.Pbs.Commands.PbsCommandsBundle.ExecuteGenericDestructCommand(Byte, Int64, Boolean) 
Bridge.Core.Internal.DispatcherObjectProxy.Dispose(Boolean) 
Bridge.Core.Internal.Transaction.Dispose(Boolean) 
Bridge.Core.Internal.DispatcherObjectProxy.Finalize() 
ntdll!KiFastSystemCallRet 
ntdll!NtWaitForSingleObject+c 
kernel32!WaitForSingleObjectEx+ac 
kernel32!WaitForSingleObject+12 
jvm!JVM_FindSignal+5cc49 
jvm!JVM_FindSignal+4d0be 
jvm!JVM_FindSignal+4d5fa 
jvm!JVM_FindSignal+beb8e 
jvm+115b 
jvm!JNI_GetCreatedJavaVMs+1d26 
Bridge_NetToJava+1220 
clr!MethodTable::SetObjCreateDelegate+bd 
clr!MethodTable::CallFinalizer+ca 
clr!SVR::CallFinalizer+a7 
clr!WKS::GCHeap::TraceGCSegments+239 
clr!WKS::GCHeap::TraceGCSegments+415 
clr!WKS::GCHeap::FinalizerThreadWorker+cd 
clr!Thread::DoExtraWorkForFinalizer+114 
clr!Thread::ShouldChangeAbortToUnload+101 
clr!Thread::ShouldChangeAbortToUnload+399 
clr!ManagedThreadBase_NoADTransition+35 
clr!ManagedThreadBase::FinalizerBase+f 
clr!WKS::GCHeap::FinalizerThreadStart+10c 
clr!Thread::intermediateThreadProc+4b 
kernel32!BaseThreadStart+34
完整调用堆栈 作用

.JNIEnv_.NewByteArray(JNIEnv_*, Int32) 
Bridge.NetToJava.JVMBridge.ExecutePBSCommand(Byte[], Int32, Byte[])
Bridge.Core.Internal.Pbs.Commands.PbsDispatcher.Execute(Bridge.Core.Internal.Pbs.PbsOutputStream, Bridge.Core.Internal.DispatcherObjectProxy) 
Bridge.Core.Internal.Pbs.Commands.PbsCommandsBundle.ExecuteGenericDestructCommand(Byte, Int64, Boolean) 
Bridge.Core.Internal.DispatcherObjectProxy.Dispose(Boolean) 
Bridge.Core.Internal.Transaction.Dispose(Boolean) 
Bridge.Core.Internal.DispatcherObjectProxy.Finalize() 
ntdll!KiFastSystemCallRet 
ntdll!NtWaitForSingleObject+c 
kernel32!WaitForSingleObjectEx+ac 
kernel32!WaitForSingleObject+12 
jvm!JVM_FindSignal+5cc49 
jvm!JVM_FindSignal+4d0be 
jvm!JVM_FindSignal+4d5fa 
jvm!JVM_FindSignal+beb8e 
jvm+115b 
jvm!JNI_GetCreatedJavaVMs+1d26 
Bridge_NetToJava+1220 
clr!MethodTable::SetObjCreateDelegate+bd 
clr!MethodTable::CallFinalizer+ca 
clr!SVR::CallFinalizer+a7 
clr!WKS::GCHeap::TraceGCSegments+239 
clr!WKS::GCHeap::TraceGCSegments+415 
clr!WKS::GCHeap::FinalizerThreadWorker+cd 
clr!Thread::DoExtraWorkForFinalizer+114 
clr!Thread::ShouldChangeAbortToUnload+101 
clr!Thread::ShouldChangeAbortToUnload+399 
clr!ManagedThreadBase_NoADTransition+35 
clr!ManagedThreadBase::FinalizerBase+f 
clr!WKS::GCHeap::FinalizerThreadStart+10c 
clr!Thread::intermediateThreadProc+4b 
kernel32!BaseThreadStart+34

由于我没有发现任何问题,因此我不会在这里发布正式答案,而是讲述一个关于我有时经历的类似事件的故事:

我们通过JNI创建了由java对象支持的C对象,并决定在finalize方法中清理C对象。然而,我们设想了死锁,因为finalize是从非应用程序线程(垃圾收集器)调用的。由于在收集垃圾时整个world都会停止,因此每当终结器遇到锁时,它就会立即成为死锁。因此,我们决定使用一种称为幻影引用的java机制。可以将一个数字绑定到这些“引用”(C指针)中的每一个,然后VM删除一个被引用的对象,并将这样的引用放入队列中。我们可以在适当的时候提取这些数据并删除C对象


我想至少你的问题是一样的。

我不知道.NET终结器对于Java终结器来说是否同样是个坏主意,但是使用类似终结器的任何东西(无论平台如何)的潜在(死)锁定代码(我在最底层看到Win32条件调用)肯定是个坏主意。您需要清除本机代码中的任何潜在锁定,或者在Java中使用.NET级别的紧急制动超时。使用终结器被认为是一个坏主意,因此使用.NET中的终结器调用JNI调用Java听起来不是一个好主意谢谢你的回答,但在我们的情况下,情况正好相反。我们从.NET创建Java对象,并希望在收集.NET对象时清理Java对象。我知道这一点。如果.net没有提供类似的机制,您仍然可以在finalize方法中将ID排队。是什么让您解决了这个关键问题。但是,从这个队列中获取项目并执行相同的dispose机制的线程可以完全挂起在相同的位置,我将有一个挂起的线程。我甚至不能中止它,因为它是在本机代码中运行的。除非您认为GC被挂起是因为它调用本机代码导致死锁,而另一个线程不会挂起?我认为一旦GC线程尝试获取应用程序锁,它就会挂起。由于所有应用程序线程都已停止,因此无法释放锁定的线程。如果相同的mechansim在应用程序线程中命中相同的锁,则另一个线程将继续运行并释放锁。。。在GC中,其中一个涉及的锁是大胖子停止世界锁。这就是诀窍。你知道怎么做吗?我们从.NET创建Java对象,并希望在收集.NET对象时清理Java对象收集.NET对象时完全同时清理Java对象是绝对必要的吗?当.NET对等对象已被收集时,是否存在尝试使用孤立Java对象的危险?您不能将Java中的清理与.NET分离吗?在不同的时间做?