Java~面向对象编程中对象的生命周期(对象的创建过程,终结阶段,强引用、软引用、弱引用、虚引用 )


在Java中,对象的生命周期包括以下几个阶段

创建阶段

  • java是面向对象的编程语言,那么对象的创建过程用代码体现就是
Person person = new Person();

类加载检查

  • 虚拟机遇到一条new指令时,首先将去检查这个指令的参数是否能在常量池中定位到一 个类的符号引用,并且检查这个符号引用代表的类是否已被加载、解析和初始化过。如果没 有,那必须先执行相应的类加载过程。
  • 类加载的主要过程就是加载过程, 链接过程, 初始化过程, 加载过程主要就是依赖双亲委派模型找到class文件, 然后进行链接, 链接过程就是先解析class文件是不是正确的, 然后在为这个解析后的类对象进行空间的分配和静态值的默认赋值, 最后再执行符号的替换,初始化就是执行静态代码块和给静态变量赋初始值
  • 如果通过指令检查到偶类对象就不在进行加载了, 直接进行下一步
  • 还有一个点要注意我们在创建一个对象的时候是一个典型的递归过程, 在准备实例化一个类的对象前,首先准备实例化该类的父类,如果该类的父类还有父类,那么准备实例化该类的父类的父类,依次递归直到递归到Object类
    在这里插入图片描述

分配内存

  • 在类加载检查通过后,接下来虚拟机将为新生对象分配内存。对象所需内存的大小在类 加载完成后便可完全确定,为对象分配空间的任务等同于把 一块确定大小的内存从Java堆中划分出来。
  • 这个步骤有两个问题:

1.如何划分内存。

2.在并发情况下, 可能出现正在给对象A分配内存,指针还没来得及修改,对象B又同时使用了原来的指针来 分配内存的情况。

  • 划分内存的方法:

“指针碰撞”(Bump the Pointer)
如果Java堆中内存是绝对规整的,所有用过的内 存都放在一边,空闲的内存放在另一边,中间放着一个指针作为分界点的指示器,那所分配 内存就仅仅是把那个指针向空闲空间那边挪动一段与对象大小相等的距离。(每次通过CAS的获取最新指针)

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

初始化

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

设置对象头

  • 在HotSpot虚拟机中,对象在内存中存储的布局可以分为3块区域:对象头(Header)、 实例数据(Instance Data)和对齐填充(Padding)。

  • HotSpot虚拟机的对象头包括两部分信息

  • 第一部分用于存储对象自身的运行时数据, 如哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等。

  • 对象头的另外一部分是类型指针,即对象指向它的类对象的指针,虚拟机通过这个指 针来确定这个对象是哪个类的实例。

  • 初始化零值之后,虚拟机要对对象进行必要的设置,例如这个对象是哪个类的实例、如何才能找到类的类对象信息、对象的哈希码、对象的GC分代年龄等信息。这些信息存放在对象的对象头

执行方法

  • 执行方法,即对象按照程序员的意愿进行初始化。对应到语言层面上讲,就是为属性赋值(注意,这与上面的赋零值不同,这是由程序员赋的值),和执行构造方法。

引用派遣

  • 在堆内存中派遣一个指向这个对象的引用到栈区上, 让程序可以访问到
    在这里插入图片描述

应用阶段

  • 对象至少被一个强引用持有着。可一通过这个强引用使用堆上的这个对象

不可见阶段

  • 当一个对象处于不可见阶段时,说明程序本身不再持有该对象的任何强引用,虽然该这些引用仍然是存在着的。
  • 这种情况下,该对象仍可能被JVM等系统下的某些已装载的静态变量或线程或JNI等强引用持有着,这些特殊的强引用被称为”GC root”。存在着这些GC root会导致对象的内存泄露情况,无法被回收。
  • 简单说就是程序的执行已经超出了该对象的作用域了。

不可达阶段

  • 对象处于不可达阶段是指该对象不再被任何强引用所持有包括程序中的强引用和JVM系统下的强引用。

  • 但是在实际中又会根据不同的引用产生不同的实际回收时间

强引用,软引用,弱引用,虚引用

  • 从 JDK1.2 版本开始,把对象的引用分为四种级别,从而使程序能更加灵活的控制对象的生命周期。这四种级别由高到低依次为:强引用、软引用、弱引用和虚引用。
强引用(Strong Reference)
  • 强引用就是我们经常使用的引用,其写法如下:
Object o = new Object();

特点:

  1. 只要还有强引用指向一个对象,垃圾收集器就不会回收这个对象。
  2. 显式地设置 o 为 null,或者超出对象的生命周期,此时就可以回收这个对象。具体回收时机还是要看垃圾收集策略。
  3. 在不用对象的时将引用赋值为 null,能够帮助垃圾回收器回收对象。比如 ArrayList 的 clear() 方法实现:
 public void clear() {
     modCount++;

     // clear to let GC do its work
     for (int i = 0; i < size; i++)
         elementData[i] = null;
     size = 0;
 }

软引用(Soft Reference)
  • 如果一个对象只具有软引用,在内存足够时,垃圾回收器不会回收它;如果内存不足,就会回收这个对象的内存。也就是大多数情况下他是fullGC回收的
  • 使用场景:
  1. 图片缓存。图片缓存框架中,“内存缓存”中的图片是以这种引用保存,使得 JVM 在发生 OOM 之前,可以回收这部分缓存。
  2. 如果将浏览过的网页存储到内存中会造成内存的大量浪费,甚至会造成内存溢出这时候就可以使用软引用
        Person person = new Person(20);
        //软引用
        SoftReference<Person> soft = new SoftReference<>(person);
        if (soft.get() == null) {
            System.out.println("已经死亡");
        } else {
            System.out.println("还活着");
            Person person1 = soft.get();
            System.out.println(person1);
        }
弱引用(Weak Reference)
  • 简单来说,就是将对象留在内存的能力不是那么强的引用。当垃圾回收器扫描到只具有弱引用的对象,不管当前内存空间是否足够,都会回收内存。

  • 弱引用与软引用的区别在于:只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程, 因此不一定会很快发现那些只具有弱引用的对象。所以被软引用关联的对象只有在内存不足时才会被回收,而被弱引用关联的对象在JVM进行垃圾回收时总会被回收。

        Person person = new Person(20);
        //软引用
        SoftReference<Person> soft = new SoftReference<>(person);

        Person person1 = new Person(23);
        //弱引用
        WeakReference<Person> week = new WeakReference<>(person1);

        //通知GC
        System.gc();
        Thread.sleep(1000);
        if (soft.get() == null) {
            System.out.println("person: 软引用已经死亡");
        } else {
            System.out.println("person: 软引用还活着");
            System.out.println(soft.get());
        }
        if (week.get() == null) {
            System.out.println("person1: 弱引用已经死亡");
        } else {
            System.out.println("person1: 弱引用还活着");
            System.out.println(soft.get());
        }
  • 如果一个对象是偶尔的使用,并且希望在使用时随时就能获取到,但又不想影响此对象的垃圾收集,那么应该用 Weak Reference 来记住此对象。或者想引用一个对象,但是这个对象有自己的生命周期,你不想介入这个对象的生命周期,这时候就应该用弱引用,这个引用不会在对象的垃圾回收判断中产生任何附加的影响。
虚引用(Phantom Reference)
  • 虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。
  • 无法通过虚引用来获取对一个对象的真实引用, 他的get永远是null。
  • 虚引用与软引用和弱引用的一个区别在于:虚引用必须和引用队列(ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中。当发生GC,虚引用就会被回收,我们可以通过ReferenceQueue获得虚引用被回收的通知
        Person obj = new Person(12);
        ReferenceQueue<Object> refQueue = new ReferenceQueue<>();
        PhantomReference<Object> phantomReference = new PhantomReference<Object>(obj,refQueue);
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    Reference reference = refQueue.poll();
                    if (reference == null) {
                        System.out.println("phantomReference:" + "被回收");
                        break;
                    } else {
                        System.out.println("还活着");
                    }
                }
            }
        }).start();
  • 使用场景:
  1. 可以用来跟踪对象堆垃圾回收的活动
  2. 一般可以通过虚引用达到回收一些非java内的一些资源比如堆外内存的行为。例如:在 DirectByteBuffer 中,会创建一个 PhantomReference 的子类Cleaner的虚引用实例用来引用该 DirectByteBuffer 实例,Cleaner 创建时会添加一个 Runnable 实例,当被引用的 DirectByteBuffer 对象不可达被垃圾回收时,将会执行 Cleaner 实例内部的 Runnable 实例的 run 方法,用来回收堆外资源。
  3. 一旦这个对象被回收,相应的用户线程会收到通知并对直接内存进行清理工作。
  • 在实际程序设计中一般很少使用弱引用与虚引用,使用软引用的情况较多,这是因为软引用可以加速JVM对垃圾内存的回收速度,可以维护系统的运行安全,防止内存溢出(OutOfMemory)等问题的产生
  • 所以有效的合适的使用软引用可以做到在不能影响程序运行的前提下还能对JVM优化

收集阶段

  • 当垃圾回收器发现该对象已经处于“不可达阶段”并且垃圾回收器已经对该对象的内存空间重新分配做好准备时,则对象进入了“收集阶段”。如果该对象已经重写了finalize()方法,则会去执行该方法的终端操作。

finalize()方法

  • 当垃圾回收器要宣告一个对象死亡时,至少要经过两次标记过程:如果对象在进行可达性分析后发现没有和GC Roots相连接的引用链(到了不可达阶段),就会被第一次标记,并且判断是否执行finalizer( )方法,如果对象覆盖finalizer( )方法且未被虚拟机调用过,那么这个对象会被放置在F-Queue队列中,并在稍后由一个虚拟机自动建立的低优先级的Finalizer线程区执行触发finalizer( )方法,但不承诺等待其运行结束。
  • 如果没有覆盖finalizer( )方法就直接销毁他, 所以这个方法的目的就是为对象逃脱死亡的最后一次机会。(只要重新与引用链上的任何一个对象建立关联即可。)但是不建议使用,运行代价高昂,不确定性大,且无法保证各个对象的调用顺序。可用try-finally或其他替代。

终结阶段

  • 当对象执行完finalize()方法后仍然处于不可达状态时,则该对象进入终结阶段。在该阶段是等待垃圾回收器对该对象空间进行回收。

对象空间重新分配阶段

  • 圾回收器对该对象的所占用的内存空间进行回收或者再分配了,则该对象彻底消失了,称之为“对象空间重新分配阶段”。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值