for 循环如何与堆栈溢出相关?
How could a for loop be related to a stack overflow?
我遇到过一段代码,只有在for循环重复一定次数后才会出现栈溢出异常
代码如下:
public class Whosebug
{
public static void Test()
{
List<int> list = new List<int>() {1, 2};
Container container = new Container {List = list};
for (int i = 0; i < 10000; i++) // This matters
{
foreach (var item in container.List)
{
Console.WriteLine(item);
}
}
}
}
public class Container
{
private IEnumerable<int> list;
public IEnumerable<int> List
{
get
{
//return list.OrderBy(x => x); <- This is OK
list = list.OrderBy(x => x); // This is not
return list;
}
set { list = value; }
}
}
当执行方法 Test() 时,you can see a long series of "1" and "2" being printed on screen before the error actually happens.(我认为这意味着 for 循环正在正常推进)
并且如果条件变为"i < 1000"(或更少)则不会发生堆栈溢出异常。
The memory dump showed "List.Orderby" is likely the direct cause of the problem
我知道像这样写一个"get"是不好的做法,但是我不明白是什么导致了这里的堆栈溢出异常,为什么它看起来是累积的而不是递归的死亡陷阱。
这可能是枚举代码中的一个陷阱,或者是编译器造成的,或者只是我的疏忽?
无论如何,寻找解释,感谢您的帮助。
Stacktrace here
在文本中:
000000e86215e460 00007ffe28670f7c (MethodDesc 00007ffe28567738 +0x2c System.Linq.OrderedEnumerable`1[[System.Int32, mscorlib]].GetEnumerator())
000000e86215e4a0 00007ffe849ce396 (MethodDesc 00007ffe844a4ce8 +0x66 System.Linq.Buffer`1[[System.Int32, mscorlib]]..ctor(System.Collections.Generic.IEnumerable`1<Int32>))
000000e86215e4c0 00007ffe86bad279 (MethodDesc 00007ffe866d7630 +0x19 System.StubHelpers.StubHelpers.SafeHandleRelease(System.Runtime.InteropServices.SafeHandle))
000000e86215e510 00007ffe28670fe7 (MethodDesc 00007ffe28567e80 +0x47 System.Linq.OrderedEnumerable`1+<GetEnumerator>d__1[[System.Int32, mscorlib]].MoveNext())
000000e86215e520 00007ffe28670f7c (MethodDesc 00007ffe28567738 +0x2c System.Linq.OrderedEnumerable`1[[System.Int32, mscorlib]].GetEnumerator())
000000e86215e560 00007ffe849ce396 (MethodDesc 00007ffe844a4ce8 +0x66 System.Linq.Buffer`1[[System.Int32, mscorlib]]..ctor(System.Collections.Generic.IEnumerable`1<Int32>))
000000e86215e580 00007ffe86c5b552 (MethodDesc 00007ffe867e3f60 +0xf2 DomainNeutralILStubClass.IL_STUB_PInvoke(Microsoft.Win32.SafeHandles.SafeFileHandle, Byte*, Int32, Int32 ByRef, IntPtr))
000000e86215e5d0 00007ffe28670fe7 (MethodDesc 00007ffe28567e80 +0x47 System.Linq.OrderedEnumerable`1+<GetEnumerator>d__1[[System.Int32, mscorlib]].MoveNext())
000000e86215e5e0 00007ffe28670f7c (MethodDesc 00007ffe28567738 +0x2c System.Linq.OrderedEnumerable`1[[System.Int32, mscorlib]].GetEnumerator())
000000e86215e5e8 00007ffe86c5b526 (MethodDesc 00007ffe867e3f60 +0xc6 DomainNeutralILStubClass.IL_STUB_PInvoke(Microsoft.Win32.SafeHandles.SafeFileHandle, Byte*, Int32, Int32 ByRef, IntPtr))
000000e86215e620 00007ffe849ce396 (MethodDesc 00007ffe844a4ce8 +0x66 System.Linq.Buffer`1[[System.Int32, mscorlib]]..ctor(System.Collections.Generic.IEnumerable`1<Int32>))
000000e86215e690 00007ffe28670fe7 (MethodDesc 00007ffe28567e80 +0x47 System.Linq.OrderedEnumerable`1+<GetEnumerator>d__1[[System.Int32, mscorlib]].MoveNext())
000000e86215e6a0 00007ffe28670f7c (MethodDesc 00007ffe28567738 +0x2c System.Linq.OrderedEnumerable`1[[System.Int32, mscorlib]].GetEnumerator())
000000e86215e6e0 00007ffe849ce396 (MethodDesc 00007ffe844a4ce8 +0x66 System.Linq.Buffer`1[[System.Int32, mscorlib]]..ctor(System.Collections.Generic.IEnumerable`1<Int32>))
000000e86215e710 00007ffe86b9b46a (MethodDesc 00007ffe86950f90 +0x8a System.IO.StreamWriter.Flush(Boolean, Boolean))
000000e86215e750 00007ffe28670fe7 (MethodDesc 00007ffe28567e80 +0x47 System.Linq.OrderedEnumerable`1+<GetEnumerator>d__1[[System.Int32, mscorlib]].MoveNext())
000000e86215e760 00007ffe28670f7c (MethodDesc 00007ffe28567738 +0x2c System.Linq.OrderedEnumerable`1[[System.Int32, mscorlib]].GetEnumerator())
000000e86215e7a0 00007ffe849ce396 (MethodDesc 00007ffe844a4ce8 +0x66 System.Linq.Buffer`1[[System.Int32, mscorlib]]..ctor(System.Collections.Generic.IEnumerable`1<Int32>))
000000e86215e7d0 00007ffe28670da5 (MethodDesc 00007ffe28565c68 +0xe5 ConsoleTest.Container.get_List())
000000e86215e810 00007ffe28670fe7 (MethodDesc 00007ffe28567e80 +0x47 System.Linq.OrderedEnumerable`1+<GetEnumerator>d__1[[System.Int32, mscorlib]].MoveNext())
000000e86215e820 00007ffe28670f7c (MethodDesc 00007ffe28567738 +0x2c System.Linq.OrderedEnumerable`1[[System.Int32, mscorlib]].GetEnumerator())
000000e86215e860 00007ffe2867065f (MethodDesc 00007ffe28565b98 +0x11f ConsoleTest.Whosebug.Test())
000000e86215e900 00007ffe286704ba (MethodDesc 00007ffe28565ac0 +0x3a ConsoleTest.Program.Main(System.String[]))
问题是您的 get
属性 每次都用 [=14= 定义的新对象替换 list
变量].
请注意 OrderBy(x => x)
还没有进行任何排序,它创建了一个对象来定义如何 return 项目,它只会被执行 if/when 你用例如迭代它foreach
.
我将尝试展示这样做的后果:
- 在第一个运行之前,
list
等于List<int>() {1, 2}
- 之后运行一次,
list
等于(List<int>() {1, 2}).OrderBy(x => x)
- 之后运行两次,
list
等于(List<int>() {1, 2}).OrderBy(x => x).OrderBy(x => x)
- 在它 运行 10000 次之后...我什至不会尝试显示
list
包含的内容。
枚举 10000-fold-nested 个对象导致 10000 个 Enumerator 对象旋转起来,每个对象执行前一个对象的有序检索,直到遇到原始源 List<int>
。显然在到达源列表之前 运行 超出了 space。
解决方法是不要每次都 re-assign 列出,或者(如果出于某种原因您觉得必须这样做)然后使用 ToList():[=25= 使其成为 "do" OrderBy ]
get
{
list = list.OrderBy(x => x).ToList(); // creates a new list and does not keep to OrderBy() object
return list;
}
每次访问 属性 时,仍然会导致冗余 re-ordering 和 re-assignment。如果你总是需要排序,那么将逻辑放在 setter 中会更有效率,所以它只会发生一次:
get { return list; }
set { list = value.OrderBy(x => x).ToList(); }
我遇到过一段代码,只有在for循环重复一定次数后才会出现栈溢出异常
代码如下:
public class Whosebug
{
public static void Test()
{
List<int> list = new List<int>() {1, 2};
Container container = new Container {List = list};
for (int i = 0; i < 10000; i++) // This matters
{
foreach (var item in container.List)
{
Console.WriteLine(item);
}
}
}
}
public class Container
{
private IEnumerable<int> list;
public IEnumerable<int> List
{
get
{
//return list.OrderBy(x => x); <- This is OK
list = list.OrderBy(x => x); // This is not
return list;
}
set { list = value; }
}
}
当执行方法 Test() 时,you can see a long series of "1" and "2" being printed on screen before the error actually happens.(我认为这意味着 for 循环正在正常推进)
并且如果条件变为"i < 1000"(或更少)则不会发生堆栈溢出异常。
The memory dump showed "List.Orderby" is likely the direct cause of the problem
我知道像这样写一个"get"是不好的做法,但是我不明白是什么导致了这里的堆栈溢出异常,为什么它看起来是累积的而不是递归的死亡陷阱。
这可能是枚举代码中的一个陷阱,或者是编译器造成的,或者只是我的疏忽? 无论如何,寻找解释,感谢您的帮助。
Stacktrace here
在文本中:
000000e86215e460 00007ffe28670f7c (MethodDesc 00007ffe28567738 +0x2c System.Linq.OrderedEnumerable`1[[System.Int32, mscorlib]].GetEnumerator())
000000e86215e4a0 00007ffe849ce396 (MethodDesc 00007ffe844a4ce8 +0x66 System.Linq.Buffer`1[[System.Int32, mscorlib]]..ctor(System.Collections.Generic.IEnumerable`1<Int32>))
000000e86215e4c0 00007ffe86bad279 (MethodDesc 00007ffe866d7630 +0x19 System.StubHelpers.StubHelpers.SafeHandleRelease(System.Runtime.InteropServices.SafeHandle))
000000e86215e510 00007ffe28670fe7 (MethodDesc 00007ffe28567e80 +0x47 System.Linq.OrderedEnumerable`1+<GetEnumerator>d__1[[System.Int32, mscorlib]].MoveNext())
000000e86215e520 00007ffe28670f7c (MethodDesc 00007ffe28567738 +0x2c System.Linq.OrderedEnumerable`1[[System.Int32, mscorlib]].GetEnumerator())
000000e86215e560 00007ffe849ce396 (MethodDesc 00007ffe844a4ce8 +0x66 System.Linq.Buffer`1[[System.Int32, mscorlib]]..ctor(System.Collections.Generic.IEnumerable`1<Int32>))
000000e86215e580 00007ffe86c5b552 (MethodDesc 00007ffe867e3f60 +0xf2 DomainNeutralILStubClass.IL_STUB_PInvoke(Microsoft.Win32.SafeHandles.SafeFileHandle, Byte*, Int32, Int32 ByRef, IntPtr))
000000e86215e5d0 00007ffe28670fe7 (MethodDesc 00007ffe28567e80 +0x47 System.Linq.OrderedEnumerable`1+<GetEnumerator>d__1[[System.Int32, mscorlib]].MoveNext())
000000e86215e5e0 00007ffe28670f7c (MethodDesc 00007ffe28567738 +0x2c System.Linq.OrderedEnumerable`1[[System.Int32, mscorlib]].GetEnumerator())
000000e86215e5e8 00007ffe86c5b526 (MethodDesc 00007ffe867e3f60 +0xc6 DomainNeutralILStubClass.IL_STUB_PInvoke(Microsoft.Win32.SafeHandles.SafeFileHandle, Byte*, Int32, Int32 ByRef, IntPtr))
000000e86215e620 00007ffe849ce396 (MethodDesc 00007ffe844a4ce8 +0x66 System.Linq.Buffer`1[[System.Int32, mscorlib]]..ctor(System.Collections.Generic.IEnumerable`1<Int32>))
000000e86215e690 00007ffe28670fe7 (MethodDesc 00007ffe28567e80 +0x47 System.Linq.OrderedEnumerable`1+<GetEnumerator>d__1[[System.Int32, mscorlib]].MoveNext())
000000e86215e6a0 00007ffe28670f7c (MethodDesc 00007ffe28567738 +0x2c System.Linq.OrderedEnumerable`1[[System.Int32, mscorlib]].GetEnumerator())
000000e86215e6e0 00007ffe849ce396 (MethodDesc 00007ffe844a4ce8 +0x66 System.Linq.Buffer`1[[System.Int32, mscorlib]]..ctor(System.Collections.Generic.IEnumerable`1<Int32>))
000000e86215e710 00007ffe86b9b46a (MethodDesc 00007ffe86950f90 +0x8a System.IO.StreamWriter.Flush(Boolean, Boolean))
000000e86215e750 00007ffe28670fe7 (MethodDesc 00007ffe28567e80 +0x47 System.Linq.OrderedEnumerable`1+<GetEnumerator>d__1[[System.Int32, mscorlib]].MoveNext())
000000e86215e760 00007ffe28670f7c (MethodDesc 00007ffe28567738 +0x2c System.Linq.OrderedEnumerable`1[[System.Int32, mscorlib]].GetEnumerator())
000000e86215e7a0 00007ffe849ce396 (MethodDesc 00007ffe844a4ce8 +0x66 System.Linq.Buffer`1[[System.Int32, mscorlib]]..ctor(System.Collections.Generic.IEnumerable`1<Int32>))
000000e86215e7d0 00007ffe28670da5 (MethodDesc 00007ffe28565c68 +0xe5 ConsoleTest.Container.get_List())
000000e86215e810 00007ffe28670fe7 (MethodDesc 00007ffe28567e80 +0x47 System.Linq.OrderedEnumerable`1+<GetEnumerator>d__1[[System.Int32, mscorlib]].MoveNext())
000000e86215e820 00007ffe28670f7c (MethodDesc 00007ffe28567738 +0x2c System.Linq.OrderedEnumerable`1[[System.Int32, mscorlib]].GetEnumerator())
000000e86215e860 00007ffe2867065f (MethodDesc 00007ffe28565b98 +0x11f ConsoleTest.Whosebug.Test())
000000e86215e900 00007ffe286704ba (MethodDesc 00007ffe28565ac0 +0x3a ConsoleTest.Program.Main(System.String[]))
问题是您的 get
属性 每次都用 [=14= 定义的新对象替换 list
变量].
请注意 OrderBy(x => x)
还没有进行任何排序,它创建了一个对象来定义如何 return 项目,它只会被执行 if/when 你用例如迭代它foreach
.
我将尝试展示这样做的后果:
- 在第一个运行之前,
list
等于List<int>() {1, 2}
- 之后运行一次,
list
等于(List<int>() {1, 2}).OrderBy(x => x)
- 之后运行两次,
list
等于(List<int>() {1, 2}).OrderBy(x => x).OrderBy(x => x)
- 在它 运行 10000 次之后...我什至不会尝试显示
list
包含的内容。
枚举 10000-fold-nested 个对象导致 10000 个 Enumerator 对象旋转起来,每个对象执行前一个对象的有序检索,直到遇到原始源 List<int>
。显然在到达源列表之前 运行 超出了 space。
解决方法是不要每次都 re-assign 列出,或者(如果出于某种原因您觉得必须这样做)然后使用 ToList():[=25= 使其成为 "do" OrderBy ]
get
{
list = list.OrderBy(x => x).ToList(); // creates a new list and does not keep to OrderBy() object
return list;
}
每次访问 属性 时,仍然会导致冗余 re-ordering 和 re-assignment。如果你总是需要排序,那么将逻辑放在 setter 中会更有效率,所以它只会发生一次:
get { return list; }
set { list = value.OrderBy(x => x).ToList(); }