Fork me on GitHub

this关键字,垃圾回收

一 this关键字

1   自己的理解是:

1.1 出现在类创建之初的构造器或方法中,而一般不再类具体实现中出现,也不会出现在静态方法中。

1.2 字面意思是当前对象的引用,你可以像对待其他引用一样对待这个引用。

2  一些特殊的用法:

2.1 返回this

public class Leaf {

    int i = 0;

    Leaf increment() {
        i++;
        return this;
    }

    void print() {
        System.out.println("i = " + i);
    }

    public static void main(String[] args) {
        Leaf x = new Leaf();
        x.increment().increment().increment().print();
    }
}

输出结果为3,每调用一次increment(),返回的对象的元素i加一,最后打印。

 

2.3 构造器中调用构造器

public class Flower {
    int petalCount = 0;
    String s = "initial value";

    Flower(int petals) {
        petalCount = petals;
        System.out.println("Constructor w/ int arg only, petalCount = " + petalCount);
    }

    Flower(String ss) {
        System.out.println("Constructor w/ string arg only, s = " + ss);
        s = ss;
    }

    Flower(String s, int petals) {
        this(petals);
        //- this(s); // Can't call two!
        this.s = s; // Another use of "this"
        System.out.println("String & int args");
    }

    Flower() {
        this("hi", 47);
        System.out.println("no-arg constructor");
    }

    void printPetalCount() {
        //- this(11); // Not inside constructor!
        System.out.println("petalCount = " + petalCount + " s = " + s);
    }

    public static void main(String[] args) {
        Flower x = new Flower();
        x.printPetalCount();
    }
}

在构造器中运用this调用构造器可以实现更多奇怪的想法。

注意,只能在构造器中调用构造器一次。

结果:

Constructor w/ int arg only, petalCount = 47
String & int args
no-arg constructor
petalCount = 47 s = hi

static简单说明:静态方法是为类而创建的,不需要任何对象。事实上,这就是静态方法的主要目的,静态方法看起来就像全局方法一样,但是 Java 中不允许全局方法。

使用静态方法,因为不存在 this,所以你没有向一个对象发送消息。不是面向对象的。

如果你发现代码中出现了大量的 static 方法,就该重新考虑自己的设计了。static 的概念很实用,许多时候都要用到它。

 

二 垃圾回收

1 .finalize()方法

  Java垃圾回收器只能回收通过new分配的对象,除此之外需要使用finalize()方法。

  一般在Java使用本地方法时调用,本地方法支持调用c和c++的代码,这些语言可能有垃圾回收器无法回收的对象。

  如果 Java 虚拟机(JVM)并未面临内存耗尽的情形,它可能不会浪费时间执行垃圾回收以恢复内存。

  使用示范:

  

// housekeeping/TerminationCondition.java
// Using finalize() to detect a object that
// hasn't been properly cleaned up

import onjava.*;

class Book {
    boolean checkedOut = false;

    Book(boolean checkOut) {
        checkedOut = checkOut;
    }

    void checkIn() {
        checkedOut = false;
    }

    @Override
    protected void finalize() throws Throwable {
        if (checkedOut) {
            System.out.println("Error: checked out");
        }
        // Normally, you'll also do this:
        // super.finalize(); // Call the base-class version
    }
}

public class TerminationCondition {

    public static void main(String[] args) {
        Book novel = new Book(true);
        // Proper cleanup:
        novel.checkIn();
        // Drop the reference, forget to clean up:
        new Book(true);
        // Force garbage collection & finalization:
        System.gc();
        new Nap(1); // One second delay
    }

}

输出:Error: checked out

 

2 垃圾回收器工作方式

2.1 Java堆工作方式

  Java堆拥有和c++语言栈差不多的读写速度。

  原因:它类似一个传送带,每分配一个新对象,它就向前移动一格。这意味着对象存储空间的分配速度特别快。Java 的"堆指针"只是简单地移动到尚未分配的区域,所以它的效率与 C++ 在栈上分配空间的效率相当。

  但是不完全是传送带,那样会导致频繁的页面调度,最终内存会耗尽。

  解决:垃圾回收器的介入,当它工作时,一边回收内存,一边使堆中的对象紧凑排列,这样"堆指针"就可以很容易地移动到更靠近传送带的开始处,也就尽量避免了页面错误。

2.2 引用计数 

  原理:每个对象中含有一个引用计数器,每当有引用指向该对象时,引用计数加 1。当引用离开作用域或被置为 null 时,引用计数减 1。垃圾回收器会遍历含有全部对象的列表,当发现某个对象的引用计数为 0 时,就释放其占用的空间。

  缺点:如果对象之间存在循环引用,那么它们的引用计数都不为 0,就会出现应该被回收但无法被回收的情况。(形成闭环)

  该方法几乎没有在任何Java虚拟机中使用。

  

2.3 停止-复制

  原理:存活对象追溯到栈或静态存储区,每次从栈或静态存储区出发,遍历所有的引用,这个引用链条可能会穿过数个对象层次,你将会发现所有"活"的对象。形成一个网,网上的都是活得,不在网上的就会被垃圾回收。

  优点:很好解决对象间循环引用的问题,这些对象不会被发现。

  在此方式下形成停止-复制,需要先暂停程序运行,将活的对象复制到另一个堆/空块,此时还会重新紧密排列,可以按照前面描述的那样简单、直接地分配新空间。

  缺点:效率低,所有指向它的引用都必须修正。

  1,得有两个堆,然后在这两个分离的堆之间来回折腾,得维护比实际需要多一倍的空间。某些 Java 虚拟机对此问题的处理方式是,按需从堆中分配几块较大的内存,复制动作发生在这些大块内存之间。

  2,程序稳定复制减少,垃圾减少或无,此时复制浪费时间占用资源。解决:要是没有新垃圾产生,就会转换到另一种模式“标记-清扫”。

 

2.4 标记-清扫

  原理,遍历标记存活对象,但不回收,遍历完统一处理,未标记直接清理,无复制过程。

  缺点,剩下的堆不连续,要连续空间就需要重新整理剩下的对象。

2.5 注意

  1 垃圾回收不是后台处理,而是需要程序停止,优先级很低。

  2 当可用内存较低时,垃圾回收器会暂停程序。在具体实施时,标记清扫和停止复制会根据垃圾回收效率自适应切换。

  3 "即时"(Just-In-Time, JIT)编译器。Java虚拟机提高速度的附加技术,可以把程序全部或部分翻译成本地机器码,所以不需要 JVM 来进行翻译,因此运行得更快。

当需要装载某个类(通常是创建该类的第一个对象)时,编译器会先找到其 .class 文件,然后将该类的字节码装入内存。你可以让即时编译器编译所有代码,但这种做法有两个缺点:一是这种加载动作贯穿整个程序生命周期内,累加起来需要花更多时间;二是会增加可执行代码的长度(字节码要比即时编译器展开后的本地机器码小很多),这会导致页面调度,从而一定降低程序速度。另一种做法称为惰性评估,意味着即时编译器只有在必要的时候才编译代码。这样,从未被执行的代码也许就压根不会被 JIT 编译。新版 JDK 中的 Java HotSpot 技术就采用了类似的做法,代码每被执行一次就优化一些,所以执行的次数越多,它的速度就越快。

 

  参考:On Java 8

 

posted @ 2020-12-18 22:42  卡卡北  阅读(110)  评论(0)    收藏  举报