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内存分代策略 - 风中程序猿 - 博客园
          相关文章
          |
          8天前
          |
          缓存 算法 Java
          JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
          这篇文章详细介绍了Java虚拟机(JVM)中的垃圾回收机制,包括垃圾的定义、垃圾回收算法、堆内存的逻辑分区、对象的内存分配和回收过程,以及不同垃圾回收器的工作原理和参数设置。
          29 4
          JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
          |
          6天前
          |
          存储 监控 算法
          美团面试:说说 G1垃圾回收 底层原理?说说你 JVM 调优的过程 ?
          尼恩提示: G1垃圾回收 原理非常重要, 是面试的重点, 大家一定要好好掌握
          美团面试:说说 G1垃圾回收 底层原理?说说你 JVM 调优的过程  ?
          |
          4天前
          |
          存储 监控 算法
          Java中的内存管理与垃圾回收机制解析
          本文深入探讨了Java编程语言中的内存管理方式,特别是垃圾回收机制。我们将了解Java的自动内存管理是如何工作的,它如何帮助开发者避免常见的内存泄漏问题。通过分析不同垃圾回收算法(如标记-清除、复制和标记-整理)以及JVM如何选择合适的垃圾回收策略,本文旨在帮助Java开发者更好地理解和优化应用程序的性能。
          |
          10天前
          |
          算法 Java
          谈谈HotSpot JVM 中的不同垃圾回收器
          【10月更文挑战第5天】理解 HotSpot JVM 中的不同垃圾回收器(如 CMS、G1 和 ZGC)的区别,需要深入了解它们的设计原理、工作方式和应用场景。以下是对这三个垃圾回收器的简要概述以及一个示例 Java 程序,虽然示例程序本身不能直接展示垃圾回收器的内部机制,但可以帮助观察不同垃圾回收器的行为。
          10 1
          |
          13天前
          |
          算法 Java 开发者
          Java中的垃圾回收机制:从原理到实践
          Java的垃圾回收机制(Garbage Collection, GC)是其语言设计中的一大亮点,它为开发者提供了自动内存管理的功能,大大减少了内存泄漏和指针错误等问题。本文将深入探讨Java GC的工作原理、不同垃圾收集器的种类及它们各自的优缺点,并结合实际案例展示如何调优Java应用的垃圾回收性能,旨在帮助读者更好地理解和有效利用Java的这一特性。
          |
          15天前
          |
          监控 算法 Java
          Java中的内存管理:理解垃圾回收机制
          【10月更文挑战第2天】 在本文中,我们将深入探讨Java编程语言中的内存管理机制,特别是垃圾回收机制。我们将从基本原理、垃圾回收算法到实际应用场景全面解析,帮助你更好地理解和优化Java应用的内存使用。无论你是初学者还是有经验的开发者,这篇文章都能带给你新的启发和思考。
          26 2
          |
          25天前
          |
          算法 Java 程序员
          深入理解Java的垃圾回收机制
          【9月更文挑战第31天】在Java的世界里,有一个默默守护者,它负责清理不再使用的对象,确保内存的有效利用。这就是垃圾回收器(Garbage Collector, GC)。本文将带你一探究竟,了解它是如何工作的,以及为何我们需要关心它的存在。
          |
          27天前
          |
          监控 Java 程序员
          深入理解Java中的垃圾回收机制
          【9月更文挑战第29天】在Java编程的海洋中,垃圾回收(Garbage Collection, GC)是维持内存健康的灯塔。本文将带你探索GC的奥秘,从它的本质、工作机制到优化策略,让你的代码像海豚一样优雅地畅游。
          |
          7天前
          |
          存储 监控 算法
          深入理解Java内存模型与垃圾回收机制
          【10月更文挑战第10天】深入理解Java内存模型与垃圾回收机制
          12 0
          |
          7天前
          |
          监控 算法 Java
          Java中的垃圾回收机制深度解析
          【10月更文挑战第10天】 本文深入探讨了Java语言核心特性之一的垃圾回收机制(Garbage Collection, GC),揭示了其在内存管理中的关键角色。通过对GC的工作原理、分类、算法以及调优策略的细致分析,旨在帮助开发者更好地理解并有效利用这一机制,提升Java应用的性能与可靠性。不同于常规摘要,本文聚焦于为读者提供一份关于Java GC全面而深入的解读,助力把握Java内存管理的精髓。