博客
关于我
JVM虚拟机-垃圾回收机制与垃圾收集器概述
阅读量:714 次
发布时间:2019-03-17

本文共 3674 字,大约阅读时间需要 12 分钟。

Java垃圾回收:从基础到深入

垃圾回收(Garbage Collection, GC)是Java虚拟机(JVM)中最为关键的功能之一。它的主要职责是管理内存,确保程序能够高效运行。作为一名开发人员,了解垃圾回收的工作原理对于优化内存使用和解决内存泄漏问题至关重要。本文将从基础到深入探讨Java垃圾回收的各个方面。

垃圾回收的定义

垃圾回收是JVM用于管理对象内存的机制。它的核心作用是清除不再使用的对象,这些对象占用的内存被称为“垃圾”。当内存资源有限时,垃圾回收器会介入,将这些无用的对象从内存中移除,释放内存空间供新对象使用。

垃圾回收的历史可以追溯到1960年,当时由MIT的Lisp语言引入。从那时起,垃圾回收技术不断发展,不仅在Java中得到了广泛应用,也成为其他语言垃圾回收的重要参考。

垃圾回收的区域

在Java中,垃圾回收主要管理的是对象内存。JVM将内存划分为多个区域,其中最重要的区域是Java堆(Heap)。由于垃圾回收器负责管理Java堆内存,Java堆也被称为垃圾收集器管理的区域(Garbage Collected Heap)。

为了更好地实现垃圾回收,Java堆被进一步划分为新生代和老生代:

  • 新生代(Eden + Survivor):新生代占整个Java堆的1/3,对象在这里分配内存时需要担保。如果内存不足,垃圾回收器会触发新生代垃圾回收(Minor GC)。

  • 老生代(Survivor):老生代占整个Java堆的2/3,主要存放已有较多年龄的对象。老生代中还划分为From Survivor和To Survivor两个区域,用于对象的复制和存活。

这种划分方式的目的是为了在垃圾回收时更高效地管理内存。

垃圾回收的机制

垃圾回收的流程可以分为以下几个阶段:

  • 内存分配:对象首先会在Eden区域分配内存。当Eden区域不足时,垃圾回收器会触发Minor GC。

  • 大对象分配:对于大对象,JVM会直接将它们放入老生代,以避免为大对象分配内存时的额外开销。

  • 对象晋升:在Minor GC后,如果对象仍然存活,它会进入Survivor区域中的一个。对象的年龄会增加,当达到一定阈值(默认为15)时,对象会被晋升到老生代中。

  • 老年代回收:当老生代内存不足时,垃圾回收器会触发老年代垃圾回收(Major GC)。

  • 垃圾回收在HotSpot实现中主要有两种类型:部分收集(Partial GC)和整堆收集(Full GC)。部分收集包括新生代回收(Minor GC)、老年代回收(Old GC)和混合回收(Mixed GC)。整堆收集则会回收整个Java堆和方法区。

    如何判断对象已经死亡

    判断对象是否已经死亡是垃圾回收的关键步骤。主要使用引用计数法和可达性分析算法。

    引用计数法

    引用计数法通过跟踪对象的引用次数来判断对象是否还在使用。每当有一个地方引用了对象,引用计数器加1;引用失效时,计数器减1。当引用计数器为0时,说明对象已经死亡。

    这种方法简单高效,但无法处理循环引用问题。循环引用是指两个对象互相引用,但没有其他对象使用它们的情况。这种情况会导致垃圾回收器无法正确识别对象的死亡。

    可达性分析算法

    可达性分析算法通过定义GC Roots作为根起点,从这些节点开始向下搜索,找出所有可达的对象。无法到达GC Roots的对象即为不可达对象,可能已经死亡。

    GC Roots包括以下几种对象:

    • 虚拟机栈中的本地变量表引用的对象
    • 本地方法栈中的引用的对象
    • 方法区中类静态属性引用的对象
    • 方法区中常量引用的对象
    • 同步锁持有的对象

    对引用对象的判断

    虽然不可达对象不一定会被回收,但需要经过两次标记才能真正宣告对象死亡:

  • 第一次标记:判断对象是否需要执行finalize方法。
  • 第二次标记:如果对象在第一次标记中被判定为需要执行finalize方法,则会被放入一个队列中。只有当对象与引用链上的任何对象建立关联时,第二次标记才会取消回收。
  • 关于引用,Java在JDK1.2之后引入了强引用、软引用、弱引用和虚引用四种引用类型。这些引用类型的区别在于它们对对象存活的影响程度不同。

    强引用(StrongReference)

    强引用是最强大的引用类型,只有强引用持有的对象不会被垃圾回收,即便是内存不足也不会回收。

    软引用(SoftReference)

    软引用不会阻止垃圾回收器回收对象,但在内存不足时会优先回收。软引用常用于实现内存敏感的高速缓存。

    弱引用(WeakReference)

    弱引用不会阻止垃圾回收器回收对象,只要发现只具有弱引用的对象,垃圾回收器就会直接回收它们。弱引用对象的存活时间较短,因为垃圾回收器优先级较低,可能需要较长时间才能发现这些对象。

    虚引用(PhantomReference)

    虚引用不会阻止垃圾回收器回收对象,但需要与引用队列结合使用。虚引用主要用于跟踪对象被垃圾回收的活动。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收前将虚引用加入引用队列。程序可以通过检查引用队列来了解被引用的对象是否将要被回收。

    对常量池中的废弃常量进行判断

    在字符串常量池中,如果一个字符串常量没有被任何String对象引用,它就是废弃常量。如果在内存回收时需要回收该常量,将会被系统清理出常量池。

    对无用类进行判断

    一个类被称为“无用类”,需要满足以下三个条件:

  • 类的所有实例都已经被回收。
  • 加载该类的ClassLoader已经被回收。
  • 该类对应的Class对象没有被任何地方引用,无法通过反射访问该类的方法。
  • 虚拟机可以对满足上述条件的无用类进行回收,但这并不是所有无用类都会被回收。

    垃圾收集算法

    垃圾收集算法主要有标记-清除算法、标记-复制算法、标记-整理算法和分代收集算法。

    标记-清除算法

    标记-清除算法是垃圾收集的基础算法。它分为两个阶段:首先标记出所有不需要回收的对象,然后清除所有未被标记的对象。虽然简单,但效率低下,容易产生大量空间碎片。

    标记-复制算法

    标记-复制算法是对标记-清除算法的改进。它将内存分为两块,每次使用其中一块。当一块内存使用完后,将还存活的对象复制到另一块,然后清理使用的内存。这种方式减少了内存碎片的产生。

    标记-整理算法

    标记-整理算法是针对老年代的标记算法。它的标记阶段与标记-清除算法一样,但后续阶段不是直接清除内存,而是将存活对象集中到一端,然后清理端边界以外的内存。

    分代收集算法

    分代收集算法根据对象的存活区域划分成新生代和老生代,分别采用不同的收集算法。新生代采用标记-复制算法,老生代采用标记-整理算法。这种划分方式使垃圾回收更高效。

    垃圾收集器

    垃圾收集器是垃圾回收的具体实现。不同的垃圾收集器有不同的特点和适用场景。

    CMS收集器

    CMS(Concurrent Mark Sweep)收集器是一款并发垃圾收集器,旨在最短停顿时间。它使用标记-清除算法,并行标记和清除,提升用户体验。

    Serial收集器

    Serial收集器是垃圾回收的基本实现,采用单线程垃圾收集。它简单高效,但会阻塞其他线程。

    ParNew收集器

    ParNew收集器是Serial收集器的多线程版本,除了使用多线程外,行为与Serial完全一致。它是服务器模式下的首选收集器之一。

    Parallel Scavenge收集器

    Parallel Scavenge收集器注重内存使用率。它与ParNew类似,但更关注CPU资源。在JDK1.8中,默认使用Parallel Scavenge和Parallel Old收集器。

    Serial Old收集器

    Serial Old收集器是Serial收集器的老年代版本,主要用于JDK1.5及以前版本,与Parallel Scavenge搭配使用。

    Parallel Old收集器

    Parallel Old收集器是Parallel Scavenge老年代版本,使用多线程和标记-整理算法,适合注重CPU资源的场合。

    G1收集器

    G1(Garbage-First)收集器是一款高效的垃圾收集器,适合多核、大容量内存的环境。它以极高概率满足GC停顿时间要求,同时具备高吞吐量性能。

    G1收集器的工作原理包括初始标记、并发标记、最终标记和筛选回收。它通过维护优先列表,每次根据允许的收集时间,优先回收价值最大的区域,从而实现高效垃圾回收。

    ZGC收集器

    ZGC收集器与G1类似,但具体实现方式有所不同。对于细节,可以参考相关资料进一步了解。

    参考

    《深入理解Java虚拟机》第三版,是一本关于Java虚拟机的权威书籍,内容丰富,涵盖了Java虚拟机的各个方面。如果你想更深入地了解Java虚拟机和垃圾回收机制,这本书是一个不容错过的好书。

    通过以上内容的学习,你应该对Java垃圾回收有一个全面的了解。垃圾回收是Java开发中非常重要的一部分,了解它的原理和实现,可以帮助你更好地优化内存使用,解决内存泄漏问题,并提升程序性能。

    转载地址:http://sipez.baihongyu.com/

    你可能感兴趣的文章
    nodejs系列之express
    查看>>
    nodejs系列之Koa2
    查看>>
    Nodejs连接mysql
    查看>>
    nodejs连接mysql
    查看>>
    NodeJs连接Oracle数据库
    查看>>
    nodejs配置express服务器,运行自动打开浏览器
    查看>>
    NodeMCU教程 http请求获取Json中文乱码解决方案
    查看>>
    Nodemon 深入解析与使用
    查看>>
    NodeSession:高效且灵活的Node.js会话管理工具
    查看>>
    node~ http缓存
    查看>>
    node不是内部命令时配置node环境变量
    查看>>
    node中fs模块之文件操作
    查看>>
    Node中同步与异步的方式读取文件
    查看>>
    node中的get请求和post请求的不同操作【node学习第五篇】
    查看>>
    Node中的Http模块和Url模块的使用
    查看>>
    Node中自启动工具supervisor的使用
    查看>>
    Node入门之创建第一个HelloNode
    查看>>
    node全局对象 文件系统
    查看>>
    Node出错导致运行崩溃的解决方案
    查看>>
    Node响应中文时解决乱码问题
    查看>>