.NET JIT编译器会优化掉方法调用吗?

5
从一个 ASP.NET 应用程序返回的堆栈跟踪,生成自 ArgumentNullException,让人觉得错误发生在下面代码的最后一行。就我所看到的,这是不可能的,但如果 JIT 优化了对 Bar 的调用(这将导致不同的堆栈跟踪),那么它就可以解释一切了。我确定不是 c# 编译器的问题,因为 CIL 看起来像我期望的那样。JIT 编译器是否可能删除了对 Bar 的调用?
c# 4, .NET 4.0.30319.1, ASP.NET 4.0.30319.1
编辑: 我应该提到这是发布配置,启用了优化代码 = on,调试信息 = pdb-only。
Stack Trace:

[ArgumentNullException: Value cannot be null. Parameter name: value]
CreateHiddenField(HtmlTextWriter tr, String name, String value) in Foo.cs:129
Foo(IHttpContext context, HtmlTextWriter writer) in Foo.cs:106

private static void Foo(IHttpContext context, HtmlTextWriter writer)
{ // line 103
  Bar(writer, AuthorizationServerResponseDetailsHttpRequestParser.RequestSAMLFieldName, context);
  Bar(writer, AuthorizationServerResponseDetailsHttpRequestParser.RequestTargetFieldName, context);
  // line 106 - blank line in source code.
  CreateHiddenField(tr, name, string.Empty); // looks like its here
}

private static void Bar(HtmlTextWriter tr,string name, IHttpContext context)
{ // line 116
   #region Sanitation
   if (tr == null) { throw new System.ArgumentNullException("tr"); }
   if (name == null) { throw new System.ArgumentNullException("name"); }
   if (context == null) { throw new System.ArgumentNullException("context"); }
   #endregion

   CreateHiddenField(tr, name, context.RequestQueryString(name));
}

private static void CreateHiddenField(HtmlTextWriter tr, string name, string value)
{ // line 127
   #region Sanitation
   if (tr == null) { throw new System.ArgumentNullException("tr"); }
   if (name == null) { throw new System.ArgumentNullException("name"); }
   if (value == null) { throw new System.ArgumentNullException("value"); }
   #endregion

   // payload...
}

1
你能提供一下堆栈跟踪吗?如果符合要求,JIT 可以内联一个方法,这可能会导致它从堆栈跟踪中“消失”... 我想是这样。 - Joshua
它在第一个代码块中。我删除了命名空间/文件名以使其易读。 - jasper
1
你知道吗,我本以为那不是一个堆栈跟踪,因为它非常小。我猜它被内联了,但我不知道编译器是否会发出代码来保留“正确”的堆栈跟踪。堆栈跟踪是否带有任何行号或字节偏移量? - Joshua
2
授权服务器响应详细信息 HTTP 请求解析器... 很棒 :) - Gene
@Joshua更新了堆栈跟踪中的行号和代码。 - jasper
@Gene,这还不到一半呢 ;) - jasper
2个回答

4
根据http://www.hanselman.com/blog/ReleaseISNOTDebug64bitOptimizationsAndCMethodInliningInReleaseBuildCallStacks.aspx的说法,如果JITter内联方法调用,它确实会在堆栈跟踪中被折叠。如果您不想进行内联(这可能不是一个好主意),您可以使用
[MethodImpl(MethodImplOptions.NoInlining)] 

关于方法。对于一个exe,您也可以使用INI文件告诉JITter生成跟踪信息(在链接页面底部),但我不确定如何在ASP.NET应用程序中使用。


这正是我一直在寻找的,但我找不到它。 - Joshua
完美。URL确认了我怀疑的事情,但当我搜索时,我找不到类似那篇文章的内容。 - jasper

2
我从未看到JIT删除一个方法调用,只是将其内联。通常,它会删除未使用的局部变量,但我相当确定JIT无法静态分析调用方法的所有副作用,因此我认为它永远不会删除方法调用。
我猜测在CreateHiddenField内部调用了一个方法并传递了空值,该方法也有一个名为value的参数。由于默认情况下在Release模式下启用了内联,因此您无法真正信任堆栈跟踪(或异常发生的所谓行号)。问题可能出现在CreateHiddenField内部或更深入的不可见堆栈中。 :)
您可以禁用优化以更好地了解堆栈跟踪。MSDN 在这里提供了一些说明。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接