Java对象创建时,如何进行内存分配?

1、对象的创建流程

  • 根据逃逸分析,判断是否可以栈上分配,不满足则进行堆上分配
  • 判断是否为大对象,如果是大对象,在老年代进行内存分配,不满足则进行新生代分配,
  • 首先尝试分配到TLAB(Thread Local Allocation Buffer),如果TLAB足够,则分配成功。
  • 根据碰撞指针或空闲列表记录的可用地址,采用CAS的方式在新生代中分配内存,解决分配内存时并发问题

在这里插入图片描述

2、什么是逃逸分析

1、逃逸分析概念

通俗理解:如果一个对象在一个方法内定义,如果被方法外部的引用所指向,那认为它逃逸了。否者,这个对象,没有发生逃逸。

public void test(){
    Object obj =  new Object(); //未发生逃逸,只在该方法内,进行逃逸分析就是为这种对象做优化
}
2、逃逸分析有什么用?

逃逸分析好处就是:对不能逃逸的对象做优化

1、栈上分配

在栈上分配空间给新创建的对象,其实目前Hotspot并没有实现真正意义上的栈上分配,实际上是标量替换。(广义上的栈上分配)

2、标量替换

标量替换:即时编译可以将对象打散,将对象替换为一个个很小的局部变量,就可以存放在方法栈帧的局部变量表中,也就实现了栈上分配

private static void alloc() {
    Point point = new Point(1,2);
    System.out.println("point.x" + point.x + ";point.y" + point.y);
}
//标量替换后:
private static void alloc() {
    int x = 1;
    int y = 2;
    System.out.println("point.x = " + x + "; point.y=" + y);
}
  • 标量 :不可再分,基本数据类型
  • 聚合量: 可再分,引用数据类型

将对象替换为一个个局部变量后,就可以非常方便的在栈上进行分配了。为当前线程在虚拟机栈中分配一块空间,这块空间称为Java线程栈,里面存放的都是方法栈帧,一个栈帧代表的就是一个函数的调用,包含局部变量表、操作栈、动态连接、返回地址。

  • 局部变量表:存放基本类型,引用类型在堆中的地址,或者是方法的返回地址等
  • 操作栈 :存放计算过程中的中间结果,同时作为计算过程中变量临时的存储空间
  • 动态连接: 指向了运行时常量池中方法的地址,当你要调用某一方法可以通过动态链接找到
  • 方法出口:记录方法结束后,继续运行下一个栈帧对应的哪个方法哪行代码
3、锁消除

如果堆上的共享数据不可能逃逸出去被其它线程访问到,那么就可以把它们当成私有数据对待,也就可以将它们的锁进行消除

public class TestLockEliminate {
    public static String getString(String s1, String s2) {
        StringBuffer sb = new StringBuffer();
        sb.append(s1);
        sb.append(s2);
        return sb.toString();
    }
 
    public static void main(String[] args) {
        long tsStart = System.currentTimeMillis();
        for (int i = 0; i < 1000000; i++) {
            getString("TestLockEliminate ", "Suffix");
        }
        System.out.println("一共耗费:" + (System.currentTimeMillis() - tsStart) + " ms");
    }
}

上述代码中的StringBuffer.append是一个同步操作,但是StringBuffer却是一个局部变量,并且方法也并没有把StringBuffer返回,所以不可能会有多线程去访问它。那么此时StringBuffer中的同步操作就是没有意义的。会进行锁消除

3、什么是TLAB

TLAB 是JVM在伊甸园区为每个线程划分出来的一块专用空间,如果需要分配内存,就在自己的空间上分配,这样就不存在竞争的情况,可以大大提高分配效率

虽然每个线程在初始化时都会去堆内存中申请一块 TLAB,并不是说这个 TLAB 区域的内存其他线程就完全无法访问了,其他线程的读取还是可以的,只不过无法在这个区域中分配内存而已

当 TLAB 剩余空间不足时:

  1. 如果剩余空间大于refill_waste,就会放弃在TLAB上分配内存,直接在堆内存中分配。
  2. 如果剩余空间小于refill_waste,那么这个 TLAB 会被退回 Eden,重新申请一个新的TLAB。同时将旧TLAB中剩余可用的空间用一个 dummy object 填充满

在这里插入图片描述

为什么需要dummy object 填充?

主要为了提升GC扫描效率,由于 TLAB 仅线程内知道哪些被分配了,外部并不知道哪一部分被使用哪一部分没有,需要做额外的检查,那么会影响 GC 扫描效率。但是填充 dummy 也造成了空间的浪费,这种浪费不能太多,所以通过最大浪费空间限制来限制这种浪费。

4、什么是空闲列表和碰撞指针

  • 碰撞指针:通过一个指针作为分界点,需要分配内存时,仅需把指针往空闲的一端移动与对象大小相等的距离,分配效率较高
  • 空闲列表:通过额外的存储记录空闲的地址,将随机 IO 变为顺序 IO,但带来了额外的空间消耗。

在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值