更新一些训练代码示例时,我注意到一些之前由 CLR 保持未初始化的本地内存现在被归零。
这是一个显示“问题”的CIL小示例:
.assembly Test{}
.assembly extern mscorlib{}
.class S extends [mscorlib]System.ValueType
{
.field public int32 n;
}
.method static void F()
{
.locals (int32, valuetype S)
ldloc 0
call void [mscorlib]System.Console::WriteLine(int32)
ldloca 1
ldfld int32 S::n
call void [mscorlib]System.Console::WriteLine(int32)
ret
}
.method static void Main()
{
.entrypoint
.locals (int32, valuetype S)
ldloc 0
call void [mscorlib]System.Console::WriteLine(int32)
ldloca 1
ldfld int32 S::n
call void [mscorlib]System.Console::WriteLine(int32)
call void F()
ret
}
这是输出:
.Net 2.0(我几乎确定它与 .Net 4.0 相同):
$ /Windows/Microsoft.NET/Framework/v2.0.50727/ilasm.exe Test.il Microsoft (R) .NET Framework IL Assembler. Version 2.0.50727.5420 Copyright (c) Microsoft Corporation. All rights reserved. Assembling 'Test.il' to EXE --> 'Test.exe' Source file is ANSI Assembled global method F Assembled global method Main Creating PE file Emitting classes: Class 1: S Emitting fields and methods: Global Methods: 2; Class 1 Fields: 1; Resolving local member refs: 3 -> 3 defs, 0 refs, 0 unresolved Emitting events and properties: Global Class 1 Resolving local member refs: 0 -> 0 defs, 0 refs, 0 unresolved Writing PE file Operation completed successfully Test.il(6) : warning -- Non-sealed value class, made sealed $ ./Test.exe 211384 0 2157704 2157328
.Net 4.5:
$ /Windows/Microsoft.NET/Framework/v4.0.30319/ilasm.exe Test.il Microsoft (R) .NET Framework IL Assembler. Version 4.0.30319.17929 Copyright (c) Microsoft Corporation. All rights reserved. Assembling 'Test.il' to EXE --> 'Test.exe' Source file is ANSI Assembled global method F Assembled global method Main Creating PE file Emitting classes: Class 1: S Emitting fields and methods: Global Methods: 2; Class 1 Fields: 1; Resolving local member refs: 3 -> 3 defs, 0 refs, 0 unresolved Emitting events and properties: Global Class 1 Resolving local member refs: 0 -> 0 defs, 0 refs, 0 unresolved Writing PE file Operation completed successfully Test.il(6) : warning : Non-sealed value class, made sealed $ ./Test.exe 0 0 0 0
我很好奇此实现更改背后的基本原理。
因此,欢迎任何反馈,尤其是来自内部人士的反馈,或对任何文章的引用,或者更好的是对规范的引用。 :)
提前致谢。
最佳答案
这只是一个巧合,没有任何事情是故意清除你的本地人的。 Jitter 尝试将本地变量保留在寄存器中,如果不需要,它甚至不会分配堆栈空间。你的程序很小,当你不使用/32bitpreferred 标志编译它时,它很可能会为你的本地选择一个寄存器,该寄存器之前在当前线程上没有使用过,并且仍然具有零值。您可以在以下情况下取回垃圾:
- 使用/32bitpreferred 标志编译代码。 32 位代码的寄存器较少,因此您的局部变量可能存储在脏堆栈或脏寄存器中。
- 为您的值类型创建更多字段,或使用更多局部变量,这样它们就不会适合寄存器集中,或者抖动不会选择将它们存储在那里。
- 在你的方法中做更多涉及寄存器的事情
我不知道为什么与以前的版本相比,您需要在 4.5 版本中付出更多努力来查找未初始化的值。也许这是抖动某些变化的副作用,但我只是猜测。关键是您仍然需要本地变量上的 init 标志,以确保它们的初始值为零。
关于.net - 为什么 .Net 4.5 中的内存初始化规则发生了变化?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19081119/