java 声明 对象 内存_JAVA对象的创建与内存布局

java对象的创建

当虚拟机遇到一条new指令的时候,首先会去检索,这条指令的参数能不能在常量池中定位到对于一个类的符号引用,然后会检查这个符号引用代表的类是否已经被加载、解析和初始化过,如果没有,那首先得先执行一个类的加载过程。

在类加载检查通过后,那就要为实例化的对象分配内存,对象所需的内存大小在类加载完成后是可以确定的,所以为对象分配内存空间,其实就是将一块内存空间从堆中划分出来。当堆的内存空间是工整的,有个指针作为分解点的指示器,指针的一边是已经分配的内存,指针的另外一边是空闲内存。我们只要挪动指针,即把指针向空闲的内存空间挪动一段与对象大小相等的距离即可,这种方法叫做"指针碰撞"。如果堆的内存空间不是绝对规整的,即已经分配的堆空间跟空闲空间相互交错,那这时候就只能维护一个列表,记录哪些内存块是可以用的,当分配的时候就找到一块空闲的内存块划分给对象实例,并且更新列表,这种方法叫做"空闲列表";所以,选择哪种分配方式由是堆内存块是否规整决定,在垃圾收集中,由于新生代主要采用的复制算法,如:Serial、ParNew等,没有内存碎片,所以采用的是"指针碰撞";而对于老年代的CMS收集器,采用的是"标记-清除"算法,所以会产生内存碎片,空闲内存跟已经分配的内存相互交错,所以采用的是"空闲列表"。

假如多个线程并发创建对象实例,那会不会存在在分配内存的时候发生线程安全问题。答案是肯定的,java中用什么避免这个问题呢?解决这个问题有两种方案:一:采用CAS配上失败重试的方法保证操作原子性;二:将内存的分配操作按照不同线程分别在不同的空间进行,每一个线程在JAVA堆中会预分配一小块内存,叫本地线程分配缓冲(TLAB),只有当TLAB用完想要分配新的TLAB,才需要同步锁定。可用过JVM参数-XX:+/-UseTLAB参数设定。

对象的内存布局

对象的内存区域由三个部分组成:对象头、实例数据、对齐填充块。

对象头包括两部分信息,一部分是存储对象运行时的数据,如哈希码(HashCode)、GC分代年龄、状态标志位、锁记录指针、偏向线程ID等,这部门我们称之为"Mard Word",这部分的数据在32bit跟64bit的虚拟机中的大小分别为32bit、64bit,由于需要存储的运行时数据很多,为了考虑空间效率,"Mard Word"采用非固定的数据接口,根据自己的状态复用自己的存储空间。

对象头的另外一部分是一个类型指针,即指向对象的类元数据的指针。注:并不是所有虚拟机实现都必须要在对象头上保留类型指针,查找对象元数据信息不一定要经过对象本身。如果对象是一个数组类型,那么对象头还有一块用于记录数组长度的数据。

对象的访问定位

java虚拟机栈通过reference数据来操作堆上的对象。reference类型定位一个对象有两种方式

使用句柄:java堆中划分一块空间用于句柄池,句柄中包含了数据实例数据与对象的元数据类型,reference存储的是对象的句柄地址。优点:对象移动只需改句柄中的对象地址而不需要改reference。

直接指针访问:顾名思义,reference指向的是对象的地址。优点:速度快,节省了一次指针定位的时间开销。HotSpot用的是这种方式。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值