清单 2 显示了一个可以用 escape 分析把堆分配优化掉的示例。Component.getLocation() 方法对 组件的位置做了一个保护性的拷贝,这样调用者就无法在不经意间改变组件的实际位置。先调用 getDistanceFrom() 得到另一个组件的位置,其中包括对象的分配,然后用 getLocation() 返回的 Point 的 x 和 y 字段计算两个组件之间的距离。
清单 2. 返回复合值的典型的保护性拷贝方式
public class Point {
private int x, y;
public Point(int x, int y) {
this.x = x; this.y = y;
}
public Point(Point p) { this(p.x, p.y); }
public int getX() { return x; }
public int getY() { return y; }
}
public class Component {
private Point location;
public Point getLocation() { return new Point(location); }
public double getDistanceFrom(Component other) {
int deltaX = otherLocation.getX() - location.getX();
int deltaY = otherLocation.getY() - location.getY();
return Math.sqrt(deltaX*deltaX deltaY*deltaY);
}
}
getLocation() 方法不知道它的调用者要如何处理它返回的 Point;有可能得到一个指向 Point 的引用,比如把它放在集合中,所以 getLocation() 采用了保护性的编码方式。但是,在这个示例中,getDistanceFrom() 并不会这么做,它只会使用 Point 很短的时间,然后释放它,这看起来像是对完美对象的浪费。
聪明的 JVM 会看出将要进行的工作,并把保护性拷贝的分配优化掉。首先,对 getLocation() 的调用会变成内联的,对 getX() 和 getY() 的调用也同样处理,从而导致 getDistanceFrom() 的表现会像清单 3 一样有效。
清单 3. 伪代码描述了把内联优化应用到 getDistanceFrom() 的结果
public double getDistanceFrom(Component other) {
Point otherLocation = new Point(other.x, other.y);
int deltaX = otherLocation.x - location.x;
int deltaY = otherLocation.y - location.y;
return Math.sqrt(deltaX*deltaX deltaY*deltaY);
}
在这一点上,escape 分析可以显示在第一行分配的对象永远不会脱离它的基本块,而 getDistanceFrom() 也永远不会修改 other 组件的状态。(escape 指的是对象引用没有保存到堆中,或者传递给可能保留一份拷贝的未知代