zoukankan      html  css  js  c++  java
  • 什么是垃圾回收

    乍一看,垃圾回收所做的事情应当恰如其名——查找并清除垃圾。

    其实却恰恰相反。垃圾回收会跟踪全部仍在使用的对象,然后将剩余的对象标记为垃圾。牢记了这点之后,我们再来深入地了解下这个被称为“垃圾回收”的自己主动化内存回收在JVM中究竟是如何实现的。

    手动管理内存

    在介绍现代版的垃圾回收之前,我们先来简单地回想下须要手动地显式分配及释放内存的那些日子。假设你忘了去释放内存,那么这块内存就无法重用了。这块内存被占有了却没被使用。

    这样的场景被称之为内存泄露。

    以下是用C写的一个手动管理内存的简单样例:

    int send_request() {
        size_t n = read_size();
        int *elements = malloc(n * sizeof(int));
    
        if(read_elements(n, elements) < n) {
            // elements not freed!
            return -1;
        }
    
        // …
    
        free(elements)
        return 0;
    }

    能够看到,你非常easy就会忘了释放内存。内存泄露以前是个非常普遍的问题。你仅仅能通过不断地修复自己的代码来与它们进行抗争。

    因此,须要有一种更优雅的方式来自己主动释放无用内存。以便降低人为错误的可能性。这样的自己主动化过程又被称为垃圾回收(简称GC)。

    智能指针

    自己主动垃圾回收早期的一种实现便是引用计数

    你知晓每个对象被引用了几次,当计数器归0的时候,这个对象就能够被安全地回收掉了。C++的共享指针就是一个非常著名的样例:

    int send_request() {
        size_t n = read_size();
        shared_ptr<vector<int>> elements 
                  = make_shared<vector<int>&gt();
    
        if(read_elements(n, elements) < n) {
            return -1;
        }
    
        return 0;
    }

    我们使用的shared_ptr会记录这个对象被引用的次数。假设你将它传递给别人则计数加一,当它离开了作用域后便会减一。一旦这个计数为0shared_ptr会自己主动地删除底层相应的vector

    当然这仅仅是个演示样例,由于也有读者指出来了。这个在现实中是不太可能出现的,但作为演示是足够了。

    自己主动内存管理

    在上面的C++代码中。我们还得显式地声明我们须要使用内存管理。那假设全部的对象都採用这个机制会如何呢?那简直就太方便了。这样开发者便无需考虑清理内存的事情了。执行时会自己主动知晓哪些内存不再使用了,然后释放掉它。也就是说,它自己主动地回收了这些垃圾。

    第一代的垃圾回收器是1959Lisp引入的,这项技术迄今为止一直在不断演进。

    引用计数

    刚才我们用C++的共享指针所演示的想法能够应用到全部的对象上来。很多语言比方说Perl, Python以及PHP,採用的都是这样的方式。

    这个通过一张图能够非常easy说明:

    绿色的云代表的是程序中仍在使用的对象。从技术层面上来说。这有点像是正在执行的某个方法里面的局部变量,亦或是静态变量之类的。不同编程语言的情况可能会不一样。因此这并非我们关注的重点。

    蓝色的圆圈代表的是内存中的对象,能够看到有多少对象引用了它们。灰色圆圈的对象是已经没有不论什么人引用的了。因此,它们属于垃圾对象,能够被垃圾回收器清理掉。

    看起来还不错对吧?没错,只是这里存在着一个重大的缺陷。非常easy会出现一些孤立的环。它们中的对象都不在不论什么域内。但彼此却互相引用导致引用数不为0。以下便是一个样例:

    看到了吧,红色部分其实就是应用程序不再使用的垃圾对象。由于引用计数的缺陷。因此会存在内存泄露。

    有几种方法能够解决这一问题。比方说使用特殊的“弱”引用。或者使用一个特殊的算法回收循环引用。

    之前提到的Perl,Python以及PHP等语言,都是使用相似的方法来回收循环引用的,只是这已经超出本文讲述的范围了。我们准备具体介绍下JVM所採用的方法。

    标记删除

    首先,JVM对于对象可达性的定义要明白一些。它可不像前面那样用绿色的云便含糊了事的,而是有着非常明白及具体的垃圾回收根对象(Garbage Collection Roots)的定义:

    • 局部变量
    • 活动线程
    • 静态字段
    • JNI引用
    • 其他(后面将会讨论到)

    JVM通过标记删除的算法来记录全部可达(存活)对象,同一时候确保不可达对象的那些内存能够被重用。这包括两个步骤:

    1. 标记是指遍历全部可达对象。然后在本地内存中记录这些对象的信息
    2. 删除会确保不可达对象的内存地址能够在下一次内存分配中使用。

    JVM中的不同GC算法。比方说Parallel ScavengeParallel Mark+CopyCMS都是这一算法的不同实现,仅仅是各阶段略有不同而已。从概念上来讲仍然是相应着上面所说的那两个步骤。

    这样的实现最重要的就是不会再出现泄露的对象环了:

    缺点就是应用程序的线程须要被暂停才干完毕回收。假设引用一直在变的话你是无法进行计数的。这个应用程序被暂停以便JVM能够收拾家务的情况又被称为Stop The World pause(STW)。这样的暂停被触发的可能性有非常多。只是垃圾回收应该是最常见的一种。

  • 相关阅读:
    MIne FirstBlog
    P6563 [SBCOI2020]一直在你身旁
    P6563 [SBCOI2020]一直在你身旁
    T122085 [SBCOI2020]时光的流逝
    LC 918. Maximum Sum Circular Subarray
    1026 Table Tennis
    LC 1442. Count Triplets That Can Form Two Arrays of Equal XOR
    LC 1316. Distinct Echo Substrings
    LC 493. Reverse Pairs
    1029 Median (二分)
  • 原文地址:https://www.cnblogs.com/lcchuguo/p/5164166.html
Copyright © 2011-2022 走看看