点赞再看,养成习惯,微信搜索「小大白日志」关注这个搬砖人。

文章不定期同步公众号,还有各种一线大厂面试原题、我的学习系列笔记。

基础概念

  • GC=jvm垃圾回收,垃圾回收机制是由垃圾回收器Garbage Collection来实现的。进行GC的线程是后台的守护进程(后台运行、执行特定任务),它是一个低优先级进程;当可用内存低到一定程度时,自动运行(自启),从而实现对内存的回收,故垃圾回收的时间不确定;
  • 浮动垃圾:jvm回收垃圾的同时,垃圾还在尝试产生,这些就是"浮动垃圾",即“floating Garbage”,只能等到下次GC的时候清理
  • 并行收集:垃圾回收器工作时,用户线程停止,只有多个垃圾回收线程在并行地收集垃圾
  • 并发收集:垃圾回收时,用户线程不停止,而是直接和垃圾回收线程并发地工作

如何判断对象是否是垃圾?

两种方法:引用计数法、可达性分析法(常用这种)

  • 引用计数法

每个对象都有一个引用计数器,当有地方引用该对象时,计数器+1;删除引用或引用失效时,计数器-1;当计数器=0时,说明对象没有被引用则可以被JVM回收;

优点:实现简单,判断效率高

缺点:不能解决循环引用问题

  • 可达性分析

jvm中有一部分对象可被称为【GC roots对象】,如:被栈中引用的对象、被常量引用的对象、被静态变量引用的对象,从【GC roots对象】开始向下遍历,遍历过程中与【GC roots对象】形成通路的对象则为'可达'对象=存活的对象=尚存在引用的对象,否则为'不可达'对象=死亡的对象=不存在引用的对象,当某个对象不可达时,说明对象没有被引用则可以被JVM回收

当对象被判定为垃圾对象后有何方法回收?

5种gc算法

  • 引用计数法

每个对象都会有一个引用计数器,对象增加一个引用则计数器+1,减少一个引用则计数器-1,垃圾回收时,只回收计数器为0的对象。

缺点:无法处理循环引用的情况

  • 标记清除算法

分为标记阶段和清除阶段,在标记阶段,首先通过根节点,标记所有从根节点开始的可达对象,而未被标记的对象就是未被引用的垃圾对象;然后,在清除阶段,清除所有未被标记的对象。

缺点:标记清除后会产生大量不连续的内存碎片,内存碎片太多可能会导致以后程序运行过程中需要分配较大内存对象时,无法找到足够的连续的内存

  • 标记整理算法

标记过程仍然与"标记清除"算法一样,但是后续步骤不是直接对未标记的对象进行清除,而是先让所有标记的对象都向一端移动,然后直接清除掉该端边界以外的内存,解决了没有足够大的连续内存的问题

  • 复制算法

将可用的内存按照容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将活着的对象复制到另外一块上面,然后再把使用过的那一半内存空间一次清理掉。每次都是对整个半区进行内存回收,内存分配时也就不用考虑内存碎片等复杂情况。

缺点:可用内存缩小为原来的一半。

  • 分代收集算法

java堆分成新生代和老年代,分带收集算法=新生代采用赋值算法+老年代采用标记整理算法。在新生代中,每次垃圾回收时都发现大批对象的死去,只有少量存活,故采用复制算法可以只付出少量存活对象的复制成本就可以完成收集;而老年代中因为对象存活率高,没有足够的空间让出来作垃圾回收,故使用“标记整理”算法来进行回收。

谁来回收?

用7种垃圾收集器来回收,可以对应地记忆:

串行收集器 并行收集器 吞吐量收集器
串行老年代收集器 CMS收集器 吞吐量老年代收集器
G1收集器
如上,串行收集器<->串行老年代收集器
吞吐量收集器<->吞吐量老年代收集器
并行收集器<->CMS收集器
再加上一个G1收集器,共7种收集器,这7种收集器可分别作用于新生代和老年代:
图中上下半部为新生代、老年代对应可用的收集器,连线代表可以结合使用
  • 串行收集器(Serial收集器)

    • 作用于新生代,故使用复制算法;
    • 仅仅使用一个线程完成垃圾收集工作;
    • 在垃圾收集时必须暂停其他所有的工作线程(stop-the-world),直到垃圾收集结束;
    • 专心做垃圾收集故效率比较高,使用【-XX:+UseSerialGC】打开

  • 并行收集器(ParNew收集器)

    • 作用于新生代,故使用复制算法;
    • 并行收集器其实是串行收集器的多线程版本,与Serial收集器唯一不同的地方就是在垃圾收集过程中使用多个线程并行收集,其他全部和串行收集器相同
    • 它默认开启的收集线程数与CPU的数量相同
    • 使用【-XX:UseParNewGC】打开;

除了Serial收集器,就只有它能和CMS收集器混合使用

  • 吞吐量收集器(ParallelScavenge 收集器)

    • 作用于新生代,故使用复制算法;
    • 吞吐量收集器的目的是追求高吞吐量,吞吐量=运行用户代码时间/(运行用户代码时间+运行垃圾收集时间);
    • 使用【-XX:+UseParallelGC】打开;使用【-XX:MaxGCPauseMillis】指定垃圾收集最大停顿时间,但并非越小越好;
    • 使用多个GC线程来完成垃圾收集
    • 会引起stop-the-world(垃圾收集停顿时间:垃圾收集的时候,所有java线程被挂起,除了垃圾收集器的线程,此时navtive相关代码可执行但不能与JVM交互,故stop-the-world是大家的敌人)
    • 最大的特点是'GC自适应策略'':打开GC自适应策略【-XX:+UseAdaptiveSizePolicy】,则不用再手动设置'年轻代:老年代'的大小、'Eden区:survivor区'的大小、对象晋升老年代的年龄,系统会根据实时性能去动态设置这些参数以便达到最高的吞吐量、最优的停顿时间

  • 串行老年代收集器(Serial Old收集器)

    • 因为作用于老年代,所有采用'标记整理'算法
    • 和串行收集器一样,只是它作用于老年代,是串行收集器的老年代版本

  • CMS收集器

    • 作用于老年代,故使用'标记整理'算法;
    • 以获取最短的垃圾收集停顿时间为目标,重视响应速度和用户体验的应用;
    • CMS收集器的内存回收过程是与用户线程一共并发执行的;
    • 使用【-XX:+UseConcMarkSweepGC】打开;
    • 步骤:初始标记->并发标记->重新标记->并发清除,初始阶段=标记由【GC roots】直接到达的对象,并发标记=标记由【GC roots】间接到达的对象,重新标记=修正在'并发标记'期间由于用户程序运行而导致标记变动的标记,并发清除=清除已标记的对象

  • 吞吐量老年代收集器(Parallel Old收集器)

    • 因为作用于老年代,所有采用'标记整理'算法
    • 和吞吐量收集器一样,只是它作用于老年代,是吞吐量收集器的老年代版本
    • 使用【-XX:+UseConcMarkSweepGC】打开

  • G1收集器

    • 可作用于年轻代/老年代,jdk1.9之后的默认垃圾收集器
    • 使用【-XX:+UseG1GC】打开
    • G1收集器的内存回收过程是与用户线程一起并发执行的;
    • G1收集器不会产生内存碎片,会把内存碎片收集后形成大的内存块
    • G1收集器最大的特点是把堆的内存分成一块块大小相同的区域(region),保留年轻代、老年代的概念,但年轻代、老年代也是被清晰地分为一个个区域
    • G1追求最小的gc停顿时间,并且可用参数指定gc停顿时间,也就是建立了可以预测的gc停顿时间模型,原理:因为G1收集器把堆分成了一块块大小相同的区域,每个区域可以回收的空间大小不同,G1会在后台把每个区域的回收价值(回收价值=可回收的空间大小+回收时间成本)由大到小维护成一张优先级列表,回收是先回收价值大的区域;每个区域的大小可以通过【-XX:G1HeapRegionSize】参数指定,大小区间最小1M、最大32M,一定要是2的幂次方,默认把堆内存按照2048份均分,每个区域被标记了E、S、O和H,这些区域在逻辑上被映射为Eden、Survivor、老年代Old和Humongous去(用来存放大对象)
    • 步骤:初始标记->并发标记->重新标记->筛选回收,初始阶段=标记由【GC roots】直接到达的对象,并发标记=标记由【GC roots】间接到达的对象,重新标记=修正在'并发标记'期间由于用户程序运行而导致标记变动的标记,筛选回收=对各个region计算回收价值(回收价值=可回收的空间大小+回收时间成本),并按照用户设置的停顿时间来制定回收计划

默认的垃圾回收器

jdk1.7和jdk1.8: 年轻代默认'吞吐量垃圾收集器'(即Parallel Scavenge), 老年代默认'吞吐量老年代收集器'(即Parallel Old)

jdk1.9:默认G1垃圾收集器

OK,如果文章哪里有错误或不足,欢迎各位留言。

创作不易,各位的「三连」是二少创作的最大动力!我们下期见!

JVM垃圾回收篇的更多相关文章

  1. JVM垃圾回收?看这一篇就够了!

    深入理解JVM垃圾回收机制 1.垃圾回收需要解决的问题及解决的办法总览 1.如何判定对象为垃圾对象 引用计数法 可达性分析法 2.如何回收 回收策略 标记-清除算法 复制算法 标记-整理算法 分带收集 ...

  2. Java进阶 JVM 内存与垃圾回收篇(一)

    JVM 1. 引言 1.1 什么是JVM? 定义 Java Vritual Machine - java 程序的运行环境(Java二进制字节码的运行环境) 好处 一次编译 ,到处运行 自动内存管理,垃 ...

  3. JVM学习笔记——垃圾回收篇

    JVM学习笔记--垃圾回收篇 在本系列内容中我们会对JVM做一个系统的学习,本片将会介绍JVM的垃圾回收部分 我们会分为以下几部分进行介绍: 判断垃圾回收对象 垃圾回收算法 分代垃圾回收 垃圾回收器 ...

  4. jvm - 垃圾回收

    jvm - 垃圾回收 注意 : 本系列文章为学习系列,部分内容会取自相关书籍或者网络资源,在文章中间和末尾处会有标注 垃圾回收的意义 它使得java程序员不再时时刻刻的关注内存管理方面的工作. 垃圾回 ...

  5. Java虚拟机学习笔记——JVM垃圾回收机制

    Java虚拟机学习笔记——JVM垃圾回收机制 Java垃圾回收基于虚拟机的自动内存管理机制,我们不需要为每一个对象进行释放内存,不容易发生内存泄漏和内存溢出问题. 但是自动内存管理机制不是万能药,我们 ...

  6. JVM垃圾回收机制总结:调优方法

    转载: JVM垃圾回收机制总结:调优方法 JVM 优化经验总结 JVM 垃圾回收器工作原理及使用实例介绍

  7. JVM内存管理和JVM垃圾回收机制

    JVM内存管理和JVM垃圾回收机制(1) 这里向大家描述一下JVM学习笔记之JVM内存管理和JVM垃圾回收的概念,JVM内存结构由堆.栈.本地方法栈.方法区等部分组成,另外JVM分别对新生代和旧生代采 ...

  8. JDK分析工具&JVM垃圾回收(转)

    转自:http://blog.163.com/itjin45@126/blog/static/10510751320144201519454/ 官方手册:http://docs.oracle.com/ ...

  9. 老李分享:jvm垃圾回收

    老李分享:jvm垃圾回收   poptest是国内唯一一家培养测试开发工程师的培训机构,以学员能胜任自动化测试,性能测试,测试工具开发等工作为目标.如果对课程感兴趣,请大家咨询qq:908821478 ...

随机推荐

  1. 简述 synchronized 和 java.util.concurrent.locks.Lock 的异同?

    Lock 是 Java 5 以后引入的新的 API,和关键字 synchronized 相比主要相同点: Lock 能完成 synchronized 所实现的所有功能:主要不同点:Lock 有比 sy ...

  2. Java中自动装箱与拆箱

    一.什么是封装类? Java中存在基础数据类型,但是在某些情况下,我们要对基础数据类型进行对象的操作,例如,集合中只能存在对象,而不能存在基础数据类型,于是便出现了包装器类.包装器类型就是对基本数据类 ...

  3. Redis缓存穿透、缓存雪崩、缓存击穿

    缓存穿透: ​ 缓存穿透,是指查询一个数据库一定不存在的数据.正常的使用缓存流程大致是,数据查询先进行缓存查询,如果key不存在或者key已经过期,再对数据库进行查询,并把查询到的对象,放进缓存.如果 ...

  4. java后端使用token处理表单重复提交

    保证接口幂等性,表单重复提交 前台解决方案:提交后按钮禁用.置灰.页面出现遮罩后台解决方案:   使用token,每个token只能使用一次1.在调用接口之前生成对应的Token,存放至redis 2 ...

  5. Flask 简单使用,这一篇就够了!

    #Flask 安装依赖包及作用 - jinja2 模板语言 (flask依赖包) - markupsafe 防止css攻击 (flask依赖包) - werkzeug --wkz 类似于django中 ...

  6. 为什么HTTP/3要基于UDP?可靠吗?

    目录 前言 为什么转用UDP? HTTP/3解决了那些问题? 队头阻塞问题 QPACK编码 Header 参考 推荐阅读: 计算机网络汇总 HTTP/3竟然是基于UDP的!开始我也很疑惑,UDP传输不 ...

  7. 关于CPU、指令集、架构、芯片的一些科普

    作者:王强链接:https://zhuanlan.zhihu.com/p/19893066来源:知乎 随着智能设备的广泛普及,这几年媒体上越来越多的出现关于"架构""AR ...

  8. Python中 No module named解决方法

    对于pycharm安装包失败的原因借解决办法 在pycharm中安装包安装失败:Non-zero exit code (1) 可能是在库中找不到对应版本.解决:cmd中使用命令:pip install ...

  9. visual studio 2019工具里添加开发中命令提示符的方法

    最新新装了visual studio 2019,发现默认的没有开发者命令提示符 现将添加步骤描述如下: 从VS2019菜单选择"Tools",然后选择"外部工具" ...

  10. 小小标签,强大功能——深藏不露的 <input>

    <input> 虽只是一个看似简单的 HTML 表单元素,但它这么一个单一的元素,就有多达 30 多个属性(attribute),相信无论你是个小菜鸟还是像我一样写了 15 年 HTML ...