.NET CLR VM 中的转义分析

发布于 2024-12-17 04:08:44 字数 530 浏览 2 评论 0原文

CLR 编译器/JIT 是否执行任何逃逸分析?例如,在Java中,似乎循环变量在循环中分配的不会逃脱循环的对象被分配在堆栈上而不是堆上(请参阅Java 中的转义分析)。

为了澄清,在下面的示例中,编译器是否会优化 foo 的堆分配,因为它永远不会逃脱循环。

class Foo 
{ 
   int number;
   Foo(int number) { this.number = number; }
   public override string ToString() { return number.ToString(); }
}

for (int i = 0; i < 10000000; i++)
{
   Foo foo = new Foo(i);
   Console.WriteLine(foo.ToString());
}

Is there any escape analysis performed by the CLR compiler/JIT? For example, in Java it appears that a loop variable an object allocated in a loop that doesn't escape the loop gets allocated on the stack rather than the heap (see Escape analysis in Java).

To clarify, in the example below, would the compiler optimise away the heap allocation of foo as it never escapes the loop.

class Foo 
{ 
   int number;
   Foo(int number) { this.number = number; }
   public override string ToString() { return number.ToString(); }
}

for (int i = 0; i < 10000000; i++)
{
   Foo foo = new Foo(i);
   Console.WriteLine(foo.ToString());
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

羁拥 2024-12-24 04:08:44

如果您指的是对象new Foo(i);),那么我的理解是不:这永远不会在堆栈上分配;然而,它会在零代中死亡,因此收集起来非常高效。我并不自称了解 CLI 的每一个阴暗角落,但我不知道 C# 中的任何场景会导致在堆栈上分配托管引用类型(例如stackalloc 并不真正重要,并且非常具体)。显然,在 C++ 中您还有更多选择,但它不是托管实例。

有趣的是,在 MonoTouch/AOT 上,它可能会立即被收集,但这不是主要的 CLI VM(并且适用于非常具体的场景)。

至于变量 - 通常位于堆栈上(并在每次循环迭代中重复使用) - 但它可能不会。例如,如果这是一个“迭代器块”,则所有未删除的局部变量实际上都是编译器生成的状态机上的字段。更常见的是,如果变量被“捕获”(进入匿名方法或 lambda 表达式,两者都形成闭包),则该变量将转换为编译器生成的捕获上下文上的字段, 每个循环迭代都是单独的(因为 foo 在循环内声明)。这意味着每个在堆上都是独立的

至于 i (循环变量) - 如果 that 被捕获,它会变得更有趣,

  • 在 C# 1.2 中捕获不存在,但根据规范,循环变量技术上在 C# 2.0 到 4.0 中是每次迭代
  • ,在 C# 5.0 及更高版本中循环变量是共享的(导致臭名昭著的 capture/foreach 常见问题),
  • 循环变量再次是每次迭代

这仅在捕获变量时产生影响,但会改变它在捕获上下文中具体表现的语义

If you mean the object ( new Foo(i); ), then my understanding is that no: this is never allocated on the stack; however, it will die in generation zero, so will be very efficient to collect. I don't profess to know every dark and dank corner of the CLI, but I am not aware of any scenario in C# that would lead to a managed reference-type being allocated on the stack (things like stackalloc don't really count, and are highly specific). Obviously in C++ you have a few more options, but then it isn't a managed instance.

Interestingly, on MonoTouch/AOT it might be collected immediately, but that is not the main CLI VM (and is for a very specific scenario).

As for the variable - that will usually be on the stack (and re-used for each loop iteration) - but it might not be. For example, if this is an "iterator block", then all the non-removed local-variables are actually fields on the compiler-generated state-machine. More commonly, if the variable is "captured" (into an anonymous method or lambda expression, both of which form closures), then the variable is transformed into a field on the compiler-generated capture-context, and is separate per loop iteration (since foo is declared inside the loop). This means that each is separate on the heap.

As for i (the loop variable) - if that gets captured, it gets even more interesting

  • in C# 1.2 captures didn't exist, but by the spec the loop-variable is technically per-iteration
  • in C# 2.0 to 4.0, the loop variable is shared (causing the infamous capture/foreach common question)
  • in C# 5.0 and above the loop variable is per-iteration again

this only makes a difference when the variable is captured, but changes the semantics of exactly how it manifests on the capture-context

残花月 2024-12-24 04:08:44

值类型可能会在堆栈上分配(并非总是如此),但对于引用类型的实例则不然。实际上:

特别是,引用类型实例的存储位置始终被视为长期存在的,即使它们被证明是短暂的。因此它们总是在堆上。

(埃里克·利珀特:关于值类型的真相

还有堆栈是一个实现细节值得一读。

A value type might be allocated on the stack (not always), but the same is not true for instances of reference types. In fact:

In particular, the storage locations of instances of reference types are always treated as though they are long-lived, even if they are provably short-lived. Therefore they always go on the heap.

(Eric Lippert: The Truth About Value Types)

Also The Stack Is An Implementation Detail makes a good reading.

羅雙樹 2024-12-24 04:08:44

虽然 x86 JIT 擅长“内联”值类型,但您的代码片段不符合条件,因为 ToString 方法将是对装箱对象的虚拟调用。 编辑:情况可能并非如此,因为您没有覆盖ToString

然而,根据我的实验,x64 JIT 根本没有这样做。

编辑:

如果可能,请在 x86 和 x64 上测试您的代码。

While the x86 JIT is good at 'inlining' valuetypes, your snippet will not qualify as the ToString method will be a virtual call on a boxed object. Edit: This may not be the case, as you are not overriding ToString.

The x64 JIT however does not do this at all from my experiments.

Edit:

If possible, test your code on both x86 and x64.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文