栈分配、逃逸分析与TLAB -JVM

最近在学习JVM , 在看到这块地方 , 做一个简化的日志记录 , 部分内容抄录 。
一般认为new出来的对象都是被分配在堆上 , 但是这个结论不是那么的绝对 , 通过对Java对象分配的过程分析 , 可以知道有两个地方会导致Java中new出来的对象并一定分别在所认为的堆上 。这两个点分别是Java中的逃逸分析和TLAB( Local) 。
栈上分配
在JVM中 , 堆是线程共享的 , 因此堆上的对象对于各个线程都是共享和可见的 , 只要持有对象的引用 , 就可以访问堆中存储的对象数据 。虚拟机的垃圾收集系统可以回收堆中不再使用的对象 , 但对于垃圾收集器来说 , 无论筛选可回收对象 , 还是回收和整理内存都需要耗费时间 。
如果确定一个对象的作用域不会逃逸出方法之外 , 那可以将这个对象分配在栈上 , 这样 , 对象所占用的内存空间就可以随栈帧出栈而销毁 。在一般应用中 , 不会逃逸的局部对象所占的比例很大 , 如果能使用栈上分配 , 那大量的对象就会随着方法的结束而自动销毁了 , 无须通过垃圾收集器回收 , 可以减小垃圾收集器的负载 。
JVM允许将线程私有的对象打散分配在栈上 , 而不是分配在堆上 。分配在栈上的好处是可以在函数调用结束后自行销毁 , 而不需要垃圾回收器的介入 , 从而提高系统性能 。
栈上分配的技术基础:
一是逃逸分析:逃逸分析的目的是判断对象的作用域是否有可能逃逸出函数体 。
二是标量替换:允许将对象打散分配在栈上 , 比如若一个对象拥有两个字段 , 会将这两个字段视作局部变量进行分配 。
只能在模式下才能启用逃逸分析 , 参数-XX:启用逃逸分析 , 参数-XX:+开启标量替换(默认打开) 。Java SE 6u23版本之后 , 中默认就开启了逃逸分析 , 可以通过选项-XX:+查看逃逸分析的筛选结果 。
逃逸分析 定义
逃逸分析 , 是一种可以有效减少Java 程序中同步负载和内存堆分配压力的跨函数全局数据流分析算法 。通过逃逸分析 , Java 编译器能够分析出一个新的对象的引用的使用范围从而决定是否要将这个对象分配到堆上 。
在计算机语言编译器优化原理中 , 逃逸分析是指分析指针动态范围的方法 , 它同编译器优化原理的指针分析和外形分析相关联 。当变量(或者对象)在方法中分配后 , 其指针有可能被返回或者被全局引用 , 这样就会被其他过程或者线程所引用 , 这种现象称作指针(或者引用)的逃逸() 。
Java在Java SE 6u23以及以后的版本中支持并默认开启了逃逸分析的选项 。Java的JIT编译器 , 能够在方法重载或者动态加载代码的时候对代码进行逃逸分析 , 同时Java对象在堆上分配和内置线程的特点使得逃逸分析成Java的重要功能 。
举个例子
只说定义有点抽象 , 举个小例子:
public StringBuilder escapeTest(String valOne, String vaTwo) {StringBuilder stringBuilder = new StringBuilder();stringBuilder.append(valOne);stringBuilder.append(vaTwo);return stringBuilder;}public static void main(String[] args) {TlabDemo tlabDemo = new TlabDemo();StringBuilder ret1 = tlabDemo.escapeTest("a","b");System.out.println(ret1.toString());ret1.append("c");System.out.println(ret1.toString());}
是在方法的内部变量 , 而此时它被直接返回 , 这样就有可能被其他地方的方法或参数所改变 , 这样它的作用域就不只是了 , 虽然它是一个局部变量 , 但其发生了“逃逸” , 属于方法逃逸