ps:Java中通过new指令创建对象,当jvm收到一条new指令时,会首先检查常量池中是否可以定位到指定类的符号引用;如果可以,则表示对应类已经加载到jvm中,否则就需要先进行类加载的操作。

类检查过程完成以后,会为新生的对象分配内存空间。对象所需的内存空间大小在类加载完成之后随即确定。

一.对象内存空间分配

受堆空间是否规整影响。而堆空间是否规整 ,取决于JVM采用的垃圾回收机制以及是否有压缩整理功能。

分为以下两种方式分配:

  • 指针碰撞(堆空间是规整时
    即已经分配的内存与未使用的内存都是连续的空间,此时存在着一个指针位于已用与可用内存的分界。指针向空闲空间的方向挪动一段与对象大小相等的距离即可,此方式称为指针碰撞。
    由于堆空间是共享的,所以存在并发,有以下解决方法:
    (1)对内存分配的动作进行同步处理;
    (2) tlab 本地线程分配缓冲(LTAB,Thread Local Allocation Buffer)。是一个线程专用的内存分配区域。每个线程在堆中预先申请一块内存(tlab),哪个线程需要分配空间,就在哪个线程的tlab上执行。只有当某一线程的tlab用完了才执行同步锁定。
  • 空闲列表 Free List(堆空间是不规整,使用过的与未使用的内存空间相互交错时)
    JVM 堆内存中维护了一份可用内存的列表。当需要分配内存时,会在空闲列表中确定足够大小的内存空间,分配给对象实例,并更新列表中的记录。

因此,使用Serial、ParNew等用Compact过程的垃圾收集器时,采用“指针碰撞” 方式进行对象实例内存分配,而使用像CMS这种基于MARK_SWEEP垃圾回收算法的垃圾收集器时,JVM则采用“空闲列表”为对象实例进行内存分配。

二.TLAB介绍

TLAB的全称是Thread Local Allocation Buffer,即线程本地分配缓存区,这是一个线程专用的内存分配区域。
如果设置了虚拟机参数 -XX:UseTLAB,在线程初始化时,同时也会申请一块指定大小的内存,只给当前线程使用,这样每个线程都单独拥有一个空间,如果需要分配内存,就在自己的空间上分配,这样就不存在竞争的情况,可以大大提升分配效率。
TLAB空间的内存非常小,缺省情况下仅占有整个Eden空间的1%,也可以通过选项-XX:TLABWasteTargetPercent设置TLAB空间所占用Eden空间的百分比大小。

TLAB的本质其实是三个指针管理的区域:start,top 和 end,每个线程都会从Eden分配一块空间,例如说100KB,作为自己的TLAB,其中 start 和 end 是占位用的,标识出 eden 里被这个 TLAB 所管理的区域,卡住eden里的一块空间不让其它线程来这里分配。
TLAB只是让每个线程有私有的分配指针,但底下存对象的内存空间还是给所有线程访问的,只是其它线程无法在这个区域分配而已。可以理解为 ”线程私有分配区“
当一个TLAB用满(分配指针top撞上分配极限end了),就新申请一个TLAB,而在老TLAB里的对象还留在原地什么都不用管——它们无法感知自己是否是曾经从TLAB分配出来的,而只关心自己是在eden里分配的。

参考:《深入理解Java虚拟机:JVM高级特性与最佳实践》