是否有CLR编译器/ JIT执行的转义分析?例如,在Java中,似乎循环变量是在循环中分配的不会转义循环的对象在堆栈而不是堆上分配(请参阅Java中的Escape分析).
为了澄清,在下面的示例中,编译器会优化掉堆分配,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());
}
Run Code Online (Sandbox Code Playgroud) 我正在使用Java 7中的转义分析进行一些测试,以便更好地了解哪些对象有资格进行堆栈分配.
这是我编写的用于测试堆栈分配的代码:
import java.util.ArrayList;
import java.util.Iterator;
public class EscapeAnalysis {
private static final long TIME_TO_TEST = 10L * 1000L; // 10s
static class Timestamp {
private long millis;
public Timestamp(long millis) {
this.millis = millis;
}
public long getTime() {
return millis;
}
public void setTime(long time) {
millis = time;
}
}
public static void main(String[] args) {
long r = 0;
System.out.println("test1");
r += test1();
System.out.println("test2");
r += test2();
System.out.println("test3");
r += test3();
System.out.println("test4");
r …
Run Code Online (Sandbox Code Playgroud) 我目前正在研究Go中的一些性能敏感代码.有一次,我有一个特别严密的内环,连续做三件事:
获取数据的几个指针.如果发生罕见错误,可能会有一个或多个指针nil
.
检查是否发生了此错误,如果有错误则记录错误.
处理存储在指针中的数据.
下面显示的是一个具有相同结构的玩具程序(虽然指针实际上永远不会是零).
package main
import (
"math/rand"
"fmt"
)
const BigScaryNumber = 1<<25
func DoWork() {
sum := 0
for i := 0; i < BigScaryNumber; i++ {
// Generate pointers.
n1, n2 := rand.Intn(20), rand.Intn(20)
ptr1, ptr2 := &n1, &n2
// Check if pointers are nil.
if ptr1 == nil || ptr2 == nil {
fmt.Printf("Pointers %v %v contain a nil.\n", ptr1, ptr2)
break
}
// Do work with pointer contents.
sum += *ptr1 …
Run Code Online (Sandbox Code Playgroud) performance memory-management variadic-functions go escape-analysis
func main() {
i1 := 1
A1(&i1)
}
func A1(i1 *int) *int {
return i1
}
Run Code Online (Sandbox Code Playgroud)
逃逸分析的结果为
./main.go:18:9: parameter i1 leaks to \~r1 with derefs=0:
./main.go:18:9: flow: \~r1 = i1:
./main.go:18:9: from return i1 (return) at ./main.go:19:2
./main.go:18:9: leaking param: i1 to result \~r1 level=0
Run Code Online (Sandbox Code Playgroud)
parameter i1 leaks to \~r1 with derefs=0
和是什么意思leaking param: i1 to result \~r1 level=0
首先我尝试 Google ,最相关的结果是escape-analysis-shows-channel-as-leaking-paramgolang escape leaking
的评论
“你为什么那么想?” 可以合理地假设泄漏是严重的并且与其阀杆泄漏有关。我正在努力想出一个示例上下文,其中泄漏是一件好事,例如泄漏的水桶、泄漏的油箱、泄漏的泄漏、泄漏的电容器、泄漏的船、泄漏的抽象。对于高性能 Go 专家来说,这可能是显而易见的,但对于我们其他人来说,链接到文档并提供泄漏参数所指内容的简要说明会很有帮助
我也想问同样的问题,但之后就没有再回复了。
然后我尝试阅读打印这些结果的源代码。
在compile/internal/escape/leaks.go中,我发现了评论
// 泄漏代表来自参数的一组赋值流
// …
以下是我尝试从" 逃避分析 "主题的Java Performance:The Definitive Guide,第97页重现的示例.这可能是应该发生的事情:
getSum()
必须变得足够热并且使用适当的JVM参数必须将其内联到调用者中main()
.list
和sum
变量都没有从main()
方法中转义,因此可以将它们标记为,NoEscape
因此JVM可以使用堆栈分配而不是堆分配.但是我通过jitwatch运行它,结果显示getSum()
编译成本机程序集并且没有内联main()
.更不用说因此堆栈分配也没有发生.
我在这里做错了什么?(我已将整个代码和热点日志放在这里.)
这是代码:
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.stream.IntStream;
public class EscapeAnalysisTest {
private static class Sum {
private BigInteger sum;
private int n;
Sum(int n) {
this.n = n;
}
synchronized final BigInteger getSum() {
if (sum == null) {
sum = BigInteger.ZERO;
for (int i = 0; i < …
Run Code Online (Sandbox Code Playgroud) 我刚刚尝试在jdk6-u18 VM上-XX:+DoEscapeAnalysis
启用该选项(在solaris上)并且有一个相当令人失望的经历.我正在运行一个scala应用程序,它有很多演员(其中20,000个).这是垃圾创建的秘诀!
通常,应用程序可以用堆为256MB运行,但产生巨大的垃圾量.在稳定状态下:
我认为转义分析可能会有所帮助,所以我启用了该选项并重新运行了应用程序.我发现应用程序变得越来越无法清除它收集的垃圾,直到它似乎最终花费了整个时间来做GC并且应用程序在其完全分配时"扁平化".
在这一点上,我应该说该应用程序没有抛出OutOfMemoryError
我所期望的.也许JConsole
(我用来执行分析)没有正确显示带有此选项的GC统计数据(我不相信)?
然后我删除了该选项并重新启动,应用程序再次变为"正常"!任何人都知道可能会发生什么?
基于逃逸分析的优化是Proguard的计划功能.与此同时,是否有像proguard这样的现有工具已经进行了需要转义分析的优化?
关于dalvik逃逸分析的任何想法?或者何时以及是否计划添加?
我认为转义分析是GC语言中一个非常重要的特性,可以避免每次调用方法时产生对象,并且当前我在类中预先分配一个对象并在方法的开头重置它,所以我避免分配(我正在写用于Android的3D游戏...在java)中,但我发现这种方法很丑陋并且使用预分配的对象保持内存不足.
假设我有一个 java.util.Collection 想要循环。通常我会这样做:
for(Thing thing : things) do_something_with(thing);
Run Code Online (Sandbox Code Playgroud)
但是假设这是在一些到处使用的核心实用方法中,并且在大多数地方,集合是空的。那么理想情况下,我们不希望仅仅为了执行无操作循环而对每个调用者强加迭代器分配,并且我们可以重写如下内容:
if(things.isEmpty()) return;
for(Thing thing : things) do_something_with(thing);
Run Code Online (Sandbox Code Playgroud)
如果是列表,则更极端的选择things
是使用 C 样式for
循环。
但是等等,Java 转义分析应该消除这种分配,至少在 C2 编译器处理此方法之后。所以应该不需要这种“纳米优化”。(我什至不会用微优化这个词来形容它;它对于这个来说有点太小了。)除了......
我一直听说逃逸分析是“脆弱的”,但似乎没有人谈论过什么会导致它变得混乱。直觉上,我认为更复杂的控制流将是最值得担心的,这意味着 for-each 循环中的迭代器应该被可靠地消除,因为那里的控制流很简单。
这里的标准响应是尝试进行实验,但除非我知道其中的变量,否则很难相信我可能想从这样的实验中得出的任何结论。
事实上,在这篇博客文章中,有人尝试了这样的实验,三分之二的分析器给出了错误的结果:
http://psy-lob-saw.blogspot.com/2014/12/the-escape-of-arraylistiterator.html
我对晦涩难懂的 JVM 魔法的了解比该博客文章的作者要少得多,而且很可能更容易被误导。
escape-analysis ×10
java ×6
jit ×3
jvm ×3
go ×2
jvm-hotspot ×2
scala ×2
allocation ×1
android ×1
c# ×1
clr ×1
dalvik ×1
java-7 ×1
optimization ×1
performance ×1
proguard ×1
stack ×1