一、对象的创建 对象创建过程的流程图如下: Java对象创建的4种方式: [1] 通过 New 指令,调用 Constructor 创建; [2] 通过 Object 的 clone 方法创建; [3] 通过类的反射创建; [4] 通过序列化方式创建;
下面分别实例讲述 【1】new语句创建
MyObject mo = new MyObject() ;【2】clone方法创建对象 构造函数不被自动调用。
public class CreateInstance implements Cloneable{ public CreateInstance getInstance() throws CloneNotSupportedException{ return (CreateInstance) this.clone(); } }如果需要复制上面的那个obj指向的对象实例时,调用new CreateInstance().getInstance()方法就ok了。 但是为什么不直接使用new CreateInstance().clone()呢? JDK中Object# clone()方法的原型是:protected native Object clone() throws CloneNotSupportedException; 方法修饰符是protected,而不是public。 这种访问的不可见性使得我们对Object#clone()方法不可见。所以,必需重写Object的clone方法后才能使用。
public class CreateInstance implements Cloneable{ public CreateInstance clone throws CloneNotSupportedException{ return (CreateInstance) super.clone(); } }值得注意的是 :如果需要使用clone方法,必需实现java.lang.Cloneable接口,否则会抛出java.lang.CloneNotSupportedException。 另外clone方法所做的的操作是直接复制字段的内容,换句话说,这个操作并不管该字段对应的对象实例内容。 像这样字段对字段的拷贝(field to field copy)就成为"浅拷贝",clone方法所做的正是"浅拷贝"。
【3】反射方法newInstance创建对象: 利用java.lang.Class类的newInstance方法,则可根据Class对象的实例,建立该Class所表示的类的对象实例。
CreateInstance instance = CreateInstance.class.newInstance();或者使用下面的语句(只需要存在相应的.class文件即可)
CreateInstance instance =(CreateInstance)Class.forname("com.create.instance.CreateInstance").newInstance();如果包下不存在相应.class文件,则会抛出ClassNotFoundException。 注意 :newInstance创建对象实例的时候会调用无参的构造函数,所以必需确保类中有无参数的构造函数,否则将会抛出java.lang.InstantiationException异常。
【4】 序列化readObject()方法创建对象 如果对象是通过ObjectInputStream类的readObject()方法创建的,那么Java虚拟机通过从输入流中读入的序列化数据来初始化那些非暂时性(non-transient)的实例变量。 在其他情况下,如果实例变量在声明时被显式初始化,那么就把初始化值赋给实例变量,接着再执行构造方法。这是最常见的初始化对象的方式。
二、对象的访问 使用句柄访问的好处是: reference中存储的是稳定的句柄地址,在对象被移动时,只用修改句柄中的实例数据指针,而reference本身不需要修改!
直接访问对象方式的好处是: 减少一次指针定位的时间开销,由于对象的访问是非常频繁的,因此这类开销积少成多也是一项非常的执行成本。
三、对象的销毁
程序计数器,本地方法栈,虚拟机栈因生命周期同线程一样,在线程执行结束时自动回收所分配空间。 而方法区和堆上分配的空间,则需要通过垃圾回收算法来判断是否可以回收。
判断对象是否可回收的算法有两种: [1] . 引用计数法: 【逻辑】为对象设置引用计数器,被引用则 + 1,引用失效则 - 1,减至 0 时可回收; 【优点】实现简单,判断高效; 【缺点】不能解决循环引用问题,例如:objA.attribute = objB,objB.attribute = objA;
[2] . 可达性分析算法: 【逻辑】引入GC Root概念,不能从GC Root定位到的(即不在引用链上的)对象可回收; 【优点】可支持几乎所有情况的判断; 【缺点】实现复杂,判断链路多,效率低;
可作为GC Root的对象有以下4中: 虚拟机栈(栈帧中的局部变量表)中引用的对象; 方法区中类的静态属性引用的对象; 方法区中常量引用的对象; 本地方法栈中(Native方法)引用的对象;
常用回收算法: [1] . 标记-清除: 【逻辑】标记可回收对象,直接进行清除; 【优点】回收速度快,不影响任务运行; 【缺点】内存产生大量碎片,造成内存浪费;
[2] . 标记复制: 【逻辑】将内存一分为二,标记可回收对象,复制所有存活对象到空闲内存块,然后清除标记对象; 【优点】不会产生内存碎片,复制过程简单,回收时间短; 【缺点】内存利用率不高,有一块内存一直是不能使用的;
[3] . 标记-整理-清除: 【逻辑】标记可回收对象,将存活对象移动到内存一端,然后清除标记对象; 【优点】不会产生内存碎片,内存使用率高; 【缺点】整理的过程复杂且耗时较长,会造成 stop the world,任务需暂停直到回收完成;
分代收集: 【逻辑】集众算法之所长,在不同的内存区使用不同的算法,新生代使用复制算法,年老代使用标记-整理-清除
对象引用的分类: [1] . 强引用: 正常创建的对象,只要强引用还在,对象就不会被回收; [2] . 软引用: 指向的对象有用但非必需,在内存空间不足时,可被回收; [3] . 弱引用: 指向非必需的对象,垃圾回收时直接被回收; [4] . 虚引用: 又称为幽灵引用,对引用的对象完全没有影响,也不能获取对象实例,唯一的作用是对象被回收时可以得到系统通知;
注:以上内容来自与对《深入理解Java虚拟机》的总结。
对象回收算法可参考这篇文章: https://mp.weixin.qq.com/s/Pj0HHwHG5NJhduYOf6R8xQ