本文共 1917 字,大约阅读时间需要 6 分钟。
JVM中一般情况下对象都是优先分配在堆空间的Eden区,但是有些情况下也是可以进行一些分配上的优化,本文通过两个小案例,演示一下TLAB分配和栈上分配的优化,测试版本JDK1.8。
TLAB:本地线程分配缓冲,内存分配实际上被按照不同的线程划分在不同的内存之间进行,每个线程在Eden区中中有一块独享的小区域,这样做的好处是可以减少同步处理带来的性能消耗。
下面的小案例中启动了100个线程,如果没有TLAB优化,那么启动的线程越多,对象分配时的同步处理就越耗时。
首先配置如下参数启动,-XX:-UseTLAB -XX:-DoEscapeAnalysis,表示关闭TLAB分配,关闭逃逸分析,确保对象只能在堆上分配。
public class TestAlloc { class User { } void alloc() { new User(); } public static void main(String[] args) throws InterruptedException { TestAlloc t = new TestAlloc(); CountDownLatch countDownLatch = new CountDownLatch(100); long start = System.currentTimeMillis(); for (int i = 0; i < 100; i++) { new Thread(() -> { for (int j = 0; j < 10_0000; j++) { t.alloc(); } countDownLatch.countDown(); }).start(); } countDownLatch.await(); long end = System.currentTimeMillis(); System.out.println(end - start); }}
执行耗时大约1秒。
修改配置参数启动,-XX:+UseTLAB -XX:-DoEscapeAnalysis,表示开启TLAB分配,关闭逃逸分析,确保对象只能在堆上分配。
执行耗时大约100毫秒,性能差距大约10倍。
几乎所有的对象都是分配在堆内存中,但是还有一种比较特殊的分配方式是分配在栈上,这是借助于逃逸分析来辅助实现的,逃逸分析中指出如果对象的作用域不会逃出方法或者线程之外,也就是无法通过其他途径访问到这个对象,那么就可以对这个对象采取一定程度的优化,这其中就包含了:栈上分配。
栈上分配的好处在于,对象可以随着栈的出栈过程被自然的销毁,节省了堆中垃圾回收所消耗的性能。
还是同样的案例,alloc方法中new出来的User对象,作用域只在改方法中,所以可以通过逃逸分析的结果,实现栈上分配。对象优先栈上分配,所以TLAB是否开启不影响,使用默认配置就行。
首先配置如下参数启动, -XX:-DoEscapeAnalysis,关闭逃逸分析。
public class TestAlloc { class User { } void alloc() { new User(); } public static void main(String[] args) { TestAlloc t = new TestAlloc(); long start = System.currentTimeMillis(); for (int j = 0; j < 1_0000_0000; j++) { t.alloc(); } long end = System.currentTimeMillis(); System.out.println(end - start); }}
执行耗时大约300毫秒。
当配置开启逃逸分析时 -XX:+DoEscapeAnalysis,执行耗时只有10毫秒左右。
转载地址:http://lllrb.baihongyu.com/