关闭

java虚拟机——对象的创建

标签: java创建对象new面向对象
231人阅读 评论(0) 收藏 举报
分类:

对象的创建

java是一种面向对象的语言,通常我们在创建对象时,大多是通过 "类名 变量名 = new 类名()" ,那么在底层的虚拟机中是怎么进行的呢?
(1)当虚拟机检测到一条new指令时,首先先检查当前创建的这个对象的类是否加载(通过方法区的常量池的符号引用来定位),如果这个类没有加载,则先去加载这个类(具体如何加载,我会在以后的章节继续写)。如果该类已经加载,则执行接下来的步骤。
(2)类加载后,便开始执行对象的创建,其实也就是为对象分配内存空间(一般在年轻代的Eden区进行分配,大对象直接进入老年代)。对象的所占大小在类加载后根据类中分区(在HotSpot虚拟机中对象的内存中有对象头,实际数据和对齐填充,具体接下来会讨论)便可知。
(3)在分配空间时虚拟机会根据不同情况采用不同方式:
1.指针碰撞:当java堆中的内存布局是绝对规整的,什么是绝对规整?就是所有存活的对象在堆区的一端,空闲的内存在另一端,那么在存活的对象和空闲空间的分隔点(临界点)放置了一个类似指针的指示器,当分配一个新对象的空间时,就是将指针向空闲空间方向移动和该对象大小相等的距离。
2.空闲列表:当java堆中的内存不是规整的,凌乱不堪,存活对象所占的内存和空闲内存相互交错,那么就不能使用指针碰撞的方法了。这时候虚拟机就必须需要一个列表来记录哪些位置的内存是空闲的,可以使用的,在创建对象的时候从列表中划分出一个足够大的空间给对象。
(4)那么问题来了,java堆什么时候是绝对规整的,什么时候相互交错的情况呢?
这就要由JVM的垃圾收集器来决定了。比如Serial收集器、ParNew收集器和Parallel Scavenge收集器等,在对内存进行一次回收后,便将堆内存压缩成存活对象一端、空闲内存另一端的规整情况(这几种属于年轻代的垃圾收集器,采用了复制算法)。采用空闲列表的情况,主要是由于虚拟机采用了类似于CMS这种收集器来进行回收内存,而CMS属于老年代的垃圾回收器,采用的是标记-清除算法
(5)但是现在又有一个问题出现了,如果在创建A对象时,指针(指示器)会改变位置,但此时对象B又同时正在创建,又使用了原来的指针,此时会出现线程不安全的情况,针对这种情况,JVM采用两种方式进行解决:
1.可能大家都会想到,使用同步进行处理。
2.使用本地线程分配缓存,简称TLAB(Thread Local Allocation Buffer),就是把分配内存的行为按照线程分别分配到各自之中进行,每个线程中预先在堆中分配一块空间,在线程创建对象,分配内存的时候,首先在缓存中进行分配,在缓存使用完时,使用同步进行新的TLAB的分配。
(6)在分配完内存之后,需要进行内存空间的初始化。这一步操作保证了我们在创建对象时,不需要像局部变量声明时那样必须指定初始值,可以直接使用对象中的成员变量。接下来虚拟机需要对对象进行一些必要的设置,比如:对象属于哪个类,HashCode,GC分代年龄等(这些信息都放在对象头)。
(7)在这之后,虚拟机已经将一个对象创建完成了,那么对于我们来说,仅仅一句话而已,对象才刚刚创建完成,所有字段都为各自类型的0值,所以一般在执行new指令之后就要执行<init>方法,将对象按照程序员的方式进行初始化。
至此,一个真正可用的对象创建完成。
接下来的章节会跟大家简单的讨论对象的内存布局和对象的访问方式。
如有错误,还望大家指正,谢谢
0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:893次
    • 积分:49
    • 等级:
    • 排名:千里之外
    • 原创:4篇
    • 转载:0篇
    • 译文:0篇
    • 评论:0条
    文章分类
    文章存档