有一个对象继承自IEnumerable。MoveNext在此类的方法中,它出现StackOverflowException在带有 的行上Regex。正则表达式本身在文件中查找文本匹配项。值得注意的是,一切都适用于带有 .Net 4.6 的 Windows 10 x64,错误发生在带有 .Net 4.0 的 Windows 7 x64 上。
我有 2 个猜测为什么会这样:
Regex第一:可能是由于.Net 4.0 和 .Net 4.6中方法的实现不同Regex,它在堆栈上占用更多内存,因此出现异常。第二:可能Win10和Win7的栈大小不一样。
如何查看当前堆栈大小?如何检查可用堆栈大小?还有什么可能是这个错误的原因?
搜索中的文件数IEnumerable为 2760( _objEnumerator.Count),每个文件都预先加载并作为字符串存储在这个非常IEnumerable. 下面是一个示例代码:
private class MyEnumerator : IEnumerable
{
public bool MoveNext()
{
if (_objEnumerator == null)
{
_objEnumerator = _objects.GetEnumerator();
}
Match m;
if (_current == null)
{
if (!_objEnumerator.MoveNext())
return false;
m = _regex.Match((_objEnumerator.Current).Text); // (_objEnumerator.Current).Text хранит текст файла, ошибка падает в этой строчке
}
if (m.Success)
{
// код выдающий результат
return true;
}
else
{
_current = null;
return MoveNext();
}
}
}
这个问题,我想,正是因为递归(深度2700,这个很多)。在 .NET 中,似乎从 4.5 开始,他们切换到一个新的 JIT 编译器,该编译器可以检测尾递归,并进行调用
tailcall而不是正常的递归调用。结果,堆叠没有被堵塞。由于语言不能保证尾递归优化的存在(并且到目前为止似乎不适用于 32 位目标),递归实现是代码中的错误。迭代地重写你的函数。
澄清:我尝试了与您的代码类似的代码,但它不会在 VS 2015/.NET 4.5/x64/Release 的 IL 代码中生成尾调用。所以这可能不是你的问题。我会尝试进一步调查原因。
进一步调查:在当前版本的 .NET (4.5) 中,尾递归不需要 IL 前缀
tail。示例:这是代码生成以下 IL,不带前缀
.tail:但是,本机代码
f如下所示:这意味着可以在 JIT 级别应用尾递归,而无需 IL 编译器的参与。因此,.NET 4.5 具有新的 JIT 编译器这一事实可能会有所帮助。
无论如何,问题的正确解决方案是以迭代的方式重写代码。