Java是一门面向对象的语言,在Java程序运行的过程中无时无刻都有对象被创建出来,根据Java虚拟机规范,所有的对象实例和数组都在堆中分配(实际并不是).那么在HotSpot虚拟机中是如何在Java堆上分配内存的呢?
1.对象的分配方式
1.指针碰撞
假设Java堆中的内存是绝对规整的,所有被使用过的内存被放在一边,未被使用过的内存被放在另一边,中间放着一个指针作为分隔线.那么未对象分配内存,只需要把指针往空闲内存方向移动与对象大小相当的距离就行了。这种分配方式就叫做指针碰撞
2.空闲列表
如果Java对并不是绝对规整的,使用过的内存和未使用过的内存交织在一起,这样就没有办法进行简单地进行指针碰撞了,所以HotSpot虚拟机就维护了一个空闲列表,用于记录哪些内存块是可用的,哪些内存块是不可用的.当为对象分配内存时,只需要从空闲列表中找出一块足够大的内存划分给对象实例,同时更新空闲列表上面的记录。这种分配方式就叫做空闲列表.
一般来说指针碰撞是比较简单高效的,空闲列表是相对比较复杂的.
按理来说,HotSpot虚拟机一个采用指针碰撞方式来为对象分配内存.
但实际上并非如此.具体使用哪个方式来为对象分配内存,主要取决于Java堆是否规整,而Java堆是否规整又由所采用的垃圾收集器来决定的(具体的垃圾收集器相关的知识这里并不详细说明),我认为啊,我单纯的认为Java堆是否规整取决于所采用的垃圾收集器具体是采用什么垃圾收集算法.
2.分配时的安全问题
问题所在: 对象创建在虚拟机中是非常频繁的行为,即使仅仅修改一个指针所指向的位置,在并发情况下也不一定是线程安全的.例如:当正在给A对象分配内存时,还未移动指针,对象B又使用原来的指针来分配内存.
那么如何解决在为对象分配内存时的安全问题呢?
1.对分配内存的动作进行同步处理——实际上HotSpot虚拟机是采用CAS配上失败重试的方式来保证更新操作的原子性(按照我的理解,就是自选CAS,循环进行CAS操作,直到成功为止). 这里有可能不知道CAS是什么,我这里简单的介绍一下CAS.JDK文档对CAS的说明如下:如果当前状态值等于期望值,那么就以原子方式将状态设置为给定的更新值.
2.另外一种是预先为每一个线程配置一块内存,这块内存被称之为本地线程分配缓冲(Thread Loacl Alloaction Buffer,TLAB),哪个线程要为对象分配内存,就在自己的TLAB中分配。当线程的TLAB分配完之后,要重新为该线程配置一块内存,注意,在为线程重新分配TLAB时要对该动作进行同步处理.
最后这里说明:本篇文章大量语句都是来自于<<深入理解Java虚拟机>>这本书.