学习笔记:java对象内存分配问题

我们在写java代码创建每一种对象后,jvm是怎么给它们分配内存的呢?

上图:

分配原则

一般来讲,new一个对象后,内存一般分配在堆空间中,但也有一些例外。有些对象会分配在栈上或者TLAB中。如果可以在栈上分配,就直接在栈上分配,不行就会进行TLAB分配,再不行就判断是否是大对象,大对象直接进入老年代,再不行就分配到eden区,eden若是空间不够,就会进行一次MinorGC。

大对象

顾名思义就是很大的对象,需要大量的连续内存空间,jvm会让这种对象直接进入老年代,减少eden和两个survivor区发生大量的内存复制,提高效率。

栈上分配

大家都知道,在jvm中堆是线程共享的,也就是说堆里存的东西对于所有的线程都是可见的,可访问的,虚拟机中的垃圾回收才可以回收堆中的没有被引用的对象。但是,若是有对象的作用域不会逃离方法之外,那么,这个对象就可以分配在栈中。随着方法的结束而销毁,无需回收。这就是栈上分配

TLAB分配

本地线程分配缓冲(Thread Local Allocation Buffer即TLAB,为每⼀个线程预先分配⼀块内存,JVM在给线程中的对象分配内存时,⾸先在TLAB分配。

由于对象一般会分配在堆上,而堆是全局共享的。因此在同一时间,可能会有多个线程在堆上申请空间。为了保证同一快内存的线程安全JVM有两种方式

一:CAS,⽐较和交换(Compare And Swap): CAS 是乐观锁的⼀种实现⽅式。所谓乐观锁就是,每次不加锁⽽是假设没有冲突⽽去完成某项操作,如果因为冲突失败就重试,直到成功为⽌。虚拟机采⽤ CAS 配上失败重试的⽅式保证更新操作的原⼦性。

但是很多线程同时申请内存时。CAS效率就会变得低下,所以,JVM在给线程中的对象分配内存时,⾸先在TLAB分配,当对象⼤于TLAB中的剩余内存或TLAB的内存已⽤尽时,再采⽤上述的CAS进⾏内存分配。

在给对象分配内存时,每个线程使用自己的TLAB,这样可以避免线程同步,提高了对象分配的效率。  TLAB本身占用eEden区空间,在开启TLAB的情况下,虚拟机会为每个Java线程分配一块TLAB空间。TLAB空间的内存非常小, 缺省情况下仅占有整个Eden空间的1%,  由于TLAB空间一般不会很大,因此大对象无法在TLAB上进行分配,总是会直接分配在堆上。TLAB空间由于比较小,因此很容易装满。

比如,一个100K的空间,已经使用了80KB, 当需要再分配一个30KB的对象时,肯定就无能为力了。这时虚拟机会有两种选择,第一,废弃当前TLAB,这样就会浪费20KB空间;第二,将这30KB的对象直接分配在堆上,保留当前的TLAB。

个人学习整理,有错欢迎纠正。

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20201105A0II1M00?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码关注腾讯云开发者

领取腾讯云代金券