三对象创建与回收

创建对象流程

  • 一个对象的创建过程一般如下:
  1. 首先是代码执行到new关键词,于是根据new后面的参数到常量池总定位该类的符号引用。
  2. 如果没有找到这个符号引用,说明类还没被加载,那么进行类的加载、连接和初始化。
  3. 然后jvm为实例在堆中分配内存,并把该内存空间都初始化为0值。
  4. 初始化后,jvm会进行一些必要的设置,如,把这个对象是哪个类的实例、在GC中的分代年龄信息放到对象头中。
  5. 通过构造函数对该对象进行初始化。

为实例分配内存

  • 在堆中给实例分配内存的方式有两种:指针碰撞和空闲列表。具体使用哪一种,就要看jvm中使用的是什么垃圾回收机制了。
  • 指针碰撞:如果回收空间后能做到压缩整理,使得Java堆中的内存是绝对规整的,即所有已分配内存都凑在一块连续空间,而空闲的凑在另一块,那么只要在中间放着一个指针作为分界点的指示器,在分配空间时把指针向空闲那边移动(即把部分“空闲空间”划到“已分配空间”那边)即可。

  • 空闲列表:有一个列表,其中记录了哪些内存块空闲,在分配的时候从列表中找到一块足够大的划分给实例,然后更新列表。

对象具体内容

  • 对象在内存中包括三个部分:对象头、实例数据和对其填充。
  • 对象头包括对象本身运行时的数据MarkWord(哈希码、GC分代年龄、线程持有的锁、锁状态、偏向时间戳、偏向线程id等)和类型指针(通过该指针可确定是哪个类的实例,如果是数组,还会存储数组的长度)。
  • 实例数据是对象真正存储的有效信息,即我们定义的各种字段的内容。
  • 对其填充是把实例数据填充补全到8的整数倍,因为HotSpot的内存管理要求对任何对象的大小必须是8的整数倍,而对象头已经被设计为8的整数倍了,但是实例数据就不一定了,所以需要补全。

垃圾回收

判断对象是否已死

  • 垃圾收集器在进行垃圾回收之前,要先判断对象是否已死,已死才能被回收。下面介绍两种主流的判断对象已死的算法。
  1. 引用计数算法:在对象中添加一个引用计数器,每当一个对象,计数器就加一,每当引用失效则减一。计数器为0时,说明对象不被任何人引用,那么可以回收。

    public static void method() {
    A a = new A();
    }

    public static void main(String[] args) {
    method();
    }

  • 当调用method()时,执行到new,把A的实例赋值给局部变量a时,堆中A的实例的计数器就会加一;当方法结束时,局部变量a随之销毁,那么A的实例的计数器就会减一。

  • python就是使用这种算法,但在Java中,该算法不能解决循环引用问题,即当对象A,B互相引用时,即A引用了B,B也包含了A的引用,那么当它们都为null时,它们的计数器也并不为0,如果采用引用计数算法,那么此时Java就不能回收它们了,显然是错误的。

    class A {
    private B b;
    public void setB(B b) {
    this.b = b;
    }
    }

    class B {
    private A a = new A();
    public void setA(A a) {
    this.a = a;
    }
    }

    public void method() {
    A a = new A();
    B b = new B();
    a.setB(b);
    b.setA(a);
    }

  • 堆中的实例A同时被栈内的局部变量a和堆内的实例B的成员变量a引用,实例B同时被栈内的局部变量b和堆内的实例A的成员变量b引用。

  • 当method方法执行完毕后,栈被销毁,局部变量a,b自然也被销毁,于是它们对实例A,B的引用都失效,即图中两条红线消失,实例A,B的计数器各自减一;但问题是,堆中的实例不会随着方法完毕而被销毁,于是实例A中的成员变量a仍保持着对实例B的引用,所以实例B的计数器至少还是1,实例A的计数器同理也是1,但此时已经没有地方在用它们了,它们却还是不能被回收。
  1. 可达性分析算法:判断当前引用的对象是否处于某一GCRoot的引用链上。一类可以作为GCRoot的对象是栈中的对象,因为栈帧随着方法的执行自动进栈和出栈,所以在栈中的对象肯定是有用的,那么其引用的对象也必然是有用的,所以只要在这条引用链上的对象,就肯定得判定为存活。PS:可作为GCRoot的对象:虚拟机栈和本地方法栈中的对象、方法区中的静态属性、方法区中的常量。
  • JVM实际也会回收方法区内的常量和不再使用的类的信息。不过并没有强制要求回收方法区,主要原因是性价比太低,即判断条件高,回收空间少。对于常量,如果任何对象都没有引用它,那么就可以回收了,对于类,判断其是否可回收比较复杂:该类的所有实例被回收;加载该类的类加载器被回收;没有通过反射使用该类。

垃圾收集算法</

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值