一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第17天,点击查看活动详情。
堆空间是分配对象存储的唯一选择嘛(逃逸分析)
答:现有虚拟机是对象只有分配在堆上的。只是逃逸分析这个技术可以做到将对象分配到栈上
如果一个实例对象通过逃逸分析判断是只有这个方法使用的。那么这个实例对象就可以将内存分配到栈上。
逃逸分析条件:一个对象在方法中定义之后,只在方法内部使用,则认为没有发生逃逸
public void aaa(){
A a=new A();
...
...
a=null;
}
复制代码
在Java8及其以后,默认开启逃逸分析。在编译过程中JIT编译器根据分析结果,将对应的对象优化成栈上分配
/** * 逃逸分析 * * 如何快速的判断是否发生了逃逸分析,大家就看new的对象实体是否有可能在方法外被调用。 */ public class EscapeAnalysis { public EscapeAnalysis obj; /* 方法返回EscapeAnalysis对象,发生逃逸 */ public EscapeAnalysis getInstance(){ return obj == null? new EscapeAnalysis() : obj; } /* 为成员属性赋值,发生逃逸 */ public void setObj(){ this.obj = new EscapeAnalysis(); } //思考:如果当前的obj引用声明为static的? 仍然会发生逃逸。 /* 对象的作用域仅在当前方法中有效,没有发生逃逸 */ public void useEscapeAnalysis(){ EscapeAnalysis e = new EscapeAnalysis(); } /* 引用成员变量的值,发生逃逸 */ public void useEscapeAnalysis1(){ EscapeAnalysis e = getInstance(); //这个e对象,本身就是从外面的方法逃逸进来的 //getInstance().xxx()同样会发生逃逸 } } 复制代码
逃逸分析的代码优化
-
栈上分配,就是没有发生逃逸的对象,在栈上进行内存的分配,这样在方法结束的时候出栈就释放了这个对象的内存空间
-
同步省略,逃逸分析的对象实例进行了栈上分配,那么这个对象就是线程私有的。那么如果原本对这个线程进行了加锁操作就是多余的。所以这个时候就不用进行线程数据同步了。
JIT编译器分析该锁对象只有在该线程有效,不会影响到其他线程的数据。就不需要这个锁了,就进行了锁消除
public void f() { Object hellis = new Object(); synchronized(hellis) { System.out.println(hellis); } } 这个锁本身针对的这个hellis就是一个会被逃逸分析认定为栈上分配的。所以这个线程的对象就不会出现线程共享的问题。所以这个锁就没有必要 复制代码
-
分离对象/标量替换
什么是标量:无法分解的数据,比如对象就是一个聚合量,但是一个int类型属性就是一个标量
class Point { private int x; private int y; } private static void alloc() { Point point = new Point(1,2); System.out.println("point.x" + point.x + ";point.y" + point.y); } 复制代码
如果逃逸分析一个对象不会逃逸,那么就将这个对象(聚合量)分解成所有的标量。标量(基础数据类型)肯定是分配在栈上的。所以就不需要分配堆内存