new一个对象都发生了什么

一. 之前没有进行类加载

1. 类加载,同时初始化类中的静态的属性(赋默认值)

2. 执行静态代码块

3. 分配内存空间,同时初始化非静态的属性(赋默认值)。

4. 调用父类构造器

5. 父类构造器执行完后,如果自己声明属性的同时有显示的赋值,那么进行显示赋值把默认值覆盖。

6. 执行匿名代码块

7. 执行构造器

8. 返回内存地址

二、之前已经进行了类加载

1. 分配内存空间,同时初始化非静态的属性(赋默认值)

2. 调用父类构造器

3. 父类构造器执行完后,如果自己声明属性的同时有显示的赋值,那么进行显示赋值把默认值覆盖

4. 执行匿名代码块

5. 执行构造器

6. 返回内存地址

java创建对象的过程

一、检测类是否被加载

虚拟机遇到一条new指令时,首先去检查这个指令的参数是否能够在常量池中定位到一个类的符号引用,并且检查这个符号引用代表的类是否已被加载、解析和初始化。如果没有,则去执行相应的类加载过程。

二、为新生对象分配内存

在类加载检查以后,接下来虚拟机将为新生对象分配内存。对象所需内存的大小在类加载完成后便可完全确定。

假设java堆中内存是绝对规整的,所有用过的内存都放在一边,空闲的内存放在另一边,中间放着一个指针作为分界点的指示器,那么分配内存就仅仅是把那个指针向空闲空间那边挪动一段与对象大小相等的距离,此分配方式称为“指针碰撞”。

如果java堆中的内存并不是规整的,已使用的内存和空闲的内存相互交错,那就没有办法简单地进行指针碰撞了,虚拟机就必须维护一个个列表,记录上哪些内存块是可用的,在分配的时候从列表中找到一块足够大的空间划分给对象实例,并更新列表上的记录,此分配方式称为“空闲列表”。

选择哪种分配方式是由java堆是否规整决定,而java堆是否规整又由所采用的垃圾收集器是否带有压缩整理功能决定。

三、初始化零值

内存分配完成后,虚拟机需要将分配到的内存空间都初始化为零值(不包括对象头),这一步操作保证了对象的实例字段在java代码中可以不用赋初始值就直接使用,程序能访问到这些字段的数据类型所对应的零值。

四、进行必要的设置

接下来,虚拟机要对对象进行必要的设置,例如这个对象是哪个类的实例,如何才能找到类的元数据信息、对象的GC分代年龄等信息。这些信息存放在对象的对象头中。

五、执行init方法

在上面的工作都完成以后,从虚拟机的视角来说,一个新的对象已经产生了,但从java程序的视角来看,对象的创建才刚开始,<init>方法还没有执行,所有的字段都还为零值,所以一般来说,执行new指令之后会接着执行<init>方法,把对象按照程序员的意愿进行初始化,这样,一个真正可用的对象才算完全产生出来。

总结,创建一个对象的过程就是:

检测类是否被加载,没有加载的话先加载  ->  为新生对象分配内存  ->  将分配到的内存空间都初始化为零值  ->  对对象进行必要的设置  ->  执行<init>方法进行对象初始化 

 

 

使用句柄访问对象的最大好处是reference中存储的是稳定的句柄地址,在对象被移动(垃圾收集时移动对象是非常普遍的行为)时只会改变句柄中的实例数据指针,而reference本身不需要修改。

使用直接指针访问的好处是速度更快,他节省了一次指针定位的时间开销。

Sun HotSpot虚拟机使用第二种方式进行对象访问。

 

参考:https://www.cnblogs.com/chenyangyao/p/5296807.html

参考:https://www.cnblogs.com/CZDblog/p/5589379.html

展开阅读全文

没有更多推荐了,返回首页