• 认识垃圾回收


    GC的概述

    gc(garbage collection):即垃圾收集,是指JVM用于释放那些不再使用的对象所占用的内存。
    java语言并不要求JVM有gc,也没有规定gc如何工作。不过常用的JVM都有gc,而且大多数gc都使用类似的算法管理内存和执行收集操作。在充分理解了垃圾收集算法和执行过程后,才能有效的优化它的性能。有些垃圾收集专用于特殊的应用程序。比如,实时应用程序主要是为了避免垃圾收集中断,而大多数OLTP应用程序则注重整体效率。理解了应用程序的工作负荷和JVM支持的垃圾收集算法,便可以进行优化配置垃圾收集器。垃圾收集的目的在于清除不再使用的对象,gc通过确定对象是否被活动对象引用来确定是否收集该对象。gc首先要判断该对象是否是时候可以收集,引用计数和对象引用遍历是两种常用的方法。

    GC的作用

    • 对于高级语言来说,一个基本认知是,如果不进行垃圾回收,内存迟早都会被消耗完。如果不断地分配内存空间而不进行回收,就好像不停地生产生活垃圾而从来不打扫一样。

    • 除了释放没用的对象,垃圾回收也可以清除内存里的记录碎片。碎片整理将所占用的堆内存移到堆的一端,以便JVM 将整理出的内存分配给新的对象。

    • 随着应用程序所应付的业务越来越庞大、复杂,用户越来越多,没有GC就不能保证应用程序的正常进行。而经常造成STW的GC又跟不上实际的需求,所以才会不断地尝试对GC进行优化。

    GC的位置

    在这里插入图片描述
    JVM在进行GC时,并不是对这三个区域统一回收。大部分回收都是新生代~

    • 新生区
    • 幸存区(from , to)
    • 老年区

    GC两种类:

    • 轻GC(普通的GC)
    • 重GC(全局GC)

    GC的几种算法

    目前比较常用的算法:

    • 引用计数法
    • 可达性分析算法
    • 复制算法
    • 标记清除算法
    • 标记压缩算法

    1、引用计数法

    概述:

    引用计数算法(Reference Counting)比较简单,对每个对象保存一个整型 的引用计数器属性。用于记录对象被引用的情况。
    对于一个对象A,只要有任何一个对象引用了A,则A的引用计数器就加1;当引用失效时,引用计数器就减1。只要对象A的引用计数器的值为0,即表示对象A不可能再被使用,可进行回收。

    优点:

    实现简单,垃圾对象便于辨识;判定效率高,回收没有延迟性。

    缺点:

    • 需要单独的字段存储计数器,这样的做法增加了存储空间的开销
    • 每次赋值都需要更新计数器,伴随着加法和减法操作,这增加了时间开销
    • 引用计数器有一个严重的问题,即无法处理循环引用的情况。这是一 条致命缺陷,导致在Java的垃圾回收器中没有使用这类算法

    2、可达性分析算法/追踪性垃圾收集

    概述:

    相对于引用计数而言,可达性分析算法解决了循环引用的问题。防止了内存泄露的发生。在Java的垃圾回收器中使用这类算法~

    基本思路:

    • 可达性分析算法是以根对象(GCRoots)为起始点,按照从上至下的方式搜索被根对象集合所连接的目标对象是否可达。
    • 使用可达性分析算法之后,内存中存活的对象都会被根对象集合直接或者间接连接,搜索走过的路径叫做引用链
    • 如果目标对象没有任何引用链相连,则表示不可达,为垃圾。

    3、复制算法

    背景:

    ​ 为了解决标记清除算法效率方面的问题,M.L.Minsky于1963年发表了著名的论文,“ 使用双存储区的Li sp语言垃圾收集器CALISP Garbage Collector Algorithm Using SerialSecondary Storage )”。M.L. Minsky在该论文中描述的算法被人们称为复制(Copying)算法,它也被M. L.Minsky本人成功地引入到了Lisp语言的一个实现版本中。

    核心思想:

    ​ 将活着的内存空间分为两块,每次使用一块,进行垃圾回收的时候,将存活对象复制到另一块未使用的区域,然后将源区域清空,然后交换两个内存的角色

    优点:

    • 没有标记和清除过程,实现简单,运行高效
    • 复制过去以后保证空间连续性,不会出现“碎片”问题。

    缺点:

    • 此算法的缺点也是很明显的,就是需要两倍的内存空间
    • 对于G1这种分拆成为大量region的GC,复制而不是移动,意味着GC需要维护region之间对象引用关系,不管是内存占用或者时间开销也不小。
    • 特别的 如果系统中的可用对象很多,复制算法不会很理想,因为要复制大量的对象

    使用场景:
    在新生代,对常规应用的垃圾回收,一次通常可以回收708一 99的内存空间。回收性价比很高。所以现在的商业虚拟机都是用这种复制算法用在回收新生代

    4、标记-清除法

    在了解标记-清除算法前,我们先要了解几个基本概念。

    首先是mutator和collector,这两个名词经常在垃圾收集算法中出现,collector指的就是垃圾收集器,而mutator是指除了垃圾收集器之外的部分,比如说我们应用程序本身。mutator的职责一般是NEW(分配内存),READ(从内存中读取内容),WRITE(将内容写入内存),而collector则就是回收不再使用的内存来供mutator进行NEW操作的使用。

    第二个基本概念是关于mutator roots(mutator根对象),mutator根对象一般指的是分配在堆内存之外,可以直接被mutator直接访问到的对象,一般是指静态/全局变量以及Thread-Local变量(在Java中,存储在java.lang.ThreadLocal中的变量和分配在栈上的变量 - 方法内部的临时变量等都属于此类).

    第三个基本概念是关于可达对象的定义,从mutator根对象开始进行遍历,可以被访问到的对象都称为是可达对象。这些对象也是mutator(你的应用程序)正在使用的对象。

    算法原理

    顾名思义,标记-清除算法分为两个阶段,标记(mark)和清除(sweep).

    • 标记(mark)阶段
      扫描这些对象,对活着的对象进行标记
    • 清除(sweep)阶段
      对没有标记的对象,进行清除

    优点:
    不需要额外的空间!节省空间
    缺点:
    两次扫描,严重浪费时间,会产生内存碎片。

    5、标记-压缩法

    有时也叫标记-清除-压缩法,与标记-清除法有相同的标记阶段。
    在第二阶段,则把标记对象复制到堆栈的新域中以便压缩堆栈,停止其他操作。

    • 标记(mark)阶段
      扫描这些对象,对活着的对象进行标记
    • 清除(sweep)阶段
      对没有标记的对象,进行清除
    • 压缩
    • 防止内存碎片产生,再次扫描,向一段移动存活的对象多了一个移动成本

    总结

    内存效率:
    复制算法 > 标记清除算法 > 标记压缩算法(时间复杂度)
    内存整齐度:
    复制算法 = 标记压缩算法 > 标记清除算法
    内存利用率:
    标记压缩算法 = 标记清除算法 > 复制算法

    没有最好的算法,只有最合适的算法,所以GC被称为分代收集算法

    年轻代:

    • 存活率低
    • 复制算法

    老年代:

    • 区域大:存活率高
    • 标记清除法 + 标记压缩法 混合实现
  • 相关阅读:
    贝锐蒲公英智慧运维方案:实现远程网络监控、管理、维护工业设备
    ICPC焦作站(E、F)+思维+树上dp
    Django--31Django知识体系梳理总结
    由于导线材质不同绕组直流电阻不平衡率超标
    2023年中国墙体材料发展历程及趋势分析:未来市场规模呈上升态势[图]
    冒泡排序算法
    汽车三高试验离不开的远程试验管理平台-TFM
    Zabbix5.0_介绍_组成架构_以及和prometheus的对比_大数据环境下的监控_网络_软件_设备监控_Zabbix工作笔记001
    k8s 增加 node 节点
    代理流量卡到底能赚钱吗?行业老司机带你深入了解。
  • 原文地址:https://blog.csdn.net/weixin_45737330/article/details/125913095