一、开启TLAB
TLAB(Thread Local Allocation Buffer)即线程本地分配缓存区,这是一个线程专用的内存分配区域。主要用来解决并发创建对象时分配空间造成指针碰撞的问题(减少CAS提升效率)与生命周期短小对象对GC带来的压力问题(朝生夕死,如下代码片段)。
public void demoTest () {
/*
*demo对象的作用域在demoTest方法内部
*demo对象在demoTest方法后执行创建,demoTest方法执行结束后就不在存在其他引用,开始等待GC回收
*/
Demo demo = new Demo();
demo.sayHello();
}
不足之处:TLAB的空间浪费,空间不连续,过大(大于TLAB空间一点点)或者过小(小于TLAB空间很多)都会造成空间浪费;
目前线上环境建议开启TALB 【默认开启-XX: +UseTLAB显示开启, -XX:TLABWasteTargetPercent设置TLAB空间占Eden空间的百分比大小默认1%】。
二、关闭逃逸分析
2.1 栈分配(JIT阶段)
经过逃逸分析确定一个方法中无法逃逸出此方法的对象可以直接在栈上分配。不在堆上分配对象,减轻GC负担
public void demoTest () {
Demo demo = new Demo();
demo.sayHello();
}
2.2 同步省略(消除同步,JIT阶段)
经过逃逸分析确定一个对象只能被一个线程私有访问,可以将对象同步操作转换为无同步保护的操作。通过锁消除提高并发的性能
// 逃逸分析前
public void test () {
User user = new User();
synchronized(user){
user.sayHello();
}
}
// 逃逸分析后 消除同步
public void test () {
User user = new User();
user.sayHello();
}
2.3 标量替换(JIT阶段)
经过逃逸分析确定一个对象不会被外界访问的话,就可以把这个对象拆解成若干个标量来代替。减少对象创建,节约堆内存空间,减轻GC负担
标量(Scalar)是指一个无法再分解成更小的数据的数据(Java中的原始数据类型就是标量)。聚合量(Aggregate)是指可以被分解的数据(java中的对象就是聚合量,因为他可以分解成其他聚合量和标量)。
class User {
public int a;
public int b;
public User(int a, int b){
this.a = a;
this.b = b;
}
}
// 未逃逸分析
public static void main (String[] args) {
User user = new User(1,2);
System.out.print("user.a is"+ user.a);
System.out.print("user.b is"+ user.b);
}
// 逃逸分析 标量替换后
public static void main (String[] args) {
int a = 1;
int b = 2;
System.out.print("user.a is"+ a);
System.out.print("user.b is"+ b);
}
目前线上环境建议关闭逃逸分析 【关闭逃逸分析-XX:-DoEscapeAnalysis 1.6之后默认开启,关闭标量替换-XX:-EliminateAllocation】。
三、对象空间分配流程
当开启逃逸分析与TLAB时java对象空间分配流程如下