Battle:你会TLAB,我会逃逸分析

“噔噔噔……”传来一阵敲门声,把我从美梦中惊醒了。

朦胧间听到有人在说话”阿Q,在家不?”

“来了来了”,推门一看,原来是”赵信”兄弟。

「 赵信」:自称常山赵子龙,一把三爪长枪耍的虎虎生风,见人上去就是一枪,人送外号”菊花信”。

Battle:你会TLAB,我会逃逸分析

TLAB

Battle:你会TLAB,我会逃逸分析
  • 尽管不是所有的对象实例都能够在 TLAB 中成功分配内存(因为它的空间比较小),但 JVM 明确是将 TLAB 作为内存分配的首选;
  • 一旦对象在 TLAB 空间分配内存失败时, JVM 就会尝试着通过使用加锁机制确保数据操作的原子性,从而直接在 Eden 空间中分配内存。

「 参数设置」

  • -XX:UseTLAB :设置是否开启 TLAB 空间;
  • -XX:TLABWasteTargetPercent :设置 TLAB 空间所占 Eden 空间的百分比大小,默认仅占 1% ;

Battle:你会TLAB,我会逃逸分析

堆是分配对象的唯一选择吗?

Battle:你会TLAB,我会逃逸分析
  1. 如果经过逃逸分析( Escape Analysis )后发现,一个对象并没有逃逸出方法,那么就可能被优化为栈上分配。这样就无需在堆上分配内存,也无须进行垃圾回收了。这也是最常见的堆外存储技术。
  2. 基于 OpenJDK 深度定制的 TaoBaoVM ,它创新的 GCIH(GCinvisible heap) 实现了堆外分配。将生命周期较长的 Java 对象从堆中移至堆外,并且 GC 不能管理 GCIH 内部的 Java 对象,以此达到降低GC的回收频率和提升 GC 的回收效率的目的。

Battle:你会TLAB,我会逃逸分析

「 举例一」

<span class="hljs-function"><span class="hljs-keyword">public</span>&#xA0;<span class="hljs-keyword">void</span>&#xA0;<span class="hljs-title">method</span><span class="hljs-params">()</span></span>{<br>&#xA0;&#xA0;&#xA0;&#xA0;User&#xA0;user&#xA0;=&#xA0;<span class="hljs-keyword">new</span>&#xA0;User();<br>&#xA0;&#xA0;&#xA0;&#xA0;...<br>&#xA0;&#xA0;&#xA0;&#xA0;user&#xA0;=&#xA0;<span class="hljs-keyword">null</span>;<br>}

user对象在方法内部声明,且在内部置为 null,未被方法外的方法所引用,我们就说 user对象没有发生逃逸。

「 可以」分配到栈上,并随着方法的结束,栈空间也随之移除。

「 举例二」

<span class="hljs-function"><span class="hljs-keyword">public</span>&#xA0;<span class="hljs-keyword">static</span>&#xA0;StringBuffer&#xA0;<span class="hljs-title">createStringBuffer</span><span class="hljs-params">(String&#xA0;s1,String&#xA0;s2)</span></span>{<br>&#xA0;&#xA0;&#xA0;&#xA0;StringBuffer&#xA0;sb&#xA0;=&#xA0;<span class="hljs-keyword">new</span>&#xA0;StringBuffer();<br>&#xA0;&#xA0;&#xA0;&#xA0;sb.append(s1);<br>&#xA0;&#xA0;&#xA0;&#xA0;sb.append(s2);<br>&#xA0;&#xA0;&#xA0;&#xA0;<span class="hljs-keyword">return</span>&#xA0;sb;<br>}

虽然 sb对象在方法内部被定义,但是它又作为方法的返回对象,可被其它方法调用,我们就说 sb对象发生了逃逸。

要想不发生逃逸,可以改造为:

<span class="hljs-function"><span class="hljs-keyword">public</span>&#xA0;<span class="hljs-keyword">static</span>&#xA0;String&#xA0;<span class="hljs-title">createStringBuffer</span><span class="hljs-params">(String&#xA0;s1,String&#xA0;s2)</span></span>{<br>&#xA0;&#xA0;&#xA0;&#xA0;StringBuffer&#xA0;sb&#xA0;=&#xA0;<span class="hljs-keyword">new</span>&#xA0;StringBuffer();<br>&#xA0;&#xA0;&#xA0;&#xA0;sb.append(s1);<br>&#xA0;&#xA0;&#xA0;&#xA0;sb.append(s2);<br>&#xA0;&#xA0;&#xA0;&#xA0;<span class="hljs-keyword">return</span>&#xA0;sb.toString();<br>}

Battle:你会TLAB,我会逃逸分析

JDK 6u23版本之后, HotSpot中默认开启了逃逸分析。

  • -XX:DoEscapeAnalysis :显式开启逃逸分析
  • -XX:+PrintEscapeAnalysis :查看逃逸分析的筛选结果

Battle:你会TLAB,我会逃逸分析

栈上分配

Battle:你会TLAB,我会逃逸分析
<br><span class="hljs-keyword">public</span>&#xA0;<span class="hljs-class"><span class="hljs-keyword">class</span>&#xA0;<span class="hljs-title">StackAllocation</span>&#xA0;</span>{<br>&#xA0;&#xA0;&#xA0;&#xA0;<span class="hljs-function"><span class="hljs-keyword">public</span>&#xA0;<span class="hljs-keyword">static</span>&#xA0;<span class="hljs-keyword">void</span>&#xA0;<span class="hljs-title">main</span><span class="hljs-params">(String[]&#xA0;args)</span>&#xA0;</span>{<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;<span class="hljs-keyword">long</span>&#xA0;start&#xA0;=&#xA0;System.currentTimeMillis();<br><br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;<span class="hljs-keyword">for</span>&#xA0;(<span class="hljs-keyword">int</span>&#xA0;i&#xA0;=&#xA0;<span class="hljs-number">0</span>;&#xA0;i&#xA0;< <span class="hljs-number">10000000;&#xA0;i++)&#xA0;{<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;alloc();<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;}<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;<span class="hljs-keyword">long</span>&#xA0;end&#xA0;=&#xA0;System.currentTimeMillis();<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;System.out.println(<span class="hljs-string">"&#x82B1;&#x8D39;&#x7684;&#x65F6;&#x95F4;&#x4E3A;&#xFF1A;&#xA0;"</span>&#xA0;+&#xA0;(end&#xA0;-&#xA0;start)&#xA0;+&#xA0;<span class="hljs-string">"&#xA0;ms"</span>);<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;<span class="hljs-keyword">try</span>&#xA0;{<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;Thread.sleep(<span class="hljs-number">1000000</span>);<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;}&#xA0;<span class="hljs-keyword">catch</span>&#xA0;(InterruptedException&#xA0;e1)&#xA0;{<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;e1.printStackTrace();<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;}<br>&#xA0;&#xA0;&#xA0;&#xA0;}<br><br>&#xA0;&#xA0;&#xA0;&#xA0;<span class="hljs-function"><span class="hljs-keyword">private</span>&#xA0;<span class="hljs-keyword">static</span>&#xA0;<span class="hljs-keyword">void</span>&#xA0;<span class="hljs-title">alloc</span><span class="hljs-params">()</span>&#xA0;</span>{<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;User&#xA0;user&#xA0;=&#xA0;<span class="hljs-keyword">new</span>&#xA0;User();<br>&#xA0;&#xA0;&#xA0;&#xA0;}<br><br>&#xA0;&#xA0;&#xA0;&#xA0;<span class="hljs-keyword">static</span>&#xA0;<span class="hljs-class"><span class="hljs-keyword">class</span>&#xA0;<span class="hljs-title">User</span>&#xA0;</span>{<br><br>&#xA0;&#xA0;&#xA0;&#xA0;}<br>}<br><br></ <span>

逃逸分析默认开启,也可以手动开启: -XX:+DoEscapeAnalysis

Battle:你会TLAB,我会逃逸分析

关闭逃逸分析

Battle:你会TLAB,我会逃逸分析

同步省略

Battle:你会TLAB,我会逃逸分析

我们都知道线程同步的代价是相当高的,同步的后果就是降低了并发性和性能。

JVM为了提高性能,在动态编译同步块的时候, JIT编译器可以借助逃逸分析来判断同步块所使用的锁对象是否只能够被一个线程访问。

如果符合条件,那么 JIT编译器在编译这个同步块的时候就会取消对这部分代码的同步。这个取消同步的过程就叫同步省略,也叫锁消除。

「 举例」

<span class="hljs-keyword">public</span>&#xA0;<span class="hljs-class"><span class="hljs-keyword">class</span>&#xA0;<span class="hljs-title">SynchronizedTest</span>&#xA0;</span>{<br>&#xA0;&#xA0;&#xA0;&#xA0;<span class="hljs-function"><span class="hljs-keyword">public</span>&#xA0;<span class="hljs-keyword">void</span>&#xA0;<span class="hljs-title">method</span><span class="hljs-params">()</span>&#xA0;</span>{<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;Object&#xA0;code&#xA0;=&#xA0;<span class="hljs-keyword">new</span>&#xA0;Object();<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;<span class="hljs-keyword">synchronized</span>(code)&#xA0;{<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;System.out.println(code);<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;}<br>&#xA0;&#xA0;&#xA0;&#xA0;}<br>&#xA0;&#xA0;&#xA0;&#xA0;<br>&#xA0;&#xA0;&#xA0;&#xA0;<br>&#xA0;&#xA0;&#xA0;&#xA0;<br>&#xA0;&#xA0;&#xA0;&#xA0;<span class="hljs-function"><span class="hljs-keyword">public</span>&#xA0;<span class="hljs-keyword">void</span>&#xA0;<span class="hljs-title">method2</span><span class="hljs-params">()</span>&#xA0;</span>{<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;Object&#xA0;code&#xA0;=&#xA0;<span class="hljs-keyword">new</span>&#xA0;Object();<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;System.out.println(code);<br>&#xA0;&#xA0;&#xA0;&#xA0;}<br>}<br>

❝在解释执行时这里仍然会有锁,但是经过服务端编译器的即时编译之后,这段代码就会忽略所有的同步措施而直接执行。

Battle:你会TLAB,我会逃逸分析

标量替换

Battle:你会TLAB,我会逃逸分析
  • 标量:不可被进一步分解的量,如 JAVA 的基本数据类型就是标量;
  • 聚合量:可以被进一步分解的量, 在 JAVA 中对象就是可以被进一步分解的聚合量。

聚合量可以分解成其它标量和聚合量。

标量替换,又名分离对象,即在 JIT阶段,如果经过逃逸分析,发现一个对象不会被外界访问的话,那么经过 JIT优化,就会把这个对象拆解成若干个其中包含的成员变量来替代。

「 举例」

<span class="hljs-keyword">public</span>&#xA0;<span class="hljs-class"><span class="hljs-keyword">class</span>&#xA0;<span class="hljs-title">ScalarTest</span>&#xA0;</span>{<br>&#xA0;&#xA0;&#xA0;&#xA0;<span class="hljs-function"><span class="hljs-keyword">public</span>&#xA0;<span class="hljs-keyword">static</span>&#xA0;<span class="hljs-keyword">void</span>&#xA0;<span class="hljs-title">main</span><span class="hljs-params">(String[]&#xA0;args)</span>&#xA0;</span>{<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;alloc();&#xA0;&#xA0;&#xA0;<br>&#xA0;&#xA0;&#xA0;&#xA0;}<br>&#xA0;&#xA0;&#xA0;&#xA0;<span class="hljs-function"><span class="hljs-keyword">public</span>&#xA0;<span class="hljs-keyword">static</span>&#xA0;<span class="hljs-keyword">void</span>&#xA0;<span class="hljs-title">alloc</span><span class="hljs-params">()</span></span>{<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;Point&#xA0;point&#xA0;=&#xA0;<span class="hljs-keyword">new</span>&#xA0;Point(<span class="hljs-number">1</span>,<span class="hljs-number">2</span>);<br>&#xA0;&#xA0;&#xA0;&#xA0;}<br>}<br><span class="hljs-class"><span class="hljs-keyword">class</span>&#xA0;<span class="hljs-title">Point</span></span>{<br>&#xA0;&#xA0;&#xA0;&#xA0;<span class="hljs-keyword">private</span>&#xA0;<span class="hljs-keyword">int</span>&#xA0;x;<br>&#xA0;&#xA0;&#xA0;&#xA0;<span class="hljs-keyword">private</span>&#xA0;<span class="hljs-keyword">int</span>&#xA0;y;<br>&#xA0;&#xA0;&#xA0;&#xA0;<span class="hljs-function"><span class="hljs-keyword">public</span>&#xA0;<span class="hljs-title">Point</span><span class="hljs-params">(<span class="hljs-keyword">int</span>&#xA0;x,<span class="hljs-keyword">int</span>&#xA0;y)</span></span>{<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;<span class="hljs-keyword">this</span>.x&#xA0;=&#xA0;x;<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;<span class="hljs-keyword">this</span>.y&#xA0;=&#xA0;y;<br>&#xA0;&#xA0;&#xA0;&#xA0;}<br>}<br><br><span class="hljs-function"><span class="hljs-keyword">public</span>&#xA0;<span class="hljs-keyword">static</span>&#xA0;<span class="hljs-keyword">void</span>&#xA0;<span class="hljs-title">alloc</span><span class="hljs-params">()</span></span>{<br>&#xA0;&#xA0;&#xA0;&#xA0;<span class="hljs-keyword">int</span>&#xA0;x&#xA0;=&#xA0;<span class="hljs-number">1</span>;<br>&#xA0;&#xA0;&#xA0;&#xA0;<span class="hljs-keyword">int</span>&#xA0;y&#xA0;=&#xA0;<span class="hljs-number">2</span>;<br>}<br>

Battle:你会TLAB,我会逃逸分析

❝标量替换默认开启,你也可以通过参数手动设置 -XX:+EliminateAllocations,开启之后允许将对象打散分配到栈上, GC减少,执行速度提升。

常见的发生逃逸的场景

Battle:你会TLAB,我会逃逸分析

「 举例」

<span class="hljs-keyword">public</span>&#xA0;<span class="hljs-class"><span class="hljs-keyword">class</span>&#xA0;<span class="hljs-title">EscapeAnalysis</span>&#xA0;</span>{<br><br>&#xA0;&#xA0;&#xA0;&#xA0;<span class="hljs-keyword">public</span>&#xA0;EscapeAnalysis&#xA0;obj;<br>&#xA0;&#xA0;&#xA0;&#xA0;<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;<br>&#xA0;&#xA0;&#xA0;&#xA0;<span class="hljs-function"><span class="hljs-keyword">public</span>&#xA0;<span class="hljs-keyword">void</span>&#xA0;<span class="hljs-title">setObj</span><span class="hljs-params">()</span></span>{<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;<span class="hljs-keyword">this</span>.obj&#xA0;=&#xA0;<span class="hljs-keyword">new</span>&#xA0;EscapeAnalysis();<br>&#xA0;&#xA0;&#xA0;&#xA0;}<br>&#xA0;&#xA0;&#xA0;&#xA0;<br><br>&#xA0;&#xA0;&#xA0;&#xA0;<br>&#xA0;&#xA0;&#xA0;&#xA0;<span class="hljs-function"><span class="hljs-keyword">public</span>&#xA0;EscapeAnalysis&#xA0;<span class="hljs-title">getInstance</span><span class="hljs-params">()</span></span>{<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;<span class="hljs-keyword">return</span>&#xA0;obj&#xA0;==&#xA0;<span class="hljs-keyword">null</span>?&#xA0;<span class="hljs-keyword">new</span>&#xA0;EscapeAnalysis()&#xA0;:&#xA0;obj;<br>&#xA0;&#xA0;&#xA0;&#xA0;}<br>&#xA0;&#xA0;&#xA0;<br>&#xA0;&#xA0;&#xA0;<br>&#xA0;&#xA0;&#xA0;&#xA0;<br>&#xA0;&#xA0;&#xA0;&#xA0;<span class="hljs-function"><span class="hljs-keyword">public</span>&#xA0;<span class="hljs-keyword">void</span>&#xA0;<span class="hljs-title">useEscapeAnalysis1</span><span class="hljs-params">()</span></span>{<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;EscapeAnalysis&#xA0;e&#xA0;=&#xA0;getInstance();<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;<br>&#xA0;&#xA0;&#xA0;&#xA0;}<br>&#xA0;&#xA0;&#xA0;&#xA0;<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;<br>&#xA0;&#xA0;&#xA0;&#xA0;<span class="hljs-function"><span class="hljs-keyword">public</span>&#xA0;<span class="hljs-keyword">void</span>&#xA0;<span class="hljs-title">useEscapeAnalysis</span><span class="hljs-params">()</span></span>{<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;EscapeAnalysis&#xA0;e&#xA0;=&#xA0;<span class="hljs-keyword">new</span>&#xA0;EscapeAnalysis();<br>&#xA0;&#xA0;&#xA0;&#xA0;}<br>}<br>

逃逸分析并不成熟

Battle:你会TLAB,我会逃逸分析

1999年就已经发表了关于逃逸分析的论文,但 JDK1.6中才有实现,而且这项技术到如今也不是十分成熟。

其根本原因就是无法保证逃逸分析的性能提升一定能高于它的消耗,因为逃逸分析自身也需要进行一系列复杂的分析,是需要耗时的。

一个极端的例子,就是经过逃逸分析之后,发现所有对象都逃逸了,那这个逃逸分析的过程就白白浪费掉了。

❝细心的小伙伴也应该能发现,我们在抽样器中的截图其实就是在堆中分配的对象。

以上就是今天的所有内容了,如果你有不同的意见或者更好的 idea,欢迎联系阿Q,添加阿Q可以加入技术交流群参与讨论呦!

后台留言领取 java干货资料:学习笔记与大厂面试题

Original: https://www.cnblogs.com/aqsaycode/p/14920526.html
Author: 阿Q说代码
Title: Battle:你会TLAB,我会逃逸分析

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/683657/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球