当前位置: 首页 > news >正文

JVM垃圾回收系列之垃圾收集算法

随笔

推迟了一周才发布这篇文章实属罪过,最近延迟更新是因为最近在研究下一个系列“微服务、云原生”文章的整体排版。不得不吐糟一句,java的内容真的是多到没边,尤其是当微服务成为主流,导致程序员之间的层级化也越来越严重,架构师的要求达到有史以来的恐怖高度,业务开发人员的可替换性更高(难受~~~)。
在这里插入图片描述

引言

这篇博文将介绍HotSpot虚拟机中常见的几种垃圾回收算法,如:标记清除、拷贝算法、标记压缩等。

参考书籍:“深入理解Java虚拟机”

个人java知识分享项目——gitee地址

个人java知识分享项目——github地址

标记清除(Mark Sweep)

最基础的收集算法是“标记-清除”(Mark-Sweep)算法,如同它的名字一样,算法分 为“标记”和“清除”两个阶段:

  • 标记:首先标记出所有需要回收的对象,标记的过程其实就在上一篇文章中有了介绍
  • 清除:在标记完成后统一回收所有被标记的对象

在这里插入图片描述
jvm首先会标记出可以回收的垃圾对象(通过根可达算法),然后将这些对象进行清除其实也就是将对象相应的内存块重置,这一整个的过程都是通过虚拟机的守护进程(deamon)完成。

标记清除算法是最基础的收集算法,之所以说它 是最基础的收集算法,是因为后续的收集算法都是基于这种思路并对其不足进行改进而得到的。因此此算法的缺点也是比较明显的:

  1. 效率不算高,需要停止整个应用程序,导致用户体验差
  2. 这种方式清理出来的空闲内存是不连续的,产生内存碎片。需要维护一个空闲列表,而且空间碎片太多可能会导致以后在程序运行过程中需要分配较大对象时,无法找到足够的连续内存而不得不提前触发另一次垃圾收集动作

复制算法(Copying)

为了解决效率问题,一种称为“复制”(Copying)的收集算法出现了,它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块(也就是堆内存中的两个survivor区,在以前的堆系列文章中有过介绍)。此算法的原理是通过将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。

在这里插入图片描述

现在的商业虚拟机都采用这种收集算法来回收新生代,IBM公司的专门研究表明,新生代中的对象98%是“朝生夕死”的,所以并不需要按照1:1的比例来划分内存空间,而是将内存分为一块较大的Eden空间和两块较小的Survivor空间,每次使用Eden和其中一块Survivor[1]。 当回收时,将Eden和Survivor中还存活着的对象一次性地复制到另外一块Survivor空间上,最 后清理掉Eden和刚才用过的Survivor空间。HotSpot虚拟机默认Eden和Survivor的大小比例是 8:1,也就是每次新生代中可用内存空间为整个新生代容量的90%(80%+10%),只有10% 的内存会被“浪费”。当然,98%的对象可回收只是一般场景下的数据,我们没有办法保证每 次回收都只有不多于10%的对象存活,当Survivor空间不够用时,需要依赖其他内存(这里 指老年代)进行分配担保(Handle Promotion)。

内存的分配担保就好比我们去银行借款,如果我们信誉很好,在98%的情况下都能按时偿还,于是银行可能会默认我们下一次也能按时按量地偿还贷款,只需要有一个担保人能保证如果我不能还款时,可以从他的账户扣钱,那银行就认为没有风险了。内存的分配担保也一样,如果另外一块Survivor空间没有足够空间存放上一次新生代收集下来的存活对象时, 这些对象将直接通过分配担保机制进入老年代。

复制算法的优缺点:

  • 优点:没有标记和清除过程,实现简单,运行高效。复制过去以后保证空间的连续性,不会出现“碎片”问题
  • 缺点:此算法的缺点也很明显,就是需要两倍的内存空间。对于G1这种分拆成为大量region的GC,复制而不是移动,意味着GC需要维护region之间对象引用关系,不管是内存占用或者时间开销也不小。

标记压缩(Mark Compact)

此算法是为了解决复制收集算法在对象存活率较高时就要进行较多的复制操作,效率将会变低所提出来的一种算法。更关键的是,如果不想浪费50%的空间,就需要有额外的空间进行分配担保,以应对被使用的内存中 所有对象都100%存活的极端情况,所以在老年代一般不能直接选用这种算法。
在这里插入图片描述
此算法的优缺点:

  • 优点: 消除了标记-清除算法当中,内存区域分散的缺点,我们需要给新对象分配内存时,JVM只需要持有一个内存的起始地址即可。消除了复制算法当中,内存减半的高额代价
  • 缺点:从效率上来说,标记-整理算法要低于复制算法。移动对象的同时,如果对象被其他对象引用,则还需要调整引用的地址。移动过程中,需要全程暂停用户应用程序。即:STW

分代收集算法

前面所有的这些算法中,并没有一种算法可以完全替代其他算法,它们都具有自己独特的优势和特点。

分代收集算法应运而生。分代收集算法,是基于这样一个事实:不同的对象的生命周期是不一样的。因此,不同生命周期的对象可以采取不同的收集方式,以便提高回收效率。一般是把Java堆分为新生带和老年代,这样就可以根据各个年代的特点使用不同的回收算法,以提高垃圾回收的效率。

在HotSpot中,基于分代的概念,GC所使用的内存回收算法必须结合年轻代和老年代各自的特点。

  • 年轻代(Young Gen)
    • 年轻代特点:区域较大,对象生命周期长、存活率高,回收不及年轻代频繁。
    • 这种情况复制算法的回收整理,速度是最快的。复制算法的效率只和当前存活对象大小有关,因此年轻代的回收。而复制算法内存利用率不高的问题,通过hotspot中的两个survivor的设计得到缓解。
  • 老年代(Tenured Gen)
    • 老年代特点:区域较大,对象生命周期长、存活率高,回收不及年轻代频繁。
    • 这种情况存在大量存活率较高的对象,复制算法明显变得不合适。一般是由标记-清除-整理额混合实现。
      • Mark阶段的开销与存活对象的数量成正比
      • Sweep阶段的开销与所管理区域的大小成正相关
      • Compact阶段的开销与存活对象的数据成正比

增量收集算法

如果一次性将所有的垃圾进行处理,需要造成系统长时间的停顿,那么久可以让垃圾收集线程和应用程序线程交替执行。每次,垃圾收集线程只收集一小片区域的内存空间,接着切换到应用程序线程。依次反复,直到垃圾收集完成。

  • 优点:增量收集算法通过对线程间冲突的妥善处理,允许垃圾收集线程以分阶段的方式完成标记、清理或复制工作
  • 缺点:使用这种方式,由于在垃圾回收过程中,间断性地还执行了引用程序代码,所以能减少系统的停顿时间。但是,因为线程切换和上下文转化的消耗,会使得垃圾回收的总体成本上升,造成系统吞吐量的下降。

最后再总结一下三种常见的算法的优劣:

Mark SweepMark ConpactCopying
速度中等最慢最快
空间开销少(但会堆积碎片)少(不堆积碎片)通常需要活对象的2倍大小(不堆积碎片)
移动对象

相关文章:

  • 计算机毕业设计选题推荐 40个高质量计算机毕设项目分享【源码+论文】(三)
  • BDD - SpecFlow BDD 测试实践 SpecFlow + MSTest
  • CRM项目记录(四)
  • React组件的生命周期函数
  • FFmpeg源码分析:avformat_open_input()打开媒体流
  • 深入理解关键字 一(auto,register,static,sizeof)
  • 基于Springboot+vue的停车场管理系统(Java毕业设计)
  • 详解CAN总线:CAN总线报文格式—数据帧
  • mysql进阶:canal实现mysql数据同步到redis|实现自定义canal客户端
  • React路由三种渲染方式、withRouter高阶组件、自定义导航组件
  • FaceNet-pytorch(fixing data imbalance-CASIA)
  • 【内核的设计与实现笔记】| 【01】初步了解内核
  • 【HDLBits 刷题】所有答案直达链接汇总
  • portswigger 目录遍历文件上传
  • 【微信小程序入门到精通】— swiper 超详细的属性值讲解!确定不来看看?
  • 《网管员必读——网络组建》(第2版)电子课件下载
  • 【跃迁之路】【699天】程序员高效学习方法论探索系列(实验阶段456-2019.1.19)...
  • Apache的80端口被占用以及访问时报错403
  • AWS实战 - 利用IAM对S3做访问控制
  • CSS中外联样式表代表的含义
  • Django 博客开发教程 16 - 统计文章阅读量
  • Dubbo 整合 Pinpoint 做分布式服务请求跟踪
  • HTTP--网络协议分层,http历史(二)
  • Laravel 中的一个后期静态绑定
  • PAT A1092
  • Vim 折腾记
  • 技术:超级实用的电脑小技巧
  • 类orAPI - 收藏集 - 掘金
  • 名企6年Java程序员的工作总结,写给在迷茫中的你!
  • 前端工程化(Gulp、Webpack)-webpack
  • 如何正确配置 Ubuntu 14.04 服务器?
  • 使用Envoy 作Sidecar Proxy的微服务模式-4.Prometheus的指标收集
  • 使用Maven插件构建SpringBoot项目,生成Docker镜像push到DockerHub上
  • 不要一棍子打翻所有黑盒模型,其实可以让它们发挥作用 ...
  • 关于Kubernetes Dashboard漏洞CVE-2018-18264的修复公告
  • ​ 无限可能性的探索:Amazon Lightsail轻量应用服务器引领数字化时代创新发展
  • ​如何防止网络攻击?
  • "无招胜有招"nbsp;史上最全的互…
  • (9)YOLO-Pose:使用对象关键点相似性损失增强多人姿态估计的增强版YOLO
  • (echarts)echarts使用时重新加载数据之前的数据存留在图上的问题
  • (编程语言界的丐帮 C#).NET MD5 HASH 哈希 加密 与JAVA 互通
  • (大众金融)SQL server面试题(1)-总销售量最少的3个型号的车及其总销售量
  • (附源码)springboot建达集团公司平台 毕业设计 141538
  • (附源码)ssm旅游企业财务管理系统 毕业设计 102100
  • (全注解开发)学习Spring-MVC的第三天
  • (五) 一起学 Unix 环境高级编程 (APUE) 之 进程环境
  • (原+转)Ubuntu16.04软件中心闪退及wifi消失
  • (转)EXC_BREAKPOINT僵尸错误
  • (转载)利用webkit抓取动态网页和链接
  • .md即markdown文件的基本常用编写语法
  • .NET “底层”异步编程模式——异步编程模型(Asynchronous Programming Model,APM)...
  • .Net 8.0 新的变化
  • .net core开源商城系统源码,支持可视化布局小程序
  • .NET 除了用 Task 之外,如何自己写一个可以 await 的对象?
  • .net安装_还在用第三方安装.NET?Win10自带.NET3.5安装