java对象创建与内存分配

对象的创建:

1、类加载检查:

        当jvm遇到new指令时,会先在常量池中检查能定位到一个该类的符号引用,并检查该符号引用代表的类是否已经被加载过,如果没有则需要先执行相应的类的加载过程

2、内存分配:

        当类被加载过,就需要为该新对象分配内存空间。该对象所需内存空间大小,在类加载完成后就可以确定。

内存分配方式:

        指针碰撞:

        如果堆内存空间是绝对规整的,已被占用内存在一端,未被占用内存放在另一端,中间以指针分隔,那么分配内存只是把指针分割器向未被占用一端挪动该新对象所需空间大小即可。

        空闲列表:

        如果堆内存空间是混乱的,那么根据虚拟机维护的一个列表分配该对象的内存空间。该列表记录了哪些内存块是可用的,jvm会在该列表上找到一块足够大的空间分配给该对象实例,并更新该列表。

        

并发问题解决方案:

        在并发情况下,可能会出现正在给A分配内存,指针还未修改;此时对象B又使用原来的指针分配内存。

        CAS:CAS+失败重试方式保证更新操作的原子性对分配内存空间的动作进行同步处理。

        本地线程分配缓冲(TLAB):每个线程会预先在堆中划分一小块空间,当新的对象创建时,就可以使用该内存空间进行分配。如果该内存放不下的话,再进行正常分配内存。

3、初始化零值

        将分配到的内存空间都初始化为零值(不包括对象头), 如果使用TLAB,这一工作过程也可以提前至TLAB分配时进行。这一步操作保证了对象的实例字段在Java代码中可以不赋初始值就直接使用,程序能访问到这些字段的数据类型所对应的零值。

4、设置对象头

        对对象头信息进行设置,包括GC分代年龄、对象哈希码、如何找到对象元数据信息、对象实例的归属等。对象在内存中存储的布局大致可以分为3块区域:对象头实例数据对齐填充

 32位对象头

    

64位对象头:

5、执行《init》方法:

        即对象按照程序员的意愿进行初始化。对应到语言层面上讲,就是为属性赋值(注意,这与上面的赋零值不同,这是由程序员赋的值),和执行构造方法。

对象的指针压缩:

        类型指针(Klass Pointer):即对象指向它的类型元数据的指针

        指针压缩的好处:
        1、在64位平台的HotSpot中使用32位指针(实际存储用64位),内存使用会多出1.5倍左右,使用较大指针在主内存和缓存之间移动数据,占用较大宽带,同时GC也会承受较大压力

        2、为了减少64位平台下内存的消耗,启用指针压缩功能
        3、在jvm中,32位地址最大支持4G内存(2的32次方),可以通过对对象指针的存入堆内存时压缩编码、取出到cpu寄存器后解码方式进行优化(对象指针在堆中是32位,在寄存器中是35位,2的35次方=32G),使得jvm只用32位地址就可以支持更大的内存配置(小于等于32G)
        4、堆内存小于4G时,不需要启用指针压缩,jvm会直接去除高32位地址,即使用低虚拟地址空间
        5、堆内存大于32G时,压缩指针会失效,会强制使用64位(即8字节)来对java对象寻址,这就会出现1的问题,所以堆内存不要大于32G为好

对齐填充的好处: 对于大部分处理器,对象以8字节整数倍来对齐填充都是最高效的存取方式。

对象栈上分配:

        正常情况下,对象的内存分配是对于堆而言的,但是有些特殊情况下会在线程栈上分配对象内存空间。比如:通过逃逸分析确定该对象不会被外部访问,那么该对象所占内存空间就可以随出栈而销毁,那么该对象就可以尝试先在栈上分配内存。

对象逃逸分析:就是分析对象动态作用域,当一个对象在方法中被定义后,它可能被外部方法所引用,例如作为调用参数传递到其他地方中。

public void test() {
   User user = new User();
   user.setId(1);
   user.setName("zhuge");
   //TODO 保存到数据库
   // 注:test方法中的user对象我们可以确定当方法结束这个对象就可以认为是无效对象了,对于这样的对象    
   //我们其实可以将其分配在栈内存里,让其在方法结束时跟随栈内存一起被回收掉。
}

标量替换:当确定JVM不会创建该对象,并且该对象可以进一步被分解时,将该对象成员变量分解若干个被这个方法使用的成员变量所代替,这些代替的成员变量在栈帧或寄存器上分配空间,这样就不会因为没有一大块连续空间导致对象内存不够分配。开启标量替换参数(-XX:+EliminateAllocations),JDK7之后默认开启。

总结栈分配对象内存:栈上分配依赖于逃逸分析和标量替换。

。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。

下期:对象堆分配,对象动态年龄判断机制、老年代空间担保机制

  • 32
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值