深入解析Java对象分配:逃逸分析与TLAB的详细介绍及应用

Java中的所有对象都分配在堆上吗?您错了!

深入解析Java对象分配:逃逸分析与TLAB的详细介绍及应用

更新时间:2019年6月19日 14:39:09 作者:Lovnx

本文主要介绍Java中的对象是否分配在堆上?您错了!文章通过示例代码对其进行了详细的介绍,对于大家的学习或者工作具有一定的参考学习价值。有需要的朋友可以参考一下。

前言

在学习使用Java的过程中,我们一般认为新的对象是在堆上分配的,但这个结论并不是那么绝对。通过对Java对象分配过程的分析,我们可以知道Java中有两个地方会出现问题。 new带出来的对象并不一定像想象的那样在堆上。这两点就是Java中的逃逸分析和TLAB(Thread Local Allocation Buffer)。本文首先介绍两者,然后介绍Java对象分配过程。

1. 逃逸分析

1.1 逃逸分析的定义

逃逸分析是一种跨功能的全局数据流分析算法,可以有效降低Java程序中的同步负载和内存堆分配压力。通过逃逸分析,Java Hotspot编译器可以分析一个新对象的引用的使用范围,并决定是否将该对象分配到堆上。

在计算机语言编译器优化原理中,逃逸分析是指分析指针动态范围的方法。与编译器优化原理的指针分析和形状分析有关。当一个变量(或对象)在方法中分配时,它的指针可能会被返回或全局引用,以便它可以被其他进程或线程引用。这种现象称为指针(或引用)逃逸。

Java 在 Java SE 6u23 及更高版本中默认支持并启用转义分析选项。 Java的HotSpot JIT编译器可以在方法重载或动态加载代码时对代码进行逃逸分析。同时,Java对象分配在堆上和内置线程的特性使得逃逸分析成为Java的重要功能。

1.2 逃逸分析方法

Java Hotspot 编译器使用

[普通] 查看普通副本Choi JD、Gupta M、Serrano M 等人。 Java的逃逸分析[J]. Acm Sigplan 通知,1999 年,34(10):1-19。

Jong-Deok Choi、Manish Gupta、Mauricio Seffano、Vugranam C. Sreedhar、Sam Midkiff 等人使用“Escape Analysis for Java”论文中描述的算法进行了逃逸分析。该算法引入连通图,利用连通图构造对象与对象引用之间的可达关系,并在此基础上提出组合数据流分析方法。由于该算法是上下文敏感和流敏感的,并且模拟了任意层次对象的嵌套关系,因此分析精度较高,但运行时间和内存消耗较大。

大多数逃逸分析的实现都是基于所谓的“封闭世界”的前提:所有可能执行的方法在逃逸分析之前就已经已知,程序的实际运行不会改变它们。他们之间的调用关系。但当真正的Java程序运行时,这个假设就不成立了。 Java程序的很多特性,比如动态类加载、调用本地函数、反射程序调用等,都会打破所谓的“封闭世界”约定。

无论是在“封闭世界”还是“开放世界”,逃逸分析作为一种算法而非编程语言,吸引了大量国内外学者对其进行研究。

1.3 逃逸分析后的处理

经过逃逸分析,可以获得三个物体的逃逸状态。

GlobalEscape(全局转义),即对象的引用从方法或线程中转义。例如,将对象引用复制到类变量中,或者存储在转义对象中,或者将对象引用作为方法的返回值返回给调用方法。

ArgEscape(参数级转义),是在方法调用过程中将对象传递给方法的应用。这种状态可以通过分析被调用方法的二进制代码来确定。

NoEscape(无法转义),可以用标量替换的对象。此类对象不需要在传统堆上分配。

编译器可以使用逃逸分析的结果来优化程序。

堆分配的对象成为堆栈分配的对象。如果方法中的对象引用没有转义,则该方法可能会分配在堆栈内存上,而不是公共堆内存上。

消除同步。线程同步的成本相当高,同步的后果是降低了并发性和性能。逃逸分析可以判断一个对象是否总是只被一个线程访问。如果只是一个线程访问,那么对对象的同步操作就可以转化为无同步保护的操作,这样可以大大提高并发程度和性能。

矢量替代方案。如果逃逸分析方法发现对象的内存存储结构不需要继续,则可以将对象的部分甚至全部保存在CPU寄存器中,这样可以大大提高访问速度。

下面,我们看一个逃逸分析的例子。

class Main { 
public static void main(String[] args) { 
example(); 
} 
public static void example() { 
Foo foo = new Foo(); //alloc 
Bar bar = new Bar(); //alloc 
bar.setFoo(foo); 
} 
} 
class Foo {} 
class Bar { 
private Foo foo; 
public void setFoo(Foo foo) { 
this.foo = foo; 
} 
} 

在这个例子中,我们创建了两个对象,Foo对象和Bar对象,并将Foo对象的应用程序分配给Bar对象的方法。此时,如果Bar对在堆上,就会导致Foo对象逃逸。然而,在这种情况下,通过逃逸分析,编译器可以知道Bar对象没有逃逸example()方法,所以这也意味着Foo也没有逃逸。转义示例方法。因此,编译器可以在堆栈上分配这两个对象。

1.4 逃逸分析对编译器的影响

测试代码:

package com.yang.test2; 
/** 
* Created by yangzl2008 on 2015/1/29. 
*/ 
class EscapeAnalysis { 
private static class Foo { 
private int x; 
private static int counter; 
public Foo() { 
x = (++counter); 
} 
} 
public static void main(String[] args) { 
long start = System.nanoTime(); 
for (int i = 0; i < 1000 * 1000 * 10; ++i) { 
Foo foo = new Foo(); 
} 
long end = System.nanoTime(); 
System.out.println("Time cost is " + (end - start)); 
} 
} 

设置JVM运行参数:

逃逸分析未开启,设置为:

-服务器-详细:gc

打开逃逸分析并将其设置为:

-server -verbose:gc -XX:+DoEscapeAnalysis

不进行逃逸分析的运行情况如下:

[GC 5376K->427K(63872K), 0.0006051 secs] 
[GC 5803K->427K(63872K), 0.0003928 secs] 
[GC 5803K->427K(63872K), 0.0003639 secs] 
[GC 5803K->427K(69248K), 0.0003770 secs] 
[GC 11179K->427K(69248K), 0.0003987 secs] 
[GC 11179K->427K(79552K), 0.0003817 secs] 
[GC 21931K->399K(79552K), 0.0004342 secs] 
[GC 21903K->399K(101120K), 0.0002175 secs] 
[GC 43343K->399K(101184K), 0.0001421 secs] 
Time cost is 58514571 

当逃逸分析开启时,运行情况如下:

时间成本为10031306

当未启用逃逸分析且运行上诉代码时,JVM 会执行 GC 操作。但是,当启用逃逸分析时,JVM 不会执行 GC 操作。同时,在运行时间上,开启逃逸分析的程序运行时间是未开启逃逸分析的时间的1/5。

2.TLAB

JVM在新的内存生成Eden Space中开辟了一个小的线程私有区域,称为TLAB(Thread-local Allocation buffer)。默认设置是占用Eden Space的1%。 Java程序中的许多对象都是小对象,使用后就被扔掉。它们不被线程共享,适合快速GC。因此,对于小对象,JVM通常会先在TLAB上分配,并且TLAB上的分配是线程私有的。没有锁开销。因此,在实践中,分配多个小对象通常比分配一个大对象更高效。

换句话说,Java中的每个线程都会有自己的缓冲区,称为TLAB(线程本地分配缓冲区)。每个TLAB只能由一个线程操作。 TLAB结合指针碰撞技术可以实现快速对象分配。 ,不需要任何锁进行同步,即分配对象时,不需要锁定整个堆,而只需要在自己的缓冲区中分配。

对象分配的JDK源码可以参考JVM Java对象创建【初始化】中OpenJDK源码的分析。

3. Java对象分配的过程

编译器使用逃逸分析来确定对象是分配在堆栈上还是堆上。如果分配在堆上,请转到选项 2。

如果 tlab_top + 大小

你可能想看:
« 上一篇
下一篇 »

相关推荐

java中什么是冒泡,冒泡算法

2026年09月06日

28阅读

java中冒号,java双冒号

2026年09月06日

26阅读

java中的->,java中的是什么意思

2026年09月06日

26阅读

java中的super用法,Java中super的说明

2026年09月06日

28阅读

java中反射的应用,java中反射的应用场景

2026年09月06日

26阅读

java中开关的定义,Java关键字有哪些

2026年09月06日

26阅读