我们都知道java语言与C语言最大的区别就是内存自动回收,那么JVM是怎么控制内存回收的,这篇文章将介绍JVM垃圾回收的几种算法,从而了解内存回收的基本原理。

stop the world

在介绍垃圾回收算法之前,我们需要先了解一个词“stop the world”,stop the world会在执行某一个垃圾回收算法的时候产生,JVM为了执行垃圾回收,会暂时java应用程序的执行,等垃圾回收完成后,再继续运行。如果你使用JMeter测试过java程序,你可能会发现在测试过程中,java程序有不规则的停顿现象,其实这就是“stop the world”,停顿的时候JVM是在做垃圾回收。所以尽可能减少stop the world的时间,就是我们优化JVM的主要目标。接下来我们看一下目前有哪些常见垃圾回收的算法。

引用计数法

引用计数法顾名思义,就是对一个对象被引用的次数进行计数,当增加一个引用计数就加1,减少一个引用计数就减1。

上图表示3个Teacher的引用指向堆中的Teacher对象,那么Teacher对象的引用计数就是3,以此类推Student对象的引用计数就是2。

上图表示Teacher对象的引用减少为2,Student对象的引用减少为0(减少的原因是该引用指向了null,例如teacher3=null),按照引用计数算法,Student对象的内存空间将被回收掉。

引用计数算法原理非常简单,是最原始的回收算法,但是java中没有使用这种算法,原因有2。1是频繁的计数影响性能,2是它无法处理循环引用的问题。

例如Teacher对象中引用了Student对象,Student对象中又引用了Teacher对象,这种情况下,对象将永远无法被回收。

标记清除

标记清除算法,它是很多垃圾回收算法的基础,简单来说有两个步骤:标记、清除。

标记:遍历所有的GC Roots,并将从GC Roots可达的对象设置为存活对象;

清除:遍历堆中的所有对象,将没有被标记可达的对象清除;

注意上图灰色的对象,因为从GC Root遍历不到它们(尽管它们本身有引用关系,但从GC Root无法遍历到它们),因此它们没有被标记为存活对象,在清除过程中将会被回收。

这里需要注意的是标记清除算法执行过程中,会产生“stop the world”,让java程序暂停等待以保证在标记清除的过程中,不会有新的对象产生。为什么必须暂停java程序呢?举个例子,如果在标记过程完成后,又新产生了一个对象,而该对象已经错过了标记期,那么在接下来的清除流程中,这个新产生的对象因为未被标记,所以将被视为不可达对象而被清除,这样程序就会出错,因此标记清除算法在执行时,java程序将被暂停,产生“stop the world”。

接下来我们总结一下标记清除算法:

1、因为涉及大量的内存遍历工作,所以执行性能较低,这也会导致“stop the world”时间较长,java程序吞吐量降低;

2、我们注意到对象被清除之后,被清除的对象留下内存的空缺位置,造成内存不连续,空间浪费。

接下来我们看一下其他算法能不能改善这些问题?

标记压缩

标记压缩算法你可能已经想到了,它就是在标记清除算法的基础上,增加了压缩过程。

在进行完标记清除之后,对内存空间进行压缩,节省内存空间,解决了标记清除算法内存不连续的问题。

注意标记压缩算法也会产生“stop the world”,不能和java程序并发执行。在压缩过程中一些对象内存地址会发生改变,java程序只能等待压缩完成后才能继续。

复制算法

复制算法简单来说就是把内存一分为二,但只使用其中一份,在垃圾回收时,将正在使用的那份内存中存活的对象复制到另一份空白的内存中,最后将正在使用的内存空间的对象清除,完成垃圾回收。

复制算法相对标记压缩算法来说更简洁高效,但它的缺点也显而易见,它不适合用于存活对象多的情况,因为那样需要复制的对象很多,复制性能较差,所以复制算法往往用于内存空间中新生代的垃圾回收,因为新生代中存活对象较少,复制成本较低。它另外一个缺点是内存空间占用成本高,因为它基于两份内存空间做对象复制,在非垃圾回收的周期内只用到了一份内存空间,内存利用率较低。

小结

以上我们介绍了常见的垃圾回收算法,这些算法各有各的优缺点,但在JVM中并不是单纯的使用特定的算法,而是使用的一种叫垃圾回收器的东西,垃圾回收器可以看做一系列算法的不同组合,在不同的场景使用合适的垃圾回收器,才能起到事半功倍的效果。我们下一篇将介绍垃圾回收器。

参考资料:

《实战Java虚拟机》 葛一鸣

《深入理解Java虚拟机(第2版)》 周志明

深入理解JVM(四)——垃圾回收算法的更多相关文章

  1. 深入理解JVM一垃圾回收算法

    我们都知道java语言与C语言最大的区别就是内存自动回收,那么JVM是怎么控制内存回收的,这篇文章将介绍JVM垃圾回收的几种算法,从而了解内存回收的基本原理. 一.stop the world 在介绍 ...

  2. 深入理解JVM(五) -- 垃圾回收算法

    上篇文章我们了解到哪些内存区域和哪些对象可以被回收,这篇文章我们就来了解一下具体的垃圾回收算法的思路,不讨论具体的实现. 一 最基础算法 标记-清除(Mark-Swap) 为什么说他是最基础的算法,因 ...

  3. JVM(九):垃圾回收算法

    JVM(九):垃圾回收算法 在本文中,我们将从概念模型的角度探讨 JVM 是如何回收对象,包括 JVM 是如何判断一个对象已经死亡,什么时候在哪里进行了垃圾回收,垃圾回收有几种核心算法,每个算法优劣是 ...

  4. jvm详情——3、JVM基本垃圾回收算法回收策略

    JVM基本垃圾回收算法回收策略 引用计数(Reference Counting):比较古老的回收算法.原理是此对象有一个引用,即增加一个计数,删除一个引用则减少一个计数.垃圾回收时,只用收集计数为0的 ...

  5. JVM G1垃圾回收算法简要介绍

    JVM G1垃圾回收算法简要介绍 G1的特点 能够像CMS垃圾回收算法一样并发操作应用线程(潜台词:多核) 无需太长时间即可压缩空闲内存空间(潜台词:不会引起太多的GC停顿时间) 尽可能地让GC时长可 ...

  6. JVM学习--(四)垃圾回收算法

    我们都知道java语言与C语言最大的区别就是内存自动回收,那么JVM是怎么控制内存回收的,这篇文章将介绍JVM垃圾回收的几种算法,从而了解内存回收的基本原理. stop the world 在介绍垃圾 ...

  7. 深入理解JVM(四) -- 垃圾内存回收的判定方法和内容

    上一篇文章我们学到了对象在内存中是如何存储的已经是如何被访问的,这篇文章将介绍当内存空间不够时,虚拟机将怎样判定对象可不可以被回收已经哪些地方会发生回收. 垃圾回收主要(不是全部)发生在堆内存中,当一 ...

  8. jvm的垃圾回收算法

    一.对象存活判断判断对象是否存活一般有两种方式:1.引用计数:每个对象有一个引用计数属性,新增一个引用时计数加1,引用释放时计数减1,计数为0时可以回收.此方法简单,无法解决对象相互循环引用的问题.2 ...

  9. JVM(四) 垃圾回收

    1. 堆内存结构 Java堆从GC的角度可以细分为:新生代(Eden区.From Survivor区和To Survivor区)和老年代. 1.1 新生代 新生代是用来存放新生的对象.一般占据堆的1/ ...

  10. 深入探究JVM之垃圾回收算法实现细节

    @ 目录 前言 垃圾回收算法实现细节 根节点枚举 安全点 安全区域 记忆集和卡表 写屏障 并发的可达性分析 低延迟GC Shenandoah ZGC 总结 前言 本篇紧接上文,主要讲解垃圾回收算法的实 ...

随机推荐

  1. pyspider

    Linux系统我使用CentOS.对于pycurl安装问题比较好解决,只需要先安装对应的开发包即可.执行如下命令: yum install python-devel curl-devel 分别安装py ...

  2. java基础知识三 流

    Java 流(Stream).文件(File)和IOJava.io 包几乎包含了所有操作输入.输出需要的类.所有这些流类代表了输入源和输出目标. Java.io 包中的流支持很多种格式,比如:基本类型 ...

  3. java 文件目录树

    1. 目标格式,使用tree命令时,目录树格式如下. public class TreeTest { public static void main(String[] args) { File roo ...

  4. 论文阅读笔记五十四:Gradient Harmonized Single-stage Detector(CVPR2019)

    论文原址:https://arxiv.org/pdf/1811.05181.pdf github:https://github.com/libuyu/GHM_Detection 摘要 尽管单阶段的检测 ...

  5. .net基础学java系列(三)徘徊反思

    .net基础学java系列(三)徘徊反思 上一篇文章:.net基础学java系列(二)IDE 之 插件 这两天晚上看完了IDEA的教学视频:https://edu.51cto.com/course/1 ...

  6. 【Linux】在Win10上搭建WSL(适用于Linux的Windows子系统)

    1.打开WSL  ,控制面板 -- 程序 -- 程序和功能 -- 打开或关闭Windows功能 - 选中[适用于Linux的Windows子系统] 2.开启后重启电脑 3.在Win10自带的Micro ...

  7. Semantic difference between object expressions and declarations

    object expressions are executed (and initialized) immediately, where they are used; object declarati ...

  8. mybatis中有趣的符号#与$

    ${ }是字符串替换,相当于直接显示数据,#{ }是预编译处理,相当于对数据加上双引号 即#是将传入的值当做字符串的形式,先替换为?号,然后调用PreparedStatement的set方法来赋值,而 ...

  9. 小程序app.onLaunch中获取用户信息,index.onLoad初次载入时取不到值的问题

    问题描述: //app.js App({ globalData:{ nickname:'' }, onLaunch: function () { let that=this; //假设已经授权成功 w ...

  10. something for learning

    [Unity Shaders]法线纹理(Normal Mapping)的实现细节 https://blog.csdn.net/candycat1992/article/details/41605257