java逃逸分析、TALB及对象分配的影响

我们在学习java的过程中,一般认为一个new一个对象,肯定会分配在堆上,由于有了java的逃逸分析和TLAB(Thread local allocation buffer),可能这个结论并不一定那么准确。首先先来了解下java中new一个新对象的流程:


对象创建流程

1.如果开启了逃逸分析,如果分析出某个对象没有逃逸,则会尝试将对象分配在栈上,如果不成功,则会尝试2
2.尝试分配在TLAB上,不成功则继续3
3.是否满足进入老年代(对象是否足够大)
4.eden区分配
首先来了解下逃逸分析:
经过逃逸分析之后,可以得到三种对象的逃逸状态。

1. GlobalEscape(全局逃逸), 即一个对象的引用逃出了方法或者线程。例如,一个对象的引用是复制给了一个类变量,或者存储在在一个已经逃逸的对象当中,或者这个对象的引用作为方法的返回值返回给了调用方法。
2. ArgEscape(参数级逃逸),即在方法调用过程当中传递对象的应用给一个方法。这种状态可以通过分析被调方法的二进制代码确定。
3. NoEscape(没有逃逸),一个可以进行标量替换的对象。可以不将这种对象分配在传统的堆上。

编译器可以使用逃逸分析的结果,对程序进行一下优化。

1. 堆分配对象变成栈分配对象。一个方法当中的对象,对象的引用没有发生逃逸,那么这个方法可能会被分配在栈内存上而非常见的堆内存上。不需要gc回收对象,对象会随着线程一起消亡。
2. 消除同步。线程同步的代价是相当高的,同步的后果是降低并发性和性能。逃逸分析可以判断出某个对象是否始终只被一个线程访问,如果只被一个线程访问,那么对该对象的同步操作就可以转化成没有同步保护的操作,这样就能大大提高并发程度和性能。(vector的add()、StringBuffer的append())
3. 矢量替代。Java虚拟机中的原始数据类型(int,long等数值类型以及reference类型等)都不能再进一步分解,它们就可以称为标量。相对的,如果一个数据可以继续分解,那它称为聚合量,Java中最典型的聚合量是对象。如果逃逸分析证明一个对象不会被外部访问,并且这个对象是可分解的,那程序真正执行的时候将可能不创建这个对象,而改为直接创建它的若干个被这个方法使用到的成员变量来代替。拆散后的变量便可以被单独分析与优化,可以各自分别在栈帧或寄存器上分配空间,原本的对象就无需整体分配空间了。

下面一起看个逃逸分析的例子

public class EscapeAnalysisDemo {
    public static void main(String[] args) {
        escapeAnalysis();
    }

    private static void escapeAnalysis() {
        Apple apple = new Apple();
        Person person = new Person(apple);
    }

    public static class Person {
        Apple apple;
        public Person(Apple apple) {
            this.apple = apple;
        }
    }
    public static class Apple {
    }
}

经过逃逸分析,返现Person和Apple都没有逃出escapeAnalysis()方法,因此编译器可以将这两个对象分配在栈上。

再看一个逃逸分析优化后的代码例子,代码如下:

public class EscapeAnalysis {
    public static void main(String[] args) {
        long start=System.currentTimeMillis();
        for (int i=0;i<100000*100000*10;i++){
            Foo foo=new Foo();
        }
        System.out.println(System.currentTimeMillis()-start);
    }
    public static class Foo{
        private int x;
        private static int counter;
        public Foo() {
            this.x=(++counter);
        }
    }
}

jvm参数设置如下:

没有开启逃逸分析:-server -verbose:gc  -Xmx10m -Xms10m
运行结果:
....
[GC (Allocation Failure)  3548K->1500K(9728K), 0.0003464 secs]
[GC (Allocation Failure)  3548K->1500K(9728K), 0.0003518 secs]
[GC (Allocation Failure)  3548K->1500K(9728K), 0.0004652 secs]
[GC (Allocation Failure)  3548K->1500K(9728K), 0.0003770 secs]
[GC (Allocation Failure)  3548K->1500K(9728K), 0.0003569 secs]
[GC (Allocation Failure)  3548K->1500K(9728K), 0.0003963 secs]
[GC (Allocation Failure)  3548K->1500K(9728K), 0.0003718 secs]
[GC (Allocation Failure)  3548K->1500K(9728K), 0.0003799 secs]
7191
开启逃逸分析:-server -verbose:gc  -Xmx10m -Xms10m -Xss1024m -XX:+DoEscapeAnalysis
结果:
[GC (Allocation Failure)  2048K->884K(9728K), 0.0012021 secs]
[GC (Allocation Failure)  2932K->1146K(9728K), 0.0011442 secs]
[GC (Allocation Failure)  3194K->1225K(9728K), 0.0010019 secs]
[GC (Allocation Failure)  3273K->1233K(9728K), 0.0006168 secs]
13

那么,TLAB到底是什么东西呢?
JVM在内存新生代Eden Space中开辟了一小块线程私有的区域,称作TLAB(Thread-local allocation buffer)。默认设定为占用Eden Space的1%。在Java程序中很多对象都是小对象且用过即丢,它们不存在线程共享也适合被快速GC,所以对于小对象通常JVM会优先分配在TLAB上,并且TLAB上的分配由于是线程私有所以没有锁开销。因此在实践中分配多个小对象的效率通常比分配一个大对象的效率要高。
也就是说,Java中每个线程都会有自己的缓冲区称作TLAB(Thread-local allocation buffer),每个TLAB都只有一个线程可以操作,TLAB结合bump-the-pointer技术可以实现快速的对象分配,而不需要任何的锁进行同步,也就是说,在对象分配的时候不用锁住整个堆,而只需要在自己的缓冲区分配即可。

1. 编译器通过逃逸分析,确定对象是在栈上分配还是在堆上分配。如果是在堆上分配,则进入选项2.
2. 如果tlab_top + size <= tlab_end,则在在TLAB上直接分配对象并增加tlab_top 的值,如果现有的TLAB不足以存放当前对象则3.
3. 重新申请一个TLAB,并再次尝试存放当前对象。如果放不下,则4.
4. 在Eden区加锁(这个区是多线程共享的),如果eden_top + size <= eden_end则将对象存放在Eden区,增加eden_top 的值,如果Eden区不足以存放,则5.
5. 执行一次Young GC(minor collection)。
6. 经过Young GC之后,如果Eden区任然不足以存放当前对象,则直接分配到老年代。

TLAB相关参数

虚拟机针对体积不大的对象,会优先分配到tlab区域,因此就失去了分配到老年代的机会,jvm默认开启
-XX:-UseTLAB使用该参数,禁止TLAB
TLAB:thread local allocation buffer线程本地分配缓存,线程专用的内存分配区域,为了加速对象分配,每个线程都会对应一个TLAB,独享,用来避免多线程冲突,提高分配效率,TLAB一般不会太大,当大对象无法分配在TLAB上时,则会分配在堆上。
-XX:+UseTLAB 使用TLAb
-XX:TLABSize 设置TLAB的大小
-XX:TLABRefillWasterFraction 设置进入TLAB空间的单个对象大小,它是一个比例值,默认为64,即如果对象大于整个TLAB的1/64,则在堆上创建对象(线程专用的内存分配区域,为了加速对象分配,每个线程都会对应一个TLAB空间)
-XX:PrintTLAB 查看TLAB
-XX:ResizeTLAB自调整TLABRefillWasterFraction的阈值 【实例jvm20180616-TEST02.java】
-XX:+UseTLAB -XX:TLABSize=102400 -XX:TLABRefillWasteFraction=100 -XX:-DoEscapeAnalysis

开启逃逸分析后,如果满足逃逸分析,则会在TLAB上创建,代码示例如下:

public class Test02 {
    static void alloc() {
        byte[] bytes = new byte[2];
    }

    public static void main(String[] args) {
        /**
         * -XX:+UseTLAB -XX:TLABSize=102400 -XX:TLABRefillWasteFraction=100 -     XX:+DoEscapeAnalysis
         * 必须配置最后一个参数 禁用逃逸分析,
         * 启用逃逸分析,表明所有的对象都可以在栈上分配
         */
        long start = System.currentTimeMillis();
        for (int i = 0; i < 1000000000; i++) {
            alloc();
        }
        System.out.println("cost time :"+(System.currentTimeMillis() - start));
    }
}
未开启逃逸分析:-XX:+UseTLAB -XX:TLABSize=102400 -XX:TLABRefillWasteFraction=100 -XX:-DoEscapeAnalysis
结果:cost time :4507
开启逃逸分析:-XX:+UseTLAB -XX:TLABSize=102400 -XX:TLABRefillWasteFraction=100 -XX:+DoEscapeAnalysis
结果:cost time :11

【参考博客】
1.https://blog.csdn.net/yangzl2008/article/details/43202969
2.http://www.importnew.com/23150.html

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,324评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,356评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,328评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,147评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,160评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,115评论 1 296
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,025评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,867评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,307评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,528评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,688评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,409评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,001评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,657评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,811评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,685评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,573评论 2 353

推荐阅读更多精彩内容