对象逃逸
概念:
如果某一个方法内部声明了一个局部变量,该变量只在方法内部使用,没有被方法返回或者作为参数传到方法之外的地方,那么这个变量就只在方法背部使用,则该对象是没有发生对象逃逸。反之变量被作为参数传出去或者作为返回值使用,这个对象就称之为方法逃逸,或者赋值给类变量等能被其他线程访问到的变量,称之为线程逃逸。
会发生对象逃逸的代码示例:
下面的代码userInfo对象在保存之后会返回,外面调用的地方会用到这个返回值,那么这个对象就发生了逃逸
public UserInfo saveUserInfo(String userName){
UserInfo userInfo = new UserInfo();
userInfo.setUserCode(userName);
userInfo.setPassword(PasswordUtil.random(6))
//其他DB操作等...
return userInfo;
}
下面的代码也发生了对象逃逸,因为对象作为参数传到其他方法中,会被其他方法调用
public void saveUserInfo(String userName){
UserInfo userInfo = new UserInfo();
userInfo.setUserCode(userName);
userInfo.setPassword(PasswordUtil.random(6))
//其他DB操作等...
syncUserInfo(userInfo);
return userInfo;
}
private void syncUserInfo(UserInfo userInfo){
//同步逻辑...
}
逃逸分析
逃逸分析解释
如果能证明对象没有发生逃逸行为,那么Java虚拟机是可以对该对象内存分配进行优化。具体如何优化呢,我们都知道Java对象主要是存储在堆内存中,堆内存也是所有线程共享的变量池,堆内存的资源是很宝贵的;同时虚拟机进行垃圾回收(YoungGC和FullGC)都会先计算整理,找出哪些对象能被回收的,这是一个耗时耗服务器计算资源的过程,如果一个对象能确定不会发生逃逸行为,那么虚拟机就可以将该对象分配到方法栈内存上面,这个对象就会随着方法栈帧出栈的时候一起销毁,不需要垃圾回收器去计算和回收了。在很多方法里面,这类没有发生逃逸的对象还是有很多的,如果这些对象在栈内存分配能充分利用起来,将会大大缓解垃圾收集器的压力。虚拟机在将字节码转换成机器码的时候,会对这个对象是否发生逃逸进行分析的过程称之为逃逸分析。
开启对象逃逸分析
-XX:+DoEscapeAnalysis,使用这个启动参数就可以开启虚拟机逃逸分析
-XX:+PrintEscapeAnalysis, 使用这个启动参数可以查看逃逸分析的结果
其他注意事项
我们知道栈内存分配的空间通常是很小的,所以如果大量的没有逃逸的对象被分配到栈内存中,会不会发生栈内存溢出?
回答是不会的,首先虚拟机在逃逸分析的时候会判断栈内存是否够用,如果不够用,那没办法只能继续放堆内存里面了,我无能为力。
如果我希望能充分利用栈内存,那么这里就引出一个虚拟机参数 -Xss,我们需要将他调大,来保证有更多的空间来分配逃逸对象