小师妹学jvm之:逃逸分析和tlab(代码片段)

flydean flydean     2022-12-02     585

关键词:

简介

逃逸分析我们在JDK14中JVM的性能优化一文中已经讲过了,逃逸分析的结果就是JVM会在栈上分配对象,从而提升效率。如果我们在多线程的环境中,如何提升内存的分配效率呢?快来跟小师妹一起学习TLAB技术吧。

逃逸分析和栈上分配

小师妹:F师兄,从前大家都说对象是在堆中分配的,然后我就信了。上次你居然说可以在栈上分配对象,这个实在是颠覆了我一贯的认知啊。

柏拉图说过:思想永远是宇宙的统治者。只要思想不滑坡,办法总比困难多。别人告诉你的都是一些最基本,最最通用的情况。而师兄我告诉你的则是在优化中的特列情况。

小师妹:F师兄,看起来JVM在提升运行速度方面真的做了不少优化呀。

是呀,Java从最开始被诟病速度慢,到现在执行速度直追C语言。这些运行时优化是必不可少的。还记得我们之前讲的逃逸分析是怎么回事吗?

小师妹:F师兄,这个我知道,如果一个对象的分配是在方法内部,并且没有多线程访问的情况下,那么这个对象其实可以看做是一个本地对象,这样的对象不管创建在哪里都只对本线程中的本方法可见,因此可以直接分配在栈空间中。

对的,栈上分配的对象因为不用考虑同步,所以执行速度肯定会更加快速,这也是为什么JVM会引入栈上分配的原因。

再举一个形象直观的例子。工厂要组装一辆汽车,在buildCar的过程中,需要先创建一个Car对象,然后给它按上轮子。

  public static void main(String[] args) 
    buildCar();
  
  public static void buildCar() 
    Wheel whell = new Wheel(); //分配轮子
    Car car = new Car(); //分配车子
    car.setWheel(whell);
  


class Wheel 

class Car 
  private Wheel whell;
  public void setWheel(Wheel whell) 
    this.whell = whell;
  

考虑一下上面的情况,如果假设该车间是一个机器人组装一台车,那么上面方法中创建的Car和Wheel对象,其实只会被这一个机器人访问,其他的机器人根本就不会用到这个车的对象。那么这个对象本质上是对其他机器人隐形的。所以我们可以不在公共空间分配这个对象,而是在私人的栈空间中分配。

逃逸分析还有一个作用就是lock coarsening。同样的,单线程环境中,锁也是不需要的,也可以优化掉。

TLAB简介

小师妹:F师兄,我觉得逃逸分析很好呀,栈上分配也不错。既然又这么厉害的两项技术了,为什么还要用到TLAB呢?

首先这是两个不同的概念,TLAB的全称是Thread-Local Allocation Buffers。Thread-Local大家都知道吧,就是线程的本地变量。而TLAB则是线程的本地分配空间。

逃逸分析和栈上分配只是争对于单线程环境来说的,如果在多线程环境中,不可避免的会有多个线程同时在堆空间中分配对象的情况。

这种情况下如何处理才能提升性能呢?

小师妹:哇,多个线程竞争共享资源,这不是一个典型的锁和同步的问题吗?

锁和同步是为了保证整个资源一次只能被一个线程访问,我们现在的情况是要在资源中为线程划分一定的区域。这种操作并不需要完全的同步,因为heap空间够大,我们可以在这个空间中划分出一块一块的小区域,为每个线程都分一块。这样不就解决了同步的问题了吗?这也可以称作空间换时间。

TLAB详解

小师妹,还记得heap分代技术中的一个中心两个基本点吗?哦,1个Eden Space和2个Suvivor Space吗?

技术图片

Young Gen被划分为1个Eden Space和2个Suvivor Space。当对象刚刚被创建的时候,是放在Eden space。垃圾回收的时候,会扫描Eden Space和一个Suvivor Space。如果在垃圾回收的时候发现Eden Space中的对象仍然有效,则会将其复制到另外一个Suvivor Space。

就这样不断的扫描,最后经过多次扫描发现任然有效的对象会被放入Old Gen表示其生命周期比较长,可以减少垃圾回收时间。

因为TLAB关注的是新分配的对象,所以TLAB是被分配在Eden区间的,从图上可以看到TLAB是一个一个的连续空间。

然后将这些连续的空间分配个各个线程使用。

因为每一个线程都有自己的独立空间,所以这里不涉及到同步的概念。默认情况下TLAB是开启的,你可以通过:

-XX:-UseTLAB

来关闭它。

设置TLAB空间的大小

小师妹,F师兄,这个TLAB的大小是系统默认的吗?我们可以手动控制它的大小吗?

要解决这个问题,我们还得去看JVM的C++实现,也就是threadLocalAllocBuffer.cpp:

技术图片

上面的代码可以看到,如果设置了TLAB(默认是0),那么TLAB的大小是定义的TLABSize除以HeapWordSize和max_size()中最小的那个。

HeapWordSize是heap中一个字的大小,我猜它=8。别问我为什么,其实我也是猜的,有人知道答案的话可以留言告诉我。

TLAB的大小可以通过:

-XX:TLABSize

来设置。

如果没有设置TLAB,那么TLAB的大小就是分配线程的平均值。

TLAB的最小值可以通过:

-XX:MinTLABSize

来设置。

默认情况下:

-XX:ResizeTLAB

resize开关是默认开启的,那么JVM可以对TLAB空间大小进行调整。

TLAB中大对象的分配

小师妹:F师兄,我想到了一个问题,既然TLAB是有大小的,如果一个线程中定义了一个非常大的对象,TLAB放不下了,该怎么办呢?

好问题,这种情况下又有两种可能性,我们假设现在的TLAB的大小是100K:

第一种可能性:

目前TLAB被使用了20K,还剩80K的大小,这时候我们创建了一个90K大小的对象,现在90K大小的对象放不进去TLAB,这时候需要直接在heap空间去分配这个对象,这种操作实际上是一种退化操作,官方叫做 slow allocation。

第二中个可能性:

目前TLAB被使用了90K,还剩10K大小,这时候我们创建了一个15K大小的对象。

这个时候就要考虑一下是否仍然进行slow allocation操作。

因为TLAB差不多已经用完了,为了保证后面new出来的对象仍然可以有一个TLAB可用,这时候JVM可以尝试将现在的TLAB Retire掉,然后分配一个新的TLAB空间,把15K的对象放进去。

JVM有个开关,叫做:

-XX:TLABWasteTargetPercent=N

这个开关的默认值是1。表示如果新分配的对象大小如果超出了设置的这个百分百,那么就会执行slow allocation。否则就会分配一个新的TLAB空间。

同时JVM还定义了一个开关:

-XX:TLABWasteIncrement=N

为了防止过多的slow allocation,JVM定义了这个开关(默认值是4),比如说第一次slow allocation的极限值是1%,那么下一次slow allocation的极限值就是%1+4%=5%。

TLAB空间中的浪费

小师妹:F师兄,如果新分配的TLAB空间,那么老的TLAB中没有使用的空间该怎么办呢?

这个叫做TLAB Waste。因为不会再在老的TLAB空间中分配对象了,所以剩余的空间就浪费了。

总结

本文介绍了逃逸分析和TLAB的使用。希望大家能够喜欢。

本文作者:flydean程序那些事

本文链接:http://www.flydean.com/jvm-escapse-tlab/

本文来源:flydean的博客

欢迎关注我的公众号:程序那些事,更多精彩等着您!

小师妹学jvm之:jit中的printcompilation(代码片段)

...释了LogCompilation日志文件中的内容定义。今天我们再和小师妹一起学习LogCompilation的姊妹篇PrintCompilation,看看都有什么妙用吧。PrintCompilati 查看详情

小师妹学jvm之:cacheline对代码性能的影响(代码片段)

...sembly来理解我们之前不能理解的问题。一个奇怪的现象小师妹:F师兄,之前你讲了那么多JVM中JIT在编译中的性能优化,讲真的,在工作中我们真的需要知道这些东西吗?知道这些东西对我们的工作有什么好处吗 查看详情

小师妹学jvm之:jit中的printassembly(代码片段)

...近的地方来观看JVM的运行原理:Assembly。使用PrintAssembly小师妹:F师兄,上次你给我介绍了java中的字节码,还有JIT中的Log 查看详情

jvm--08--堆2---tlab逃逸分析(代码片段)

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档文章目录堆空间分代思想问题1:为什么要把Java堆分代?不分代就不能正常工作了吗?内存分配策略对象晋升老年代的年龄阀值,... 查看详情

jvm之对象分配:栈上分配&tlab分配(代码片段)

...对于大对象无法实现栈上分配   2.4技术基础:逃逸分析    2.4.1逃逸分析的目的:判断对象的作用域是否超出函数体[即:判断是否逃逸出函数体]//user的作用域超出了函数setUser的范围,是逃逸对象//当函数结... 查看详情

jvm性能优化对象内存分配之虚拟机参数调优分析(代码片段)

...化方案总结和列举调优参数计划。主要包含:调优之逃逸分析(栈上分配)调优之线程局部缓存(TLAB)调优之G1回收器栈上分配与逃逸分析-XX:+DoEscapeAnalysis逃逸分析(EscapeAnalysis)逃逸分析的基本行为就是分析... 查看详情

jvm内存分配性能提升之——逃逸分析与tlab

...言。这些运行时优化是必不可少的。还记得我们之前讲的逃逸分析是怎么回事吗?jvm分配内存当类已经被加载完毕了,那么会执行第二步,也就是分配内存。我们都知道new对象一般情况来说生成的对象都是会存放在 查看详情

jvm之逃逸分析技术(代码片段)

引言我在面试别人的过程中,JVM内存模型我几乎必问,虽然有人说问这些就是面试造航母,工作拧螺丝。如果你想当一名CRUD码农,你可以选择不用了解这些。在JVM内存模型的问答中,有些人能说出对象是在堆... 查看详情

关于jvm的逃逸分析(代码片段)

何谓“逃逸”?  我们都知道Java中的对象默认是分配到堆上的,垃圾回收机制也会回收堆中不再使用的对象,但在此之前需要筛选可回收的对象,因此会造成,回收对象还有整理内存,都比较耗时间,开销也是非常之大... 查看详情

jvm逃逸分析(史上最全)(代码片段)

对于JVM“逃逸分析”特性,也是近年来大厂面试、高薪面试的常见面试题。和逃逸分析有关的常见面试题:Java中的对象一定是在堆上分配的吗?注:本文以PDF持续更新,最新尼恩架构笔记、面试题的PDF文件... 查看详情

jvm运行时数据区篇(堆空间扩展知识)(代码片段)

...区域TLAB3.堆空间是分配对象的唯一选择吗?4.什么是逃逸分析?5.基于逃逸分析进行的优化策略1.栈上分配2.同步省略3.标量替换6.逃逸分析总结1.堆空间常见参数设置参数名参数含义-XX:PrintFlagsInitial查看所有可设置参数的默... 查看详情

小师妹学javaio之:文件读取那些事

...读取的方式按字节读取的方式寻找出错的行数总结简介小师妹最新对javaIO中的reader和stream产生了一点点困惑,不知道到底该用哪一个才对,怎么读取文件才是正确的姿势呢?今天F师兄现场为她解答。字符和字节小师妹最近很迷... 查看详情

深入理解jvm原理之逃逸分析

...令重排序等等;有的是基于分析技术,例如关系分析或者逃逸分析等等,今天就重点介绍一下jvm中的分析技术优化---逃逸分析;内容大部分源自于《深入理解Java虚拟机》;逃逸分析一般分为两种:一种基本行为就是分析对象的... 查看详情

一文秒懂jvm逃逸分析,yyds(代码片段)

...提高JVM的性能和节省内存空间,JVM提供了一种叫做“逃逸分析”的特性,而且对于“逃逸分析”这种特性,也是近年来大厂面试常问的知识点。今天,我们就一起来聊聊什么是逃逸分析。逃逸分析的概念先以官方... 查看详情

jvm逃逸分析(代码片段)

一、JVM的运行模式1、解释模式(InterpretedMode)只使用解释器(-Xint强制JVM使用解释模式),执行一行JVM字节码就编译一行为机器码编译模式(CompiledMode)2、只使用编译器(-XcompJVM使用编译模式)... 查看详情

jvm逃逸分析doescapeanalysis(代码片段)

JVM逃逸分析JVM有栈、堆、方法区、本地栈等组成 栈:每个方法被执行的时候都会同时创建一个栈帧用于存储局部变量表、操作栈、动态连接、方法出口等信息。每个方法被调用直至执行完成的过程,就对应着一个栈帧在虚拟... 查看详情

小师妹学javaio之:文件编码和字符集unicode

...件编码解决Properties中的乱码真.终极解决办法总结简介小师妹一时兴起,使用了一项从来都没用过的新技能,没想却出现了一个无法解决的问题。把大象装进冰箱到底有几步?乱码的问题又是怎么解决的?快来跟F师兄一起看看吧... 查看详情

小师妹学javaio之:文件编码和字符集unicode

...件编码解决Properties中的乱码真.终极解决办法总结简介小师妹一时兴起,使用了一项从来都没用过的新技能,没想却出现了一个无法解决的问题。把大象装进冰箱到底有几步?乱码的问题又是怎么解决的?快来跟F师兄一起看看吧... 查看详情