1. Java对象分配流程

    JVM之对象分配:栈上分配 & TLAB分配-LMLPHP

2. 栈上分配

    2.1 本质:Java虚拟机提供的一项优化技术

    2.2 基本思想: 将线程私有的对象打散分配

    2.3 优点:

       2.3.1 可以在函数调用结束后自行销毁对象,不需要垃圾回收器的介入,有效避免垃圾回收带来的负面影响

       2.3.2 栈上分配速度快,提高系统性能

    2.4 局限性: 栈空间小,对于大对象无法实现栈上分配

    2.4 技术基础: 逃逸分析

        2.4.1 逃逸分析的目的: 判断对象的作用域是否超出函数体[即:判断是否逃逸出函数体]

//user的作用域超出了函数setUser的范围,是逃逸对象
//当函数结束调用时,不会自行销毁user
private User user;
public void setUser(){
    user = new User();
    user.setId(1);
    user.setName("blueStarWei");
}

//u只在函数内部生效,不是逃逸对象
//当函数调用结束,会自行销毁对象u
public void createUser(){
    User u = new User();
    u.setId(2);
    u.setName("JVM");
}

    2.5 栈上分配示例

package com.blueStarWei.templet;

public class AllotOnStack {

    public static void main(String[] args) {
        long start = System.currentTimeMillis();
        for (int i = 0; i < 100000000; i++) {
            alloc();
        }
        long end = System.currentTimeMillis();
        System.out.println(end - start);
    }

    private static void alloc() {
        User user = new User();
        user.setId(1);
        user.setName("blueStarWei");
    }
}

        2.5.1 上述代码调用了1亿次alloc(),如果是分配到堆上,大概需要1.5GB的堆空间,如果堆空间小于该值,必然会触发GC。

        2.5.2 使用如下参数运行,发现不会触发GC

-server -Xmx15m -Xms15m -XX:+DoEscapeAnalysis -XX:+PrintGC -XX:-UseTLAB -XX:+EliminateAllocations

       2.5.3 使用如下参数(任意一行)运行,会发现触大量GC

//不使用逃逸分析
-server -Xmx15m -Xms15m -XX:-DoEscapeAnalysis -XX:+PrintGC -XX:-UseTLAB -XX:+EliminateAllocations

//不使用标量替换
-server -Xmx15m -Xms15m -XX:+DoEscapeAnalysis -XX:+PrintGC -XX:-UseTLAB -XX:-EliminateAllocations

        2.5.3.1 可以发现:栈上分配依赖于逃逸分析标量替换 

    2.5.4 GC日志

[GC (Allocation Failure)  4095K->528K(15872K), 0.0025208 secs]
[GC (Allocation Failure)  4624K->552K(15872K), 0.0012518 secs]
[GC (Allocation Failure)  4648K->608K(15872K), 0.0009262 secs]
......(省略)
3718

        2.5.4.1 GC日志解析

      2.5.5 JVM参数解析

3. TLAB 分配

  TLAB,全称Thread Local Allocation Buffer, 即:线程本地分配缓存。这是一块线程专用的内存分配区域TLAB占用的是eden区的空间。在TLAB启用的情况下(默认开启),JVM会为每一个线程分配一块TLAB区域。

    3.1 为什么需要TLAB?

  这是为了加速对象的分配。由于对象一般分配在堆上,而堆是线程共用的,因此可能会有多个线程在堆上申请空间,而每一次的对象分配都必须线程同步,会使分配的效率下降。考虑到对象分配几乎是Java中最常用的操作,因此JVM使用了TLAB这样的线程专有区域来避免多线程冲突,提高对象分配的效率。

    3.2 局限性: TLAB空间一般不会太大(占用eden区),所以大对象无法进行TLAB分配,只能直接分配到堆上。

    3.3 分配策略:

  一个100KB的TLAB区域,如果已经使用了80KB,当需要分配一个30KB的对象时,TLAB是如何分配的呢?

    此时,虚拟机有两种选择:第一,废弃当前的TLAB(会浪费20KB的空3.4 间);第二,将这个30KB的对象直接分配到堆上,保留当前TLAB(当有小于20KB的对象请求TLAB分配时可以直接使用该TLAB区域)。

  JVM选择的策略是:在虚拟机内部维护一个叫refill_waste的值,当请求对象大于refill_waste时,会选择在堆中分配,反之,则会废弃当前TLAB,新建TLAB来分配新对象。

  【默认情况下,TLAB和refill_waste都是会在运行时不断调整的,使系统的运行状态达到最优。】

    3.4 JVM参数解析


4. 附件 

    4.1 User类

packagepackag  com.blueStarWei.templet;

public class User {

    private int id;

    private String name;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }


}
 

5. 参考文献

    5.1 《实战Java虚拟机 - JVM故障诊断与性能优化》

    5.2  栈上分配、TLAB : https://blog.csdn.net/yangsnow_rain_wind/article/details/80434323

    5.3  快速解读GC日志 : https://blog.csdn.net/renfufei/article/details/49230943

01-09 00:49