一、JVM的运行模式有三种:
- 运行模式
- 解释模式(Interpreted Mode):只使用解释器(-Xint 强制JVM使用解释模式),执行一行JVM字节码就编译一行为机器码
- 编译模式(Compiled Mode):只使用编译器(-Xcomp JVM使用编译模式),先将所有JVM字节码一次编译为机器码,然后一次性执行所有机器码
- 混合模式(Mixed Mode):依然使用解释模式执行代码,但是对于一些 “热点” 代码采用编译模式执行,JVM一般采用混合模式执行代码
- 三种模式区别
- 解释模式启动快,对于只需要执行部分代码,并且大多数代码只会执行一次的情况比较适合;
- 编译模式启动慢,但是后期执行速度快,而且比较占用内存,因为机器码的数量至少是JVM字节码的十倍以上,这种模式适合代码可能会被反复执行的场景;
- 混合模式是JVM默认采用的执行代码方式,一开始还是解释执行,但是对于少部分 “热点 ”代码会采用编译模式执行,这些热点代码对应的机器码会被缓存起来,下次再执行无需再编译,这就是我们常见的JIT( Just In Time Compiler )即时编译技术。
在即时编译过程中JVM可能会对我们的代码最一些优化,比如对象逃逸分析等
二、何为逃逸分析
逃逸分析(Escape Analysis)简单来讲就是,Java Hotspot 虚拟机可以分析新创建对象的使用范围,并决定是否在 Java 堆上分配内存的一项技术。
- 逃逸分析的 JVM 参数如下:
- 开启逃逸分析:-XX:+DoEscapeAnalysis
- 关闭逃逸分析:-XX:-DoEscapeAnalysis
- 显示分析结果:-XX:+PrintEscapeAnalysis
JDK7之后默认开启逃逸分析,可以不用额外加这个参数。
对象逃逸分析:就是分析对象动态作用域,当一个对象在方法中被定义后,它可能被外部方法所引用,例如作为调用参数传递到其他地方中。
public Person test1() {
// 栈帧局部变量表中,但是会被外部引用,person将会在堆内存中开辟一块内存区域,栈的引用指向这块堆内存地址
Person person = new Person();
person.setName("hongjiawei");
person.setAge(18);
return person;
}
public void test2() {
// 栈帧局部变量表中,不会被外部引用到,person对象内存分配会被分配到栈上,new Person对象而不会在堆内存中
Person person = new Person();
person.setName("hongjiawei");
person.setAge(18);
}
很显然test1方法中的person对象被返回了,这个对象的作用域范围不确定
test2方法中的person对象我们可以确定当方法结束,这个对象就可以认为是无效对象了,对于这样的对象我们其实可以将其分配的栈内存里,让其在方法结束时跟随栈内存一起被回收掉。
JVM对于这种情况可以通过开启逃逸分析参数(-XX:+DoEscapeAnalysis)来优化对象内存分配位置,,如果要关闭使用参数(-XX:-DoEscapeAnalysis)
三、思考问题
【问】new Object() 一定是在堆上吗?
答:不一定,对象不一定都是在堆上分配内存的。JDK7之后默认开启逃逸分析,对象有可能会分配到了 栈内存。