Java内存区域

一:java内存区域划分
Java内存区域

二:对象的创建
Java内存区域
在对象(这里的对象主要是指java普通对象,不包含数组以及class对象)创建过程中,为对象分配内存空间也就是把一块确定的内存从堆中划分出来。当java堆中的内存是规整的时候,也就是空闲内存在一起,而已使用的内存在一起,那么java虚拟机只需要用一个指针来分隔开这个界限,当要分配内存时,只需要将这个指针移动对象大小的位置就可以,这种内存分配方式称为“指针碰撞”。当java堆内存不规整时,就不能简单的靠一个指针来区分哪一部分内存是空闲,哪一部分内存是已使用,因此java虚拟机就需要维护一个列表,来记录区分已使用和空闲的内存空间,在分配内存时从列表中找到一个足够大的空间划分给对象实例,并更新列表上的记录,这种方式称为“空闲列表”。选择哪种分配方式是由java堆是否规整来决定的,而java堆内存是否规整,取决于垃圾回收器GC是否带有压缩整理功能决定。因此,在使用Serial,ParNew等带Compact过程的收集器时,系统采用的分配算法是指针碰撞,而使用CMS这种基于标记清除的算法的收集器时,通常采用空闲列表分配方式。

除了考虑内存分配方式之外,还需要考虑对象创建操作是否频繁,如果频繁,那么可能出现问题,比如在多线程情况下,采用指针碰撞分配内存,在还未来得及改一个线程分配内存的指针时,分配了内存,但指针还未来得及修改,另一个对象又用原来的指针位置去修改内存位置,这就造成了线程不安全的情况。因此,解决这个问题有两种方案,一种是虚拟机采用CAS机制配上失败重试的方式来保证更新操作的原子性;另一种就是把内存分配的动作按照线程划分在不同的空间之中进行,即每个线程在java堆中预先分配一小块内存,称为本地线程分配缓冲(TLAB),哪个线程要分配内存,就在哪个线程的TLAB上分配,只有TLAB用完之后再分配新的TLAB时,才需要同步锁定。虚拟机是否使用TLAB,可以通过-XX:+/-UseTLAB参数来设定。

这整个对象创建的过程都是由解释器来完成的(大部分平台都是模版解释器来进行对象创建)。

三:对象的内存布局
Java内存区域

四:对象的访问定位符
对象创建之后,对对象的访问主要有两种方式:

1.句柄方式:是说在java栈中存放了java堆中的句柄池中的该对象对应句柄的地址,在该句柄中包含了到对象实例数据的指针和到对象类型数据的指针。到对象实例数据的指针保存了java堆中实例池中的对象实例数据数据的引用。而到对象类型数据的指针则保存了指向方法区中的对象类型数据的引用。这样,当在GC垃圾回收器在回收内存时将对象实例数据移动时,无需更改栈中的地址引用,而只需要修改java堆中的句柄池中的对应句柄中的实例数据指针就可以了。
Java内存区域

2.直接指针访问:是说在java栈中直接存放的是java堆中的对象的引用。在java堆中的对象由对象实例数据和到对象类型数据的指针两部分组成。而到对象类型数据的指针的值就是方法区中的对象类型数据的地址。该方式最大的好处就是访问速度快。
Java内存区域