Java虚拟机-垃圾回收简介

简介: Java虚拟机-垃圾回收简介

 

image.gif编辑

一、如何判定对象为垃圾对象

-verbose:gc  打印垃圾回收简单信息参数
-xx:+PringDCDetail 打印垃圾回收的详细信息

image.gif

引用计数法

引用计数算法很简单,它实际上是通过在对象头中分配一个空间来保存该对象被引用的次数。如果该对象被其它对象引用,则它的引用计数加一,如果删除对该对象的引用,那么它的引用计数就减一,当该对象的引用计数为0时,那么该对象就会被回收。

采用引用计数的垃圾收集机制中,垃圾收集的开销被分摊到整个应用程序的运行当中了,而不是在进行垃圾收集时,要挂起整个应用的运行,直到对堆中所有对象的处理都结束。因此,采用引用计数的垃圾收集不属于严格意义上的"Stop-The-World"的垃圾收集机制。

引用计数算法有一个比较大的问题,那就是它不能处理环形数据,即如果有两个对象相互引用,那么这两个对象就不能被回收,因为它们的引用计数始终为1。这也就是我们常说的“内存泄漏”问题。

Python中采用的是引用计数机制为主,标记-清除和分代收集两种机制为辅的策略。

可达性分析法

为了引用计数算法中循环引用导致垃圾不会被回收的问题,在Java中采取了 可达性分析法。同样采用此法的还有C#、Lisp(最早的一门采用动态内存分配的语言)。该方法的基本思想是通过一系列的“GC Roots”对象作为起点进行搜索,如果在“GC Roots”和一个对象之间没有可达路径,则称该对象是不可达的,不过要注意的是被判定为不可达的对象不一定就会成为可回收对象。被判定为不可达的对象要成为可回收对象必须至少经历两次标记过程,如果在这两次标记过程中仍然没有逃脱成为可回收对象的可能性,则基本上就真的成为可回收对象了。

GC Roots的对象

    • 虚拟机栈(栈帧中的本地变量表)中引用的对象。(可以理解为:引用栈帧中的本地变量表的所有对象)
    • 方法区中静态属性引用的对象(可以理解为:引用方法区该静态属性的所有对象)
    • 方法区中常量引用的对象(可以理解为:引用方法区中常量的所有对象)
    • 本地方法栈中(Native方法)引用的对象(可以理解为:引用Native方法的所有对象)

    二、如何回收

    JVM的内存分代划分

    Java虚拟机将堆内存划分为新生代老年代永久代。

    image.gif编辑

    新生代(Young)

    HotSpot将新生代划分为三块,一块较大的Eden空间和两块较小的Survivor空间,默认比例为8:1:1。划分的目的是因为HotSpot采用复制算法来回收新生代,设置这个比例是为了充分利用内存空间,减少浪费。新生成的对象在Eden区分配(大对象除外,大对象直接进入老年代),当Eden区没有足够的空间进行分配时,虚拟机将发起一次Minor GC。

    老年代(Old)

    在新生代中经历了多次(具体看虚拟机配置的阀值)GC后仍然存活下来的对象会进入老年代中。老年代中的对象生命周期较长,存活率比较高,在老年代中进行GC的频率相对而言较低,而且回收的速度也比较慢。

    永久代(Permanent)

    永久代存储类信息、常量、静态变量、即时编译器编译后的代码等数据,对这一区域而言,Java虚拟机规范指出可以不进行垃圾收集,一般而言不会进行垃圾回收。

    回收策略

    1.标记-清除算法(Mark-Sweep)

    算法分为“标记”和“清除”两个阶段:首先标记出所有需要回收的对象,在标记完成后统一回收掉所有被标记的对象。

    标记-清除算法的主要缺点:

      • 效率问题:标记和清除过程的效率都不高;
      • 空间问题:标记清除之后会产生大量不连续的内存碎片,空间碎片太多可能会导致,碎片过多会导致大对象无法分配到足够的连续内存,从而不得不提前触发GC,甚至Stop The World。

      image.gif编辑

      2.复制算法(Copying)

      为解决效率问题,“复制”收集算法出现了。它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。

      这样使得每次都是对其中的一块进行内存回收,内存分配时也就不用考虑内存碎片等复杂情况,只要移动堆顶指针,按顺序分配内存即可,实现简单,运行高效。

      它的主要缺点有两个:

        • 效率问题:在对象存活率较高时,复制操作次数多,效率降低;
        • 空间问题:內存缩小了一半;需要使用老年代的額外空间做分配担保。

        From Survivor, To Survivor使用的就是复制算法。老年代不使用这种算法。

        image.gif编辑

        3.标记-整理算法

        复制收集算法在对象存活率较高时就要执行较多的复制操作,效率将会变低。更关键的是,如果不想浪费50%的空间,就需要有额外的空间进行分配担保,以应对被使用的内存中所有对象都100%存活的极端情况,所以在老年代一般不能直接选用这种算法。    

        根据老年代的特点,有人提出了另外一种“标记-整理”(Mark-Compact)算法,标记过程仍然与“标记-清除”算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。也称为标记-整理-清除算法。

        image.gif编辑

        4.分代收集算法(Generational Collection)

        GC分代的基本假设:绝大部分对象的生命周期都非常短暂,存活时间短。

        “分代收集”算法,把Java堆分为新生代和老年代,这样就可以根据各个年代的特点采用最适当的收集算法。在新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,那就选用复制算法,只需要付出少量存活对象的复制成本就可以完成收集。而老年代中因为对象存活率高、没有额外空间对它进行分配担保,就必须使用“标记-清理”或“标记-整理”算法来进行回收。

        参考资料:

          1. 引用计数算法 - 简书
          2. JAVA垃圾回收-可达性分析算法_这瓜保熟么的博客-CSDN博客_可达性分析算法
          3. JVM 之(4)垃圾回收算法(标记 -清除、复制、标记-整理、分代收集)_夏目 "的博客-CSDN博客_标记清除算法
          4. Java虚拟机:JVM内存分代策略 - 风中程序猿 - 博客园
          相关文章
          |
          4天前
          |
          存储 算法 Java
          先有JVM还是先有垃圾回收器?
          是先有垃圾回收器再有JVM呢,还是先有JVM再有垃圾回收器呢?或者是先有垃圾回收再有JVM呢?历史上还真是垃圾回收更早面世,先有垃圾回收再有JVM。下面我们就来刨析刨析JVM的垃圾回收~
          13 0
          先有JVM还是先有垃圾回收器?
          |
          2天前
          |
          Java 虚拟化 Docker
          Docker简介及用途,为什么要使用Docker?Docker容器和虚拟机的区别
          Docker简介及用途,为什么要使用Docker?Docker容器和虚拟机的区别
          |
          3天前
          |
          存储 算法 Java
          Java一分钟之-Java内存模型与垃圾回收机制概览
          【5月更文挑战第16天】本文简述Java内存模型(JMM)和垃圾回收(GC)机制。JMM包括栈、堆、方法区、程序计数器和本地方法栈。GC负责回收不再使用的对象内存,常用算法有新生代、老年代和全堆GC。文章讨论了内存溢出、死锁和GC性能等问题,提出了解决方案,如调整JVM参数和优化GC策略。此外,还强调了避免内存泄漏、大对象管理及正确释放资源的重要性。理解这些概念有助于提升Java应用的性能和稳定性。
          13 1
          |
          4天前
          |
          SQL Java API
          Java一分钟之-JPA:Java持久化API简介
          【5月更文挑战第14天】Java Persistence API (JPA) 是Java的ORM规范,用于简化数据库操作。常见问题包括实体映射、事务管理和性能问题。避免错误的关键在于明确主键策略、妥善使用事务、优化查询及理解实体生命周期。示例展示了如何定义实体和使用`EntityManager`保存数据。JPA通过标准化API让开发者更专注于业务逻辑,提升开发效率和代码维护性。
          14 0
          |
          4天前
          |
          安全 Oracle 小程序
          01|Java简介与历史
          01|Java简介与历史
          11 0
          |
          4天前
          |
          安全 算法 Java
          深入浅出JVM(十三)之垃圾回收算法细节
          深入浅出JVM(十三)之垃圾回收算法细节
          |
          4天前
          |
          存储 算法 Java
          深入浅出JVM(十二)之垃圾回收算法
          深入浅出JVM(十二)之垃圾回收算法
          |
          4天前
          |
          算法 Java PHP
          JVM 的垃圾回收机制以及垃圾回收算法的详解
          JVM 的垃圾回收机制以及垃圾回收算法的详解
          10 0
          |
          4天前
          |
          存储 算法 Java
          了解Java内存管理与垃圾回收机制
          了解Java内存管理与垃圾回收机制
          8 0
          |
          4天前
          |
          监控 算法 安全
          JVM工作原理与实战(三十九):G1垃圾回收器原理
          JVM作为Java程序的运行环境,其负责解释和执行字节码,管理内存,确保安全,支持多线程和提供性能监控工具,以及确保程序的跨平台运行。本文主要介绍了G1垃圾回收器执行流程、年轻代回收原理、卡表(Card Table)、记忆集的生成流程、年轻代回收的详细步骤、混合回收的步骤、初始标记、并发标记、SATB、转移等内容。
          23 0