“今天在读《Java工程师修炼之道》在JVM内存模型中讲到堆这一节提到了逃逸分析和TLAB,今天就和大家一起分享一下。”
01
—
new的对象在哪里?
有可能你会回答在堆,或者在堆的Edon区或者老年代。但是new的对象还可能会出现在栈或者TLAB区域中。纳尼?还会在栈?开什么国际玩笑,小编跟你讲这是真的,JVM内部的优化机制决定它的内存分配不会是单一的。 为什么不全部在堆里面呢?试想一下,堆为线程共享区域,为了保证内存分配的正确性,需要采用同步机制,但同时损失了效率与性能,如果对象在栈中存放就能避免线程同步带来的效率问题,但栈的空间限制也不能容纳那么多的对象。因此我们看一下虚拟机内部在new一个对象的时候是如何为对象选择一个空间。 首先在栈中尝试分配,然后在TLAB中分配,大对象直接放到老年代,最后才进入Eden区。
02
—
什么样的对象在栈上分配呢?
未逃逸的对象优先考虑在栈上分配。什么是逃逸呢?
一个对象的指针被多个方法或线程引用时,我们称这个对象的指针逃逸,所谓的逃逸分析就是找出这些逃逸或者未逃逸的对象为后面的优化做准备。看下面这个例子,stu从方法create逃逸到类的静态变量,有可能会被其他方法或者线程引用而修改其状态。
public class Escape{ public static Student stu; public static void create(){ stu = new Student(); } } 一般发生逃逸的方式有两种:
方法逃逸:当一个对象在方法中定义之后,作为参数传递到其它方法中
线程逃逸:如类变量或实例变量,可能被其它线程访问到
一般对于未逃逸的对象,也就是这些对象是线程安全的,JVM有优化措施,当然除了在栈上分配该对象,还有两种方式,下面我们看一下对未逃逸的对象的三种优化方式: 同步清除,同步是有性能消耗的,既然未逃逸,那么该对象为线程私有,因此可以消除同步操作,提升性能。
标量替换:1、标量是指不可分割的量,如java中基本数据类型和reference类型,相对的一个数据可以继续分解,称为聚合量;2、如果把一个对象拆散,将其成员变量恢复到基本类型来访问就叫做标量替换;3、如果逃逸分析发现一个对象不会被外部访问,并且该对象可以被拆散,那么经过优化之后,并不直接生成该对象,而是在栈上创建若干个成员变量;
栈上分配:就是在栈上分配对象,但实际上HotSpot并未真真实现该对象的栈上分配,而是在栈上对该对象做了标量替换。
03
—
如何启用逃逸分析?
Java启动增加参数-XX:+DoEscapeAnalysis,你有可能会发现相对于关闭逃逸分析,可能减少GC次数,也可能存在由于标量替换出现相对较少的对象创建。
04
—
TLAB是什么?
TLAB是在新生代中开辟了一小块线程私有的区域,该区域默认占据了Eden区的1%,TLAB全称Thread Local Allocation Buffer。由于TLAB是线程私有的,也就是每个线程都有自己的TLAB区域,没有锁的开销,因此效率较高。 05
—
如何启用TLAB?
Java启动时增加参数-XX:+UseTLAB,-XX:TLABWasteTargetPercent设置TLAB占用Eden区的百分比大小。
今天和大家聊到这里,我是公众号“面试怪圈”的可爱猪猪,我们下篇文章见~ 想下载各种面试、视频、架构等资料,可访问 www.mianshiguaiquan.com 一站搞定!
这份面试资料已经第5版了,特别实用: