深入理解Java虚拟机----第2章 Java内存区域与内存溢出异常

第2章 Java内存区域与内存溢出异常

对于2.2和2.4节可以参考这篇博文Java JVM 内存区域与内存溢出异常

2.3 HotSpot 虚拟机对象探秘
2.3.1 对象的创建
Java作为一门面向对象语言,在程序运行过程中会产生大量的对象,在语言层面上来看,创建一个对象仅仅需要一个new关键字即可,但是在虚拟机中创建一个普通的Java对象(不包括Class对象和数组)的过程你是否了解?
当虚拟机遇到一条new指令的时候,会首先去检查一下在常量池中能否定位到一个类的符号引用和指令参数相匹配,并检查这个引用符号所代表的类是否已经执行过被加载、被解析、被初始化的过程,如果没有就需要对这个类进行类加载。
检查通过后,虚拟机开始为这个新生的对象分配内存。这个对象所需要的内存大小在类加载完成之后即可确定下来,为对象分配内存实在堆上进行的,也可以理解为将堆中一块内存从堆中划分出来。而在堆中为对象分配内存有两种方式,一种是“指针碰撞”,顾名思义当堆中的内存时规整的时候(即所有被使用的内存放在一边,所有空闲的内存放在另一边),只需要将作为空闲内存和使用内存分界线的指针向空闲内存那一边移动一段与所需要分配的内存相同的大小即可。另外一种叫“空闲列表”,就是当堆的内存不是规整的时候,JVM会维护一个列表记录哪块内存是可以使用的,哪块内存已经被占用了,分配内存就是从列表上找一块足够大的空间分配给这个对象,这种方式有一个缺点,就是如果我们需要一个100K的空间但是这个时候堆上剩下的空间有101K,但是这101K分为了90K和11K两块,由于没有连续的空间大于等于100K,所以这个时候明明空间够用但是还是会报OutOfMemoryError的异常,而内存是否规整则取决于垃圾收集器所采用的收集算法是标记-清除还是标记-整理又或者是暂停-复制。
除了分配空间问题,我们还要考虑一个问题,要知道对象的创建在虚拟机中是一个非常频繁的行为,仅仅修改一个指针的位置,在并发的情况下并不是线程安全的,很有可能会出现当A申请了内存之后指针还没有来得及修改,这个时候B就使用了相同的内存地址进行了分配。而解决这个问题有两个办法,第一个是我们上一篇文章提到过得TLAB(本地线程分派缓冲),就是将堆的部分空闲分配为线程私有的,而对象创建的时候分配空间是在私有的缓冲区中进行的,这样也就保证了线程安全,当然如果TLAB的空间用完了需要申请更多的空间,这个时候虚拟机会进行同步锁定,以保证线程安全,是否使用TLAB可以通过配置-XX:+/-UseTLAB的值来设定。第二个办法是对内存分配的行为进行了同步处理,同时加入失败重试的方式保证了内存更新操作的原子性。
在内存分配完成后,虚拟机需要将分配的空间(不包括对象头,这个后面会讲)初始化为零值,当使用了TLAB的时候,这个步骤可以在TLAB分配的时候就进行。这一操作保证了我们对象的成员变量不赋初值就可以直接使用,这个时候他们的值就是数据类型所对应的零值。
初始化零值之后,虚拟机会对这个对象进行必要的设置,如这个对象是来自哪个类的实例、类的元数据信息、对象的hashcode、对象的GC年龄分代等,这些信息都是放在对象的对象头之中的。
在完成这一系列的工作之后在虚拟机的视角一个对象已经创建成功了,但是从Java程序的视角来看,对象的创建还没有结束,方法还没有执行,这里我的理解init方法似乎和构造方法的作用差不多,但是不是一回事(先执行init方法后执行构造方法),在init方法里面会按照我们的意愿对成员变量进行初始化,这样一个对象就真正的产生了。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值