JVM 系列 - 内存区域 - Java 堆(五)

2018-12-06  本文已影响0人  _晓__

特点

Java 堆会出现的异常

运行时数据区

运行时数据区

JIT 编译器

即时编译器(Just-in-time Compilation,JIT)

什么是热点代码

如何检测热点代码

HotSpot 虚拟器为每个方法准备了两类计数器:方法调用计数器和回边计数器,两个计数器都有一定的阈值,超过阈值就会触发JIT 编译。
-XX:CompileThreshold 可以设置阈值大小,Client 编译器模式下,阈值默认的值1500,而 Server 编译器模式下,阈值默认的值则是10000。

方法调用计数器
回边计数器

逃逸分析

对象的三种逃逸状态

private Object o;

/**
 * 给全局变量赋值,发生逃逸(GlobalEscape)
 */
public void globalVariablePointerEscape() {
    o = new Object();
}

/**
 * 方法返回值,发生逃逸(GlobalEscape)
 */
public Object methodPointerEscape() {
    return new Object();
}

/**
 * 实例引用传递,发生逃逸(ArgEscape)
 */
public void instancePassPointerEscape() {
    Object o = methodPointerEscape();
}

/**
 * 没有发生逃逸(NoEscape)
 */
public void noEscape() {
    Object o = new Object();
}

配置逃逸分析

标量替换

-XX:+EliminateAllocations 可以开启标量替换
-XX:+PrintEliminateAllocations 查看标量替换情况(Server VM 非Product 版本支持)

栈上分配

栈上分配的技术基础是逃逸分析和标量替换。使用逃逸分析确认方法内局部变量对象(未发生逃逸,线程私有的对象,指的是不可能被其他线程访问的对象)不会被外部访问,通过标量替换将该对象分解在栈上分配内存,不用在堆中分配,分配完成后,继续在调用栈内执行,最后线程结束,栈空间被回收,局部变量对象也被回收。方法执行完后自动销毁,而不需要垃圾回收的介入,减轻 GC 压力,从而提高系统性能。

使用场景:对于大量的零散小对象,栈上分配提供了一种很好的对象分配策略,栈上分配的速度快,并且可以有效地避免垃圾回收带来的负面的影响,但由于和堆空间相比,栈空间比较小,因此对于大对象无法也不适合在栈上进行分配。

测试栈上分配:

public static void alloc() {
    byte[] b = new byte[2];
    b[0] = 1;
}

public static void main(String[] args) {
    long timeMillis = System.currentTimeMillis();
    for (int i = 0; i < 100000000; i++) {
         alloc();
    }
    // 开启使用栈上分配执行时间 6 ms左右
    // 关闭使用栈上分配执行时间 900 ms左右
    System.out.println(System.currentTimeMillis() - timeMillis);
}

同步消除

线程同步本身比较耗,如果确定一个对象不会逃逸出线程,无法被其它线程访问到,那该对象的读写就不会存在竞争,对这个变量的同步措施就可以消除掉。单线程中是没有锁竞争。(锁和锁块内的对象不会逃逸出线程就可以把这个同步块取消)

测试同步消除:

public static void alloc() {
    byte[] b = new byte[2];
    synchronized (b) {
         b[0] = 1;
    }
}

public static void main(String[] args) {
    long timeMillis = System.currentTimeMillis();
    for (int i = 0; i < 100000000; i++) {
         alloc();
    }
    // 开启使用同步消除执行时间 10 ms左右
    // 关闭使用同步消除执行时间 3870 ms左右
    System.out.println(System.currentTimeMillis() - timeMillis);
}

TLAB

扩展

虚拟机对象分配流程:首先如果开启栈上分配,JVM 会先进行栈上分配,如果没有开启栈上分配或则不符合条件的则会进行 TLAB 分配,如果 TLAB 分配不成功,再尝试在 Eden 区分配,如果对象满足了直接进入老年代的条件,那就直接分配在老年代。


虚拟机对象分配流程
上一篇下一篇

猜你喜欢

热点阅读