常见的垃圾回收机制

如何工作

在某些 Java 虚拟机中,堆的实现截然不同:它更像一个传送带,每分配一个新对象,它就向前移动一格。
这意味着对象存储空间的分配速度特别快。Java 的”堆指针”只是简单地移动到尚未分配的区域,所以它的效率与 C++ 在栈上分配空间的效率相当

垃圾回收器工作时,一边 回收内存,一边 使堆中的对象紧凑排列,这样 堆指针就可以更容易的移动到空闲区域的位置上
垃圾收集器在分配存储空间的同时会将对象重新排列,由此实现一个高速的、有无限空闲空间的堆模型。

  1. 对象可能不被垃圾回收
  2. 垃圾回收不等于析构
  3. 垃圾回收只与内存有关
    4.

  4. 无论是”垃圾回收”还是”终结” ,都不一定保证会发生;

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

finalize()方法 (有个大概印象即可)

finalize()方法

需要使用finalize()的特殊情况:你创建的对象不是通过new来分配内存的,而垃圾回收器只知道如何释放用new创建的对象内存。java允许在类中定义一个名为finalize()的方法(继承与Object)来处理这种情况
finalize()方法 的使用案例

释放 通过某种创建对象方式之外的方式 为对象分配的存储空间

解释:上边的情况 主要发生在使用本地方法的情况下,本地方法主要支持C/C++,这也许会调用C的malloc()函数来分配空间,这时,如果没用调用free()方法主动释放malloc到的空间,就会造成内存泄漏,所以需要在finalize()方法中调用free

终结条件

安全的标记对象是否可以被终结,例如下例 要销毁未被登记的Book对象时,报错

System.gc();强制终结

class Book{
    boolean checkedOut = false;

    public Book(boolean checkOut) {
        this.checkedOut = checkOut;
    }

    void checkIn(){
        this.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 JavaTest{
    @Test
    public void test8() throws InterruptedException {
        Book book = new Book(true);
        book.checkIn();

        new Book(true);

        System.gc();

        Thread.sleep(1000);
    }

}

垃圾回收机制

引用计数

原理:每个对象中都有一个引用计数器,每当有引用指向该对象时,引用计数器+1;当引用离开作用域或是被置为null时,引用计数器-1;如果发现某个对象的引用计数为0时,就释放其空间(引用计数器模式经常会在计数器为0时立即释放对象)

特点

  1. 简单
  2. 速度慢(开销不大,但在整个生命周期频繁发生的负担)

    在每次内存对象被引用或引用被销毁的时候都必须修改引用计数,这类操作被称为footprint。引用计数的footprint是很高的。这使得程序整体的性能受到比较大的影响

  3. 出现循环引用时会出问题(应该被回收,但没有)
  4. .btw:”引用计数通常用于解释垃圾收集的工作方式,但它似乎并没有出现在任何JVM实现中”

更快的策略依据

对于任何没有被废弃的对象,最终都能追溯到它存活在 静态存储区中的引用
因此,如果从栈和静态存储区开始遍历所有引用(包括对象内部的),就能找到所有存活的对象
匿名对象的相关思考:我们通常将匿名对象作为方法参数传递,func(new B()),在该方法中,B对象被关联了引用

所以,遍历所有的引用即可得到所有”活”的对象,然后再去遍历这些对象中的引用,如此反复,就能得到一个对象网络,其中的对象就都是 活 的;

循环引用示例

class A{
  public B b;
}
class B{
  public A a;
}
public class Main{
    public static void main(String[] args){
    A a = new A();  // A对象被引用 A对象计数器+1
    B b = new B();  // B对象被引用 B对象计数器+1
    a.b=b;          // B对象被引用 B对象计数器+1
    b.a=a;          // A对象被引用 A对象计数器+1
    }
}

上例 当栈上的引用遍历到a,发现a对象中有个B对象的引用,指向b,这个b引用中又有个A的引用 又指向a 如此循环 根本找不到活的对象

这样也解决了循环引用问题,循环引用的对象不会被发现

停止-复制(stop-and-copy)

原理先暂停程序的运行,然后将所有存活的对象从当前堆复制到另一个堆,没有被复制的就是需要垃圾回收的,而且,当对象被复制到新堆后,他们紧凑排列的,一个挨着一个

特点

  1. 解决了循环引用的问题(循环引用的对象不会被发现)
  2. 非后台回收模式(需要在 程序暂停的情况下进行)
  3. 相对较快
  4. 效率低下
  5. 得有两个堆:还得在这两个分离的堆之间来回折腾,得维护比实际多一倍的空间
    • 某些jvm的解决方式:按需在堆中分配几块较大的内存,复制动作就发生在这大内存间
  6. 复制本身:一旦程序进入稳定状态,可能只会产生少量垃圾,甚至没有,就算是这,复制回收器也得将所有 内存从一处复制到另一处,太浪费了
    • 某些JVM的解决方式:进行检查,要是没有新的垃圾产生,就会转换到”标记-清扫”模式(SUN公司早期JVM一直在用)。对一般用途,这种方法很慢,但当程序只会产生少量或者不产生垃圾,他速度就很快了
    • 切换到 标记清扫方式清理

标记-清扫

原理:标记: 从栈和静态存储区出发,遍历所有引用,找出所有”活”对象,每找到一个,就给对象一个标记,直到遍历完所有对象;清扫:没有被标记的对象将被清理。

特点

  1. 解决了循环引用的问题(循环引用的对象不会被发现)
  2. 相比停止-复制 较为高效(不会有任何复制动作)
  3. 非后台回收模式(需要在 程序暂停的情况下进行)
  4. 处理完后剩下的堆空间是不连续的
  5. 垃圾回收器若希望得到连续的空间,就需要整理剩下的未被清理的对象

补充:标记-整理

原理:标记:从栈和静态区域出发,遍历所有引用,对遍历过程中得到的活的对象进行标记;整理:将标记过的对象,把他们从内存开始的地方按照顺序依次摆放好,中间没有任何缝隙,在摆放完最后一个对象后,对后面的内存区域直接回收。

JVM中的块(感觉像是年龄分代)

  • 内存分配以较大的”块”为单位,较大的对象会单独占一个块。
  • 块用年代数来记录自己是否存活,若块被引用,年代数+1。
  • 垃圾回收器会对上一次回收过后 新分配的块进行整理
  • 垃圾回收器定期会清理
  • 含有小对象的那些块将被复制、整理
  • 大型对象 不会被复制(只有年代数会增加)

补充:分代收集算法

摘自JavaGuide

当前虚拟机的垃圾收集都采用分代收集算法,这种算法没有什么新的思想,只是根据对象存活周期的不同将内存分为几块。一般将 java 堆分为新生代和老年代,这样我们就可以根据各个年代的特点选择合适的垃圾收集算法。

比如在新生代中,每次收集都会有大量对象死去,所以可以选择”标记-复制”算法,只需要付出少量对象的复制成本就可以完成每次垃圾收集。而老年代的对象存活几率是比较高的,而且没有额外的空间对它进行分配担保,所以我们必须选择”标记-清除”或”标记-整理”算法进行垃圾收集。

标记清扫与停止复制的切换机制(自适应)

如果 对象一个个的都很稳定,此时如果 垃圾回收效率降低了,就会 切换到”标记-清扫”模式,以提高垃圾回收的效率,(此时jvm持续监视) 如果 堆中的碎片多了,那么就赶紧 切换回”停止-复制”模式,以真整理堆空间,保持堆的高速运转。

HotSpot技术

java执行步骤

常见的垃圾回收机制
即时编译技术(Just-in Time JIT)

想要了解更多请移步什么是JIT?怎么优化? – 知乎 (zhihu.com)

热点代码分为两类:

  • 多次调用的方法
  • 多次执行的循环体,实际上也会以整个方法作为编译对象

如何判断热点代码请看原文

可以把 全部或部分 程序 直接翻译为本地机器码,这就省去了JVM翻译,所以运行更快

为什么不能用这个技术 编译所有代码?

  • 这个技术贯穿整个程序的生命周期,累加起来慢的要死
  • 会增加可执行代码的长度(字节码要比即时编译器展开后的本地机器码小很多),导致页面调度,使程序运行变慢
  • 即时编译时以方法为单位的,如果编译出来的代码运行不了几次,就白编译了
惰性评估

即时编译器只有在必要时,才编译代码

这样,从未被执行的代码也许就压根不会被 JIT 编译。

新版 JDK 中的 Java HotSpot 技术就采用了类似的做法,代码每被执行一次就优化一些,所以执行的次数越多,它的速度就越快。

逃逸分析技术

请移步至深入理解Java中的逃逸分析

逃逸分析的基本行为就是分析对象动态作用域:当一个对象在方法中被定义后,它可能被外部方法所引用,例如作为调用参数传递到其他地方中,称为方法逃逸。

Original: https://www.cnblogs.com/daydreamer-fs/p/16747958.html
Author: fogey
Title: 常见的垃圾回收机制

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/714485/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • GPS NEMA 0183协议

    注:发送次序$GPZDA、$GPGGA、$GPGLL、$GPVTG、$GPGSA、$GPGSV*3、$GPRMC 如:$aaccc,ddd,ddd,…,ddd*hh 1…

    技术杂谈 2023年5月31日
    093
  • 使用mybatis-plus转换枚举值

    1. 使用mybatis-plus转换枚举值 枚举值转换方式有很多,有以下方式: 后端写一个通用方法,只要前端传枚举类型,后端返回相应的枚举值前端去匹配 优点:能够实时保持数据一致…

    技术杂谈 2023年7月25日
    069
  • Chromium 命名规范

    Chromium 代码中的文件数不胜数,读懂文件名可以帮我们快速定位某个文件的用途。好的文件命名方式应该是自解释的,能够实现 “望文生义” 的效果。不过 C…

    技术杂谈 2023年5月31日
    074
  • Centos7:maven打包构建项目失败,No compiler is provided in this environment.Perhaps。。。

    环境: Centos7 如果你输入 javac -version,没有显示版本信息,那么大概率解决方法是这个 yum install java-devel安装原生的: 再次运行 m…

    技术杂谈 2023年7月24日
    096
  • Mock学习记录

    使用 mockjs + rap 也是很方便的。 Mock.js http://mockjs.com/ https://www.jianshu.com/p/f3adb1aab09e …

    技术杂谈 2023年5月31日
    082
  • tar、gzip、zip、jar是什么,怎么查看?

    原创:扣钉日记(微信公众号ID:codelogs),欢迎分享,转载请保留出处。 如果你是后端程序员,我想你一定见过 *.tar.gz、 *.zip、 *.jar后缀的文件吧,这些都…

    技术杂谈 2023年7月25日
    080
  • 读取PBOC电子现金指令流

    该指令流仅适用于T=0协议卡片. 终端对IC卡的响应: 60 须要额外的工作等待时间,说明IC卡端数据还未处理好. 61 发送GET RESPONSE命令取应答数据 6C 加上取字…

    技术杂谈 2023年5月31日
    072
  • node 防抖或拒绝服务 连接池

    generic-pool https://www.npmjs.com/package/generic-pool?fileGuid=xxQTRXtVcqtHK6j8 Original…

    技术杂谈 2023年5月31日
    089
  • Excel中*替换为空

    查找输入~*替换不输入全部替换将 * 这个替换成空白 Original: https://www.cnblogs.com/gisoracle/p/16309234.htmlAuth…

    技术杂谈 2023年5月30日
    0104
  • openwrt临时封禁ip

    用的openwrt路由器,家里宽带申请了动态公网ip,为了方便把22 80端口映射到公网,发现经常被暴力破解,自己写了个临时封禁ip功能的脚本,实现5分钟内同一个ip登录密码错误1…

    技术杂谈 2023年7月25日
    082
  • 关于shape和axis的使用

    我自己对shape和axis的理解: shape表示的是维度,表示顺序是从外到内,比如一个Dataframe的形状是(4,5)那么shape[0]=4即Dataframe有4行,s…

    技术杂谈 2023年7月11日
    079
  • docker安装es-header及相关问题解决

    docker pull mobz/elasticsearch-head:5 docker run –restart=always –name elasticsearch-hea…

    技术杂谈 2023年7月24日
    084
  • 让你的Nginx支持分布式追踪opentracing

    404. 抱歉,您访问的资源不存在。 可能是网址有误,或者对应的内容被删除,或者处于私有状态。 代码改变世界,联系邮箱 contact@cnblogs.com 园子的商业化努力-困…

    技术杂谈 2023年7月25日
    073
  • 错误域控降级导致解析问题

    近两天在给分部安装辅助域控的时候,总是安装不成功,或者安装时成功了但是无法复制主域或者其他域控的信息,同步失败,还有就是它一直没有网。 解决方案 经过排查发现域名dns解析不对,经…

    技术杂谈 2023年6月21日
    084
  • Hazelcast

    声明 本系列文章为学习Hazelcast的笔记,内容大部分都为官方文档翻译,如果对Hazelcast感兴趣,可移步Hazelcast查看官方原版文档。 学习目的 公司项目服务化改造…

    技术杂谈 2023年6月1日
    0103
  • 《深度工作:如何有效使用每一点脑力》读后感

    空闲时间阅读了一下《深度工作:如何有效使用每一点脑力》,作为一个沉迷网络的人,已经很难有聚精会神的时候,所以阅读此书,记录一下读后感,争取应用到生活当中。全书分为两个方面进行说明:…

    技术杂谈 2023年6月21日
    078
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球