第5章 初始化与清理

本章思维导图

这里写图片描述

一. 构造器

构造器,又名构造方法。其中,构造方法是一种特殊方法,主要体现在两点:

  • 构造方法名,与类名完全相同;
  • 没有返回值

注意:没有返回值,但是不能使用void关键字修饰该方法。另外,每次new对象时,对应的构造方法将会自动调用,此时返回的是该对象的引用

代码示例:

public class bird {
    private int id;
    bird(){
        System.out.println("This is a no argument constructor.");
    }
    bird(int id){
        this.id = id;
        System.out.println("This is a one-argument constructor.");
    }
}

public class testCase {
    public static void main(String[] args){
        bird b1 = new bird();
        bird b2 = new bird(1);
    }
}

示例结果:

This is a no argument constructor.
This is a one-argument constructor.
二. 方法重载

方法重载,是指在同一个类中,同时存在着若干个方法名完全相同,但参数列表不同的方法。其中,参数列表不相同,主要体现在以下三个方面:

  • 参数的类型不相同;
  • 参数类型的顺序不相同;
  • 参数的数量不相同。

代码示例:

public class overloadCase {
    public void print(int id){
        System.out.println("I have one argument with int.");
    }
    public void print(String name){
        System.out.println("I have one argument with String.");
    }
    public void print(int id, String name){
        System.out.println("I have two arguments with int and String.");
    }
    public void print(String name, int id){
        System.out.println("I have two arguments with String and int.");
    }
}

public class testCase {
    public static void main(String[] args){
        overloadCase ol = new overloadCase();
        ol.print(24);
        ol.print("Merry");
        ol.print(24, "Merry");
        ol.print("Merry", 24);
    }
}

示例结果:

I have one argument with int.
I have one argument with String.
I have two arguments with int and String.
I have two arguments with String and int.
三. this关键字

使用this关键字需要注意几点:

  • 访问同类中的成员变量或者成员方法,可以省略this;
  • 使用this调用构造器时,必须位于某构造器的最开始处。

代码示例:

public class carCase {
    private int id;
    public void print(){
        System.out.println(id);
    }
    carCase(int id){
        this.id = id;
        print();
    }
    carCase(){
        this(24);   //the method of this must be ahead of a constructor
        print();
    }
}

public class testCase {
    public static void main(String[] args) {
        carCase car1 = new carCase();
        carCase car2 = new carCase(23);
    }
}

示例结果:

24
24
23
四. finalize方法

finalize()方法,继承于java.lang.Object,也就意味着每个实现类都包含了该方法。

finalize()方法的作用:

当垃圾回收器,进行对象的垃圾回收前,将会调用该方法,来执行一些重要的清理工作,如关闭读写文件的句柄等。

特别注意:finalize方法并不会回收对象所占用的存储空间。

如果需要重写finalize()方法,需要将该方法的访问权限设置为:protected或public,切记不能为private。该方法一般不会显式调用,而是当执行System.gc()方法时,finalize()方法会被自动调用。

另外,关于System.gc()的认识误区:执行垃圾回收方法System.gc(),便可以回收“失效”的对象。

其实,执行垃圾回收方法System.gc(),并不能保证“失效”对象所占用的存储空间一定被回收。该方法对于JVM来说只是建议不是命令,JVM会根据当前运行内存空间是否快要耗尽,来决定回收“失效”对象所占用的内存空间。

代码示例:

public class bird {
    private int id;
    bird(int no){
        id = no;
        System.out.println("The bird NO." + id + " was born.");
    }
    protected void finalize() throws Throwable{
        super.finalize();
        System.out.println("The bird NO." + id + " was finalized.");
    }
}

public class testCase {
    public static void main(String[] args) throws Throwable {
        bird b1 = new bird(1);
        bird b2 = new bird(2);
        bird b3 = new bird(3);
        b1.finalize();
        b1 = b2 = null;
        System.gc();
    }
}

示例结果:

The bird NO.1 was born.
The bird NO.2 was born.
The bird NO.3 was born.
The bird NO.1 was finalized.
The bird NO.2 was finalized.
The bird NO.1 was finalized.
五. 垃圾回收器(Garbage Collector)
1. 根搜索算法

根搜索算法的基本思想:通过一系列名为GC Root的对象作为起始点,即根节点。然后,从这些节点开始向下搜索,搜索所走的路径称为引用链,如果一个对象到GC Root节点没有任何引用链相连,则判定该对象为不可用,即为可以回收对象。

这里写图片描述

如上图所示,Object 5 和Object 6两个对象到GC Root节点不可达,所以将它们判定为可回收的对象。

2. 垃圾回收算法

a. 标记-清除算法

概念:

标记阶段:先通过根节点,标记所有从根节点开始的可达对象。因此,未被标记的对象就是未被引用的垃圾对象;
清除阶段:清除所有未被标记的对象。

缺点:

标记和清除的过程效率不高(标记和清除都需要从头遍历到尾) 标记清除后会产生大量不连续的碎片。

b. 复制算法:(新生代的GC)

概念:

将原有的内存空间分为两块,每次只使用其中一块,在垃圾回收时,将正在使用的内存中的存活对象复制到未使用的内存块中,然后清除正在使用的内存块中的所有对象。

优点:

这样使得每次都是对整个半区进行回收,内存分配时也就不用考虑内存碎片等情况,只要移动堆顶指针,按顺序分配内存即可,实现简单,运行效率高

缺点:

浪费内存空间

从以上描述不难看出,复制算法要想使用,最起码对象的存活率要非常低才行。

现在的商业虚拟机都采用这种收集算法来回收新生代,新生代中的对象98%都是“朝生夕死”的,所以并不需要按照1:1的比例来划分内存空间,而是将内存分为一块比较大的Eden空间和两块较小的Survivor空间,每次使用Eden和其中一块Survivor。当回收时,将Eden和Survivor中还存活着的对象一次性地复制到另外一块Survivor空间上,最后清理掉Eden和刚才用过的Survivor空间。

HotSpot虚拟机默认Eden和Survivor的大小比例是8:1,也就是说,每次新生代中可用内存空间为整个新生代容量的90%(80%+10%),只有10%的空间会被浪费。

当然,98%的对象可回收只是一般场景下的数据,我们没有办法保证每次回收都只有不多于10%的对象存活,当Survivor空间不够用时,需要依赖于老年代进行分配担保,所以大对象直接进入老年代。

c. 标记-整理算法:(老年代的GC)

概念:

标记阶段:先通过根节点,标记所有从根节点开始的可达对象。因此,未被标记的对象就是未被引用的垃圾对象
整理阶段:将将所有的存活对象压缩到内存的一端;之后,清理边界外所有的空间

优点:

不会产生内存碎片。

缺点:

在标记的基础之上还需要进行对象的移动,成本相对较高,效率也不高。

以上三种算法的区别如下:(>表示前者要优于后者,=表示两者效果一样)

  • 效率:复制算法 > 标记/整理算法 > 标记/清除算法(此处的效率只是简单的对比时间复杂度)。
  • 内存整齐度:复制算法=标记/整理算法>标记/清除算法。
  • 内存利用率:标记/整理算法=标记/清除算法>复制算法。

注:标记-整理算法不仅可以弥补标记-清除算法当中,内存区域分散的缺点,也消除了复制算法当中,内存减半的高额代价。

复制算法在对象存活率高的时候要进行较多的复制操作,效率将会降低,所以在老年代中一般不能直接选用这种算法。

d. 分代收集算法
根据对象的存活周期的不同将内存划分为若干块。一般将Java堆分为新生代和老年代:短命对象归为新生代,长命对象归为老年代

  1. 存活率低:少量对象存活,适合复制算法:在新生代中,每次GC时都发现有大批对象死去,只有少量存活(新生代中98%的对象都是“朝生夕死”),那就选用复制算法,只需要付出少量存活对象的复制成本就可以完成GC。

  2. 存活率高:大量对象存活,适合用标记-清理/标记-整理:在老年代中,因为对象存活率高、没有额外空间对他进行分配担保,就必须使用“标记-清理”或者“标记-整理”算法进行GC。

注:老年代的对象中,有一小部分是因为在新生代回收时,老年代做担保,进来的对象;绝大部分对象是因为很多次GC都没有被回收掉而进入老年代。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值