jvm(二):对象加载浅谈

通过第一节的介绍,知道了对象实际上是分配在堆空间上的。那么具体的对象分配过程是怎样的呢?

虚拟机遇到一条new 指令时,先检查该指令对应的方法(初始化方法)能否在方法区(具体来说是常量池)找到,并检查对应的类是否被加载、解析和初始化过。如果没有,那就必须先执行相应的类加载过程。类加载通过后,就可以确定对象所需的内存大小。

以上过程有一些需要注意的点:

分配方式

我们如何区别哪一块内存是属于哪一个类?
这其实也是操作系统中内存分配的一个常见问题:
1. 当内存中所有被使用的内存在内存的某一部分,未使用的放在另一部分,那么分配新内存时只需要移动一下未分配的内存和已分配内存的的临界指针。但考虑到还有内存释放的问题,单纯的移动指针方法无疑会浪费内存,因此还需要额外的压缩整理功能。jvm中的这种分配内存的方法被称为“指针碰撞”(Bump the Pointer)。
2. 压缩整理功能即移动所有被分配的内存到一边,剩下另一边作为空闲内存。但如果没有压缩功能,还有其他方法吗?我们可以维护一个表,表上记录那些区域是空闲的,这意味着,可能存在一个对象被分配两个不连续的内存空间的情况。jvm中分配内存的这种方法叫做“空闲列表”(Free List)。

同步

我们知道堆是线程共享的,共享就意味着并发,并发会带来冲突,即如何将一块内存只分配给多个线程中的一个?
这个问题也有两种方式:
1. 最简答也就是同步处理,对分配对象内存这一操作进行同步化
2. 预先为每个线程在java堆中分配一小块内存,这块内存是线程私有的,被称为本地线程分配缓冲(Thread Local Allocation Buffer, TLAB),然后生成对象所需的空间就由TLAB提供。只有当TLAB用完时,再加上同步锁从堆上划分。

初始化

对象分配完成后,变量的初始值如何确定?
对象的内存被分配完成后,虚拟机需要将分配到的内存空间都初始化为零值(至于什么是零值,这个在后面再介绍,这保证了对象的实例字段可以在不赋初始值就直接使用。

接下来

从jvm的角度来看,对象的初始化工作已经完成了,但从程序员的角度来看,其实这时候都还未执行完构造方法。
在紧接着,jvm就会执行对应的初始化方法,从这里才进入程序员所指定的方法环节。

对象的数据

对象在内存中的数据包括三部分:对象头(Header)、实例数据(Instance Data)和对其填充(Padding)。

对象头

对象头的设置是在jvm为新对象划分内存时,就会设置好的。
对象头是对于程序员透明的关于类的属性信息。
包含对象所属的类,如何定位到类的元信息,对象的哈希码,对象的GC年代年龄(后面会讲)等。
具体来说包括两类:
1. 对象自身的运行时数据
这包括哈希码,GC分代年龄,锁状态标志,线程持有锁,偏向线程ID,偏向时间戳等。
如果是数组,则还需要有一块用于记录数组长度的数据。
2. 对象指向它的类元数据的指针
用来指明该对象属于哪个类。该部分数据不是必须的。

实例数据

顾名思义,实例数据才是对象真正存储的可以被程序员操作的字段数据。

对其填充

仅起占位符的作用。。。

OutOfMemoryError和StackOverflowError

OutOfMemoryError可能出现在jvm的各个区域(除了程序计数器)。
但最常出现该信息的还是java堆。当对象数量太多导致堆上的空间不够时,就会显示该信息:

java.lang.OutOfMemoryError: Java heap space...

表示是内存溢出,且发生在java堆上。
除了对象,过多的线程也会导致内存溢出。但此时的内存溢出发生在虚拟机栈上,因为我们知道线程的执行过程是在栈上执行的,加入每个线程很大,那么栈大小一定(不可扩容)的情况下,就会出现线程数无法再增加的情况而导致内存溢出。
此时如果不能减少线程数且无法拓展内存,则可以采用减少堆的最大容量减小线程的最大栈容量来换取更多的线程,线程无法继续被创建显示的错误一般如下:

Exception in thread "main" java.lang.OutOfMemoryError: unable to create new native thread

方法区同样会出现内存溢出,因为方法区上有运行时常量池,因此如果声明了大量的字符串,则会导致内存溢出,同样的还有方法区装有太多的类(JSP也算)和方法也会导致内存溢出。

Exception in thread "main" java.lang.OutOfMemoryError: PermGen space

简单看完了OutOfMemoryError,接下来了解下StackOverflowError。
顾名思义,StackOverflowError一般出现在堆,最常见的问题是,当函数出现循环调用时(即无限递归),就会导致栈的栈帧数挤满而报StackOverflowError。
栈的深度室友限制的,无限递归总会触碰到这个限制。
除此之外,如果栈帧太大,则栈的深度会相应的减小,同样会导致StackOverflowError。

Exception in thread "main" java.lang.StackOverflowError
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值