NullReferenceException 来自一个不可能的地方?

NullReferenceException from an impossible place?

提问人:Jeffrey Zhao 提问时间:6/3/2015 最后编辑:CommunityJeffrey Zhao 更新时间:6/10/2015 访问量:1163

问:

我想知道是否有人曾经和我遇到过同样的情况,发生在一个不可能的地方。NullReferenceException

下面是引发异常的完全相同的代码片段:

private readonly StatePair[] _pairs =
{
  new StatePair(0),
  new StatePair(1),
  new StatePair(2)
};

public void CommitAll()
{
  var states = new State[_pairs.Length];

  // collect in reverse order
  for (var i = _pairs.Length - 1; i >= 0; i--)
  {
    // The only exit of CaptureState() method is a "new State()" statement.
    states[i] = _pairs[i].CaptureState();

    if (states[i] == null)
      throw new ApplicationException("The state captured is null.");
  }

  // commit in normal order
  foreach (var s in states)
  {
    if (s == null)
      throw new ApplicationException("The state is null.");

    s.Commit(); // *** NullReferenceException ***
  }
}

void Run() // Thread start
{
  while (true)
  {
    CommitAll();

    Thread.Sleep(200);
  }
}

堆栈跟踪:

System.NullReferenceException: Object reference not set to an instance of an object.
   at ...SystemUpdateCoordinator.CommitAll() in ...SystemUpdateCoordinator.cs:line 217
   at ...SystemUpdateCoordinator.Run() in ...SystemUpdateCoordinator.cs:line 22
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading.ThreadHelper.ThreadStart()

从堆栈跟踪中,我们可以看出 throws,所以看起来是 null,但我们已经检查了 null 并在每个 .如果现在返回,我们应该从正文而不是内部得到一个。s.Commit()NullReferenceExceptionsApplicationExceptionCaptureState()CaptureStateApplicationExceptionforNullReferenceExceptionforeach

可怕的是,并非所有用户都会收到异常(这是一个 WPF 应用程序)。我们大约有 10 个用户运行这个新版本,其中只有 4 个遇到错误,但它在两天内多次发生在他们身上。我们是一家大公司,因此所有用户的环境都受到限制控制,并且应该是相同的(.NET 4.0、Win 7)。

此外,这里还有一些可能会有所帮助的细节:

是否涉及多线程?

是的,但唯一的部分可能是同时访问对象,并且在 method 中它持有来自 .我已经仔细查看了其他方法,所有这些方法都受到同一实例的读取器锁的保护。此外,每次都返回一个新实例。所有其余变量都是局部变量(如),它们不会有线程问题。MyPairCaptureStateReaderWriterLockSlimMyPairReaderWriterLockSlimCaptureState()s

同时发生任何其他相关错误吗?

是的,实际上,同一新版本的应用程序还发生了另一个错误,即访问冲突错误。它应该和这个一样,但我不能 100% 确定这是原因,原因有两个:NullReferenceException

  1. 这两者并不总是一起发生,尽管它们在大多数时候一起发生。
  2. 如果并发 GC 错误损坏了内存,从而带来了 ,为什么它总是发生在相同的语句中?为什么它不会发生在其他地方?NullReferenceException

这个问题困扰了我好几天。我将其视为运行时问题,但这不是我可以向其他人证明或证明它错误的东西。我也在尝试稍微改变一下方法,看看我们是否可以获得不同的汇编代码。CommitAll()

如果我有更多信息,我会在这里发布更新。

==== 更新 1 (20150603) ====

有人问,鲜明的痕迹是否准确,我认为是。

首先,该方法没有内联。我已经转储了汇编代码,它调用了没有内联的方法。CommitAll()Commit

此外,首先这里没有检查。代码很简单,因为这里不可能有 null:throw new ApplicationException()

public void CommitAll()
{
  var states = new State[_pairs.Length];

  for (var i = _pairs.Length - 1; i >= 0; i--)
  {
    states[i] = _pairs[i].CaptureState();
  }

  foreach (var s in states)
  {
    s.Commit(); // <- Line 209
  }
}

堆栈跟踪指向第 209 行,即 .因此,我添加了一些 null 检查,堆栈跟踪变为第 217 行,这正是同一语句的新位置。s.Commit()

==== 更新 2 (20150609) ====

崩溃在过去一周只发生过一次,但奇怪的是,WER(Windows 错误报告)没有生成小型崩溃转储。事件日志仍然显示:

应用: MyApp.exe
Framework 版本: v4.0.30319
说明: 由于 IP 为 000007FEEC0F25E8 (000007FEEC0B0000) 的 .NET 运行时内部错误,退出代码为 80131506,进程已终止。

这与以前的错误日志不同,明确指出这是访问违规。用户说他没有看到 WER 对话框,因此应用程序突然崩溃。

所以我只能检查旧崩溃中的旧转储。

其中一位说这是一个“著名的并发 GC 问题”:

This dump file has an exception of interest stored in it.
The stored exception information can be accessed via .ecxr.
(2c90.381c): Access violation - code c0000005 (first/second chance not available)
clr!WKS::gc_heap::mark_object_simple+0x19a:
000007fe`eb13dc87 f70100000080    test    dword ptr [rcx],80000000h ds:00000000`00000000=????????
0:008> kb
RetAddr           : Args to Child                                                           : Call Site
000007fe`eb13dd8e : 00000000`b2103c48 00000000`2c92d850 00000000`2c92e9c0 00000000`b2023f08 : clr!WKS::gc_heap::mark_object_simple+0x19a
000007fe`eb1de7be : 00000000`b2103c48 00000000`2c92e9c0 00000000`2c92c2e0 00000000`2c92d7b0 : clr!WKS::GCHeap::Promote+0x79
000007fe`eb1e04ec : 00000000`2c92bec0 00000000`00000007 00000000`00000002 00000000`00000071 : clr!GcEnumObject+0x37
000007fe`eb1dfdd5 : 000007fe`00000000 00000000`00000000 00000000`00000000 000007fe`00000008 : clr!GcInfoDecoder::EnumerateLiveSlots+0x5dd
000007fe`eb1dea47 : 00000000`00080000 00000000`00000000 00000000`0230b540 00000000`2c92c2e0 : clr!EECodeManager::EnumGcRefs+0x13d
000007fe`eb09109f : 00000000`00000000 000007ff`02668548 00000000`2c92d7b0 00000000`00000000 : clr!GcStackCrawlCallBack+0x1e2
000007fe`eb090a6b : 00000000`2c92cb50 00000000`00000000 00000000`00000002 00000000`00000000 : clr!Thread::MakeStackwalkerCallback+0x2f
000007fe`eb0900f2 : 00000000`1fc3ab10 00000000`1fc3ab10 000000f8`00000000 00000000`00000002 : clr!Thread::StackWalkFramesEx+0x8d
000007fe`eb1de6e8 : 00000000`00000000 00000000`00000001 00000000`00000000 00000000`1fc3ab10 : clr!Thread::StackWalkFrames+0xb1
000007fe`eb13d831 : 00000000`00000000 00000000`2c92d880 00000000`00000000 00000000`023ade80 : clr!CNameSpace::GcScanRoots+0x1a4
000007fe`eb13de6d : 00000000`00000000 00000000`00000000 000007fe`eb8ea160 000007fe`eb13e6b6 : clr!WKS::gc_heap::mark_phase+0xe1
000007fe`eb29fcfd : 00000107`e77ad732 00000000`2c92d959 00000000`00000000 00000000`00000000 : clr!WKS::gc_heap::gc1+0xae
000007fe`eb13e44e : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : clr!WKS::gc_heap::garbage_collect+0x42e
000007fe`eb13d3fe : 00000000`00000030 00000000`00000002 000007fe`eb060000 00000000`00000000 : clr!WKS::GCHeap::GarbageCollectGeneration+0x14e
000007fe`eb13d12e : 00000000`0000001d 00000000`b2526b80 00000000`00000002 00000000`00000030 : clr!WKS::gc_heap::try_allocate_more_space+0x25f
000007fe`eb08d418 : 000007fe`e8b93028 00000000`00000030 00000000`00000002 00000000`2c92ddf0 : clr!FastAllocateObject+0x73e
Unable to load image C:\Windows\assembly\NativeImages_v4.0.30319_64\System.Core\84fa340f30d1921e0d8817f9344ee367\System.Core.ni.dll, Win32 error 0n2
*** WARNING: Unable to verify checksum for System.Core.ni.dll
000007fe`e8b25049 : 000007fe`e8b93028 00000000`b2526b00 00000000`b2526b00 000007fe`e8b24f9a : clr!JIT_NewFast+0xb8
Unable to load image C:\Windows\assembly\NativeImages_v4.0.30319_64\mscorlib\79d73b390cca60b8a1c1d1228c771f2f\mscorlib.ni.dll, Win32 error 0n2
*** WARNING: Unable to verify checksum for mscorlib.ni.dll
000007fe`ea18e458 : 00000000`b2526b00 00000000`00000004 00000000`b2526b00 00000000`00000000 : System_Core_ni+0x2a5049
000007fe`e8b37280 : 000007fe`e88849d8 000007fe`ea17efb0 00000000`0328be48 00000000`b24ee798 : mscorlib_ni+0x3ae458
000007ff`027790bc : 000007fe`e88dc738 00000000`b2524ed0 00000000`03137038 00000000`2c92dde8 : System_Core_ni+0x2b7280
000007ff`027789a0 : 00000000`00000000 00000000`00000000 000007ff`03b5ba48 00000000`067059f0 : 0x7ff`027790bc
...
000007fe`ea15169c : 00000000`04118280 00000000`0416ea50 00000000`0416ea50 00000000`00000000 : 0x7ff`024799c0
000007fe`ea1515ab : 00000000`0416ea50 00000000`00000000 00000000`00000000 000007fe`eb0a5a1f : mscorlib_ni+0x37169c
000007fe`ea1e6d8d : 00000000`0416ea50 00000000`00000000 00000000`00000000 00000000`00000000 : mscorlib_ni+0x3715ab
000007fe`eb09c9e4 : 00000000`0416ea78 00000000`00000000 00000000`00000000 00000000`00000000 : mscorlib_ni+0x406d8d
000007fe`eb09caf9 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : clr!CallDescrWorker+0x84
000007fe`eb09cb75 : 00000000`2c92ef48 00000000`00000001 00000000`2c92ef50 00000000`2c92f1a0 : clr!CallDescrWorkerWithHandler+0xa9
000007fe`eb09d0ac : 00000000`2c92f198 000007fe`ea0e4860 00000000`2c92f230 000007fe`e9eace7c : clr!MethodDesc::CallDescr+0x2a1
000007fe`eb16de50 : 00000000`2c92f640 00000000`2c92f220 00000000`2c92f6e0 000007fe`ea2bdf28 : clr!MethodDesc::CallTargetWorker+0x44
000007fe`eb1008e6 : 00000000`1fc3ab10 00000000`2c92f640 00000000`1fc3ab10 00000000`00001000 : clr!ThreadNative::KickOffThread_Worker+0x148
000007fe`eb10087b : 00000000`00000000 00000000`1fc3ab10 ffffffff`fffffffe 00000000`1fc3ab10 : clr!QueueUserWorkItemManagedCallback+0x92
000007fe`eb1007e8 : 00000000`00000480 00000000`004d0000 00000000`00000000 00000000`00000478 : clr!PEDecoder::CheckILOnlyImportDlls+0x294
000007fe`eb10094b : ffffffff`ffffffff 00000000`1fc3ab10 00000000`00000000 00000000`00000000 : clr!StubLinkerCPU::X86EmitPushReg+0x135
000007fe`eb16dca0 : 00000000`1fc3ab10 00000000`2c92fd80 00000000`00000001 00000000`00000000 : clr!COMArrayInfo::GetReference+0x12b
000007fe`eb22c736 : 00000000`28ef64b0 00000000`2c92f6f8 00000000`1fc3ab10 00000000`772cb98e : clr!ThreadNative::KickOffThread+0xc0
00000000`771959cd : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : clr!Thread::intermediateThreadProc+0x7d
00000000`772cb891 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : kernel32!BaseThreadInitThunk+0xd
00000000`00000000 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : ntdll!RtlUserThreadStart+0x1d

另一个说了一些不同的话:

This dump file has an exception of interest stored in it.
The stored exception information can be accessed via .ecxr.
(1714.ed4): Access violation - code c0000005 (first/second chance not available)
clr!VirtualCallStubManager::ResolveWorker+0x4a3:
000007fe`edb827d8 488b8980000000  mov     rcx,qword ptr [rcx+80h] ds:00000101`00000c84=????????????????
0:012> kb
RetAddr           : Args to Child                                                           : Call Site
000007fe`edb82713 : 00000000`00e2f360 00000000`2c50e740 00000000`00000000 800003ff`825b7314 : clr!VirtualCallStubManager::ResolveWorker+0x4a3
000007fe`edb43585 : 00000000`00000003 000007ff`0442a6b0 00000000`a004a410 00000000`8bd38210 : clr!VirtualCallStubManager::ResolveWorkerStatic+0x213
000007ff`02b416fb : 00000000`b7476e68 00000000`2c50e910 00000000`00e2bfd0 00000000`00000000 : clr!ResolveWorkerAsmStub+0x95
000007ff`02b41344 : 00000000`0a48bc20 00000000`a0444b88 00000000`8bd37108 000007fe`edbe45e4 : 0x7ff`02b416fb
...
000007ff`0245b040 : 00000000`000000fa 00000000`04157e38 00000000`045a09d0 00000000`00000000 : 0x7ff`0245b3e4
Unable to load image C:\Windows\assembly\NativeImages_v4.0.30319_64\mscorlib\79d73b390cca60b8a1c1d1228c771f2f\mscorlib.ni.dll, Win32 error 0n2
*** WARNING: Unable to verify checksum for mscorlib.ni.dll
000007fe`ea7e169c : 00000000`0431c4a0 00000000`045a09d0 00000000`045a09d0 00000000`00000000 : 0x7ff`0245b040
000007fe`ea7e15ab : 00000000`045a09d0 00000000`00000000 00000000`00000000 000007fe`edb85a1f : mscorlib_ni+0x37169c
000007fe`ea876d8d : 00000000`045a09d0 00000000`00000000 00000000`00000000 00000000`00000000 : mscorlib_ni+0x3715ab
000007fe`edb7c9e4 : 00000000`045a09f8 00000000`00000000 00000000`00000000 00000000`00000000 : mscorlib_ni+0x406d8d
000007fe`edb7caf9 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : clr!CallDescrWorker+0x84
000007fe`edb7cb75 : 00000000`2c50f168 00000000`00000001 00000000`2c50f170 00000000`2c50f3c0 : clr!CallDescrWorkerWithHandler+0xa9
000007fe`edb7d0ac : 00000000`2c50f3b8 000007fe`ea774860 00000000`2c50f450 000007fe`ea53ce7c : clr!MethodDesc::CallDescr+0x2a1
000007fe`edc4de50 : 00000000`2c50f860 00000000`2c50f440 00000000`2c50f900 000007fe`ea94df28 : clr!MethodDesc::CallTargetWorker+0x44
000007fe`edbe08e6 : 00000000`1c696620 00000000`2c50f860 00000000`1c696620 00000000`00001000 : clr!ThreadNative::KickOffThread_Worker+0x148
000007fe`edbe087b : 00000000`00000000 00000000`1c696620 ffffffff`fffffffe 00000000`1c696620 : clr!QueueUserWorkItemManagedCallback+0x92
000007fe`edbe07e8 : 000007ff`fffdc000 00000000`00000002 00000000`00000002 000007fe`f51f7163 : clr!PEDecoder::CheckILOnlyImportDlls+0x294
000007fe`edbe094b : ffffffff`ffffffff 00000000`1c696620 00000000`00000000 00000000`00000000 : clr!StubLinkerCPU::X86EmitPushReg+0x135
000007fe`edc4dca0 : 00000000`1c696620 00000000`2c50ff20 00000000`00000001 00000000`00000000 : clr!COMArrayInfo::GetReference+0x12b
000007fe`edd0c736 : 00000000`21062e70 00000000`2c50f918 00000000`1c696620 00000000`00000000 : clr!ThreadNative::KickOffThread+0xc0
00000000`76eb59cd : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : clr!Thread::intermediateThreadProc+0x7d
00000000`770eb891 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : kernel32!BaseThreadInitThunk+0xd
00000000`00000000 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : ntdll!RtlUserThreadStart+0x1d

此外,用户使用的是 .NET 4.0,但所有开发人员的计算机都使用 .NET 4.5(但面向 .NET 4.0)。这就解释了为什么 GC 错误只发生在用户身上。

C# .NET WPF NullReferenceException

评论

2赞 James Harcourt 6/3/2015
Commit() 是否包含内部返回 null ref 的代码(并且 s 实际上是 != null)?
4赞 Scott Chamberlain 6/3/2015
你能显示 的代码吗,或者至少是 也许?这里的一切都看起来不错,问题一定在更深层次上。StatePairCaptureStateCommit
5赞 Scott Chamberlain 6/3/2015
这很好,但这对我们没有帮助。您的问题(如发布的那样)没有足够的信息来跟踪问题。
4赞 Peter Duniho 6/3/2015
规则#1:它几乎从来都不是运行时错误。规则#2:如果你认为你看到的行为应该是不可能的,那么要么这种行为实际上并非不可能,要么你没有看到你认为你所看到的。假设您发布的代码实际上是正在执行的代码,那么 就不可能是 ,因此是出于其他原因。由于缺乏一个好的、最小的完整的代码示例来可靠地重现问题,Stack Overflow 无法解释这个问题。snullNullReferenceException
3赞 John Saunders 6/10/2015
未经过适当测试以在多线程环境中运行的本机代码的线程问题绝对会导致访问冲突。

答: 暂无答案