文章目录

  • 垃圾回收与内存分配
    • 1 引用
    • 2 如何判断对象需要回收
      • 2.1 引用计数算法
      • 2.2 可达性分析算法
      • 2.3 废弃常量的回收
      • 2.4 无用的类的回收
    • 3 内存分配
    • 4 垃圾收集算法
      • 4.1 标记清除算法
      • 4.2 标记复制算法
      • 4.3 标记整理算法
    • 5 垃圾收集器
      • 5.1 Serial
      • 5.2 ParNew
      • 5.3 Parallel Scavenge
      • 5.4 Serial Old
      • 5.5 Parallel Old
      • 5.6 CMS
      • 5.7 G1
      • 5.8 Shenandoah
      • 5.9 ZGC
    • 6 HotSpot垃圾收集算法
      • 6.1 根节点枚举
      • 6.2 安全点
      • 6.3 安全区域
      • 6.4 记忆集与卡表
      • 6.5 写屏障

垃圾回收与内存分配

1 引用

  • 强引用:强引用是最传统的引用定义,是指在程序代码之中普遍存在的引用赋值。只要强引用存在,就不会被垃圾回收
  • 软引用:描述一些还有用但是非必须的对象。如果内存空间足够,垃圾回收器就不会回收它,如果内存空间不足了,就会回收这些对象的内存
  • 弱引用:描述非必须的对象。弱引用的对象不管当前内存空间足够与否,都会被回收
  • 虚引用:最弱的一种引用关系。虚引用唯一目的只是为了能在对象被回收时收到一个系统通知

软引用可以加速 JVM 对垃圾内存的回收速度,可以维护系统的运行安全,防止内存溢出(OutOfMemory)等问题的产生

2 如何判断对象需要回收

2.1 引用计数算法

给对象中添加一个引用计数器,每当有一个地方引用它,计数器就加 1;当引用失效,计数器就减 1;任何时候计数器为 0 的对象就是不可能再被使用的

这个方法实现简单,效率高,但是目前主流的虚拟机中并没有选择这个算法来管理内存,其最主要的原因是它很难解决对象之间相互循环引用的问题

2.2 可达性分析算法

通过一系列称为GC Roots的根对象作为起始节点集,从这些节点开始,根据引用关系向下搜索,搜索过的路径称为引用链。如果某个对象到GC Root都没有引用链相连,则这个对象不可达,不可能被使用

可作为GC Roots的对象

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

即使在可达性分析法中不可达的对象,也并非是“非死不可”的,这时候它们暂时处于“缓刑阶段”,要真正宣告一个对象死亡,至少要经历两次标记过程

  • 可达性分析法中不可达的对象被第一次标记并且进行一次筛选,筛选的条件是此对象是否有必要执行 finalize 方法。当对象没有覆盖 finalize 方法,或 finalize 方法已经被虚拟机调用过时,虚拟机将这两种情况视为没有必要执行

  • 被判定为需要执行finalize 方法的对象将会被放在一个队列中进行第二次标记,除非这个对象与引用链上的任何一个对象建立关联,否则就会被真的回收

2.3 废弃常量的回收

如果当前没有任何对象引用该常量,且虚拟机中没有其他地方引用这个字面量,就会被回收

2.4 无用的类的回收

类需要同时满足三个条件

  • 该类所有的实例都已经被回收,也就是 Java 堆中不存在该类的任何实例
  • 加载该类的 ClassLoader 已经被回收
  • 该类对应的 java.lang.Class 对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法

虚拟机可以对满足上述 3 个条件的无用类进行回收,这里说的仅仅是“可以”,而并不是和对象一样不使用了就会必然被回收

3 内存分配

目前主流的垃圾收集器都会采用分代回收算法,因此需要将堆内存分为新生代老年代,这样就可以根据各个年代的特点选择合适的垃圾收集算法

大部分情况,对象都会首先在 Eden 区域分配,在一次新生代垃圾回收后,如果对象还存活,则会进入 s0 或者 s1,并且对象的年龄还会加 1(Eden 区->Survivor 区后对象的初始年龄变为 1),当它的年龄增加到一定程度(默认为 15 岁),就会被晋升到老年代中

分配担保机制:由于大对象在新生代中空间分配不足,所以提前转移到老年代中

分配策略

  • 对象优先在eden区分配
  • 大对象直接进入老年代
  • 长期存活的对象将进入老年代

4 垃圾收集算法

部分收集(Partial GC):指目标不是完成收集整个堆的垃圾收集,其中分为

  • 新生代收集(Minor GC/Young GC):指目标只是新生代的垃圾收集
  • 老年代收集(Major GC/Old GC):指目标只是老年代的垃圾收集
  • 混合收集(Mixed GC):指目标是整个新生代以及部分老年代的垃圾收集
  • 整堆收集(Full GC):指整个堆和方法区的垃圾收集

在新生代中,每次收集都会有大量对象死去,所以可以选择”标记-复制“算法,只需要付出少量对象的复制成本就可以完成每次垃圾收集。而老年代的对象存活几率是比较高的,而且没有额外的空间对它进行分配担保,所以我们必须选择“标记-清除”或“标记-整理”算法进行垃圾收集

4.1 标记清除算法

首先标记出所有需要回收的对象,在标记完成后统一回收掉所有被标记的对象。它是最基础的收集算法,后续的算法都是对其不足进行改进得到

缺点是执行效率不稳定,可能存在大量标记对象;内存空间可能存在碎片化

4.2 标记复制算法

它可以将内存分为大小相同的两块,每次使用其中的一块。当这一块的内存使用完后,就将还存活的对象复制到另一块去,然后再把使用的空间一次清理掉。这样就使每次的内存回收都是对内存区间的一半进行回收

代价是可用内存缩小为原来的一半

4.3 标记整理算法

根据老年代的特点提出的一种标记算法,标记过程仍然与“标记-清除”算法一样,但后续步骤不是直接对可回收对象回收,而是让所有存活的对象向一端移动,然后直接清理掉端边界以外的内存

代价是移动存活对象并更新引用需要暂停用户程序

5 垃圾收集器

5.1 Serial

新生代收集器

Serial(串行)收集器是一个单线程收集器了,它只会使用一条垃圾收集线程去完成垃圾收集工作,进行垃圾收集工作的时候必须暂停其他所有的工作线程( “Stop The World” ),直到它收集结束

新生代采用标记-复制算法,老年代采用标记-整理算法

5.2 ParNew

新生代收集器

ParNew 收集器其实就是 Serial 收集器的多线程版本,除了使用多线程进行垃圾收集外,其余行为(控制参数、收集算法、回收策略等等)和 Serial 收集器完全一样

新生代采用标记-复制算法,老年代采用标记-整理算法

5.3 Parallel Scavenge

新生代收集器

多线程收集

Parallel Scavenge 收集器关注点是吞吐量(高效率的利用 CPU),CMS 等垃圾收集器的关注点更多的是用户线程的停顿时间(提高用户体验)。所谓吞吐量就是 CPU 中用于运行用户代码的时间与 CPU 总消耗时间的比值

新生代采用标记-复制算法,老年代采用标记-整理算法

5.4 Serial Old

Serial 收集器的老年代版本,同样是一个单线程收集器

它主要有两大用途:一种用途是在 JDK1.5 以及以前的版本中与 Parallel Scavenge 收集器搭配使用,另一种用途是作为 CMS 收集器的后备方案

5.5 Parallel Old

Parallel Scavenge 收集器的老年代版本

5.6 CMS

CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器。它非常符合在注重用户体验的应用上使用,实现了让垃圾收集线程与用户线程(基本上)同时工作

采用标记-清除算法

  • 初始标记:暂停所有的其他线程,并记录下直接与 root 相连的对象,速度很快
  • 并发标记:从GC roots直接关联的对象开始遍历整个对象图,耗时较长,但不需要停顿用户线程
  • 重新标记:修正并发标记期间因为用户程序继续运行而导致标记产生变动的那一部分对象的标记记录,这个阶段的停顿时间一般会比初始标记阶段的时间稍长,远远比并发标记阶段时间短
  • 并发清除:开启用户线程,同时 GC 线程开始对未标记的区域做清扫

优点:并发收集、低停顿

缺点:

  • 对CPU资源敏感,占用一部分线程,降低了吞吐量
  • 无法处理浮动垃圾,在并发标记阶段和并发清理阶段还会有新的垃圾产生
  • 使用标记-清除算法会产生碎片

5.7 G1

G1 (Garbage-First) 是一款面向服务器的垃圾收集器,主要针对配备多颗处理器及大容量内存的机器.,以极高概率满足 GC 停顿时间要求的同时,还具备高吞吐量性能特征

G1不再以固定大小划分分代区域,而是把堆划分为多个大小相等的独立区域(Region),每个Region根据需要,扮演新生代或老年代空间

G1面向堆内存任何部分来组成回收集,哪块内存存放的垃圾最多,回收收益最大,就回收哪部分,采用Mixed GC模式

整体上看是标记-整理算法,局部上看是标记-复制算法

  • 初始标记:暂停所有的其他线程,并记录下直接与 root 相连的对象,速度很快,而且是借用Minor GC的时候同步完成的
  • 并发标记:从GC roots直接关联的对象开始遍历整个对象图,耗时较长,但不需要停顿用户线程
  • 最终标记:对用户线程做另一个短暂的暂停,用于处理并发阶段结束后仍遗留下来的少量SATB记录
  • 筛选回收:负责更新Region的统计数据,筛选出多个Region,将存活对象复制到空的Region中,清理掉需要清理的Region全部空间

5.8 Shenandoah

第一款不由Oracle开发的垃圾收集器

基于Region的堆内存布局,默认回收策略也是优先处理回收收益大的,在回收阶段可以与用户线程并发

使用转发指针实现对象移动与用户线程并发,在原有对象布局结构的最前面统一增加一个新的引用字段,当不处于并发时,该字段指向自己。当对象拥有了新的副本时,修改旧对象上转发指针的引用位置,指向新对象,这样只要旧对象未被清理掉,仍可以通过旧的引用地址访问新对象

  • 初始标记:同G1,暂停所有的其他线程,并记录下直接与 root 相连的对象,速度很快
  • 并发标记:同G1,从GC roots直接关联的对象开始遍历整个对象图,耗时较长,但不需要停顿用户线程
  • 最终标记:同G1,对用户线程做另一个短暂的暂停,用于处理并发阶段结束后仍遗留下来的少量SATB记录
  • 并发回收:先把回收集里面存活的对象复制到未使用的Region中
  • 初始引用更新:确保所有并发回收阶段中进行的收集器线程都完成了对象的移动
  • 并发引用更新:把堆中所有指向旧对象的引用修正到复制后的新地址,与用户线程并发
  • 最终引用更新:暂停用户线程,修正GC Roots中的引用更新
  • 并发清理:并发清理需要清理的Region空间

5.9 ZGC

与Shenandoah和G1相似,采用Region堆内存布局,不过Region可以动态创建和销毁、动态决定大小

  • 并发标记:同G1和Shenandoah,包括初始标记、最终标记
  • 并发预备重分配:统计需要清理的Region
  • 并发重分配:复制需要清理的Region中存活的对象至空的Region中,维护一个转发表,记录旧对象到新对象的转向关系。如果用户线程访问了旧对象,就会被内存屏障截获,转发到新的对象上,并同时修正更新该引用,称为指针的自愈
  • 并发重映射:修正堆中所有旧对象的引用(也可以让其自愈)

6 HotSpot垃圾收集算法

6.1 根节点枚举

虚拟机有办法直接得到哪些地方存放着对象引用

6.2 安全点

安全点是指特定的位置,执行到达安全点以后才能够暂停线程执行垃圾回收

主动式中断:垃圾回收需要中断线程时,仅设置一个标志位,线程不断轮询该标志位,当标志位为真时,就在最近的安全点上主动中断挂起

6.3 安全区域

安全区域可以看作被扩展的安全点,在安全区域中,引用关系不会发生变化

6.4 记忆集与卡表

垃圾收集器在新生代中建立了名为记忆集的数据结构,避免把整个老年代加进GC Roots扫描范围

卡表是记忆集的一种具体实现

卡表的每个元素都对应内存区域的一块特定大小的内存块,称为卡页。只要卡页中有一个对象字段存在跨代的指针,就把对应元素标识为1,称为这个元素变脏。在垃圾收集时只需要筛选出卡表中变脏的元素,就能找到内存块中包含的跨代指针

6.5 写屏障

写屏障可以看作引用类型赋值操作的AOP切面

在引用类型赋值操作之后,执行额外的动作,即对应的卡表元素变脏

Java虚拟机(二)——垃圾回收与内存分配相关推荐

  1. 【java虚拟机序列】java中的垃圾回收与内存分配策略

    在[java虚拟机系列]java虚拟机系列之JVM总述中我们已经详细讲解过java中的内存模型,了解了关于JVM中内存管理的基本知识,接下来本博客将带领大家了解java中的垃圾回收与内存分配策略. 垃 ...

  2. Java虚拟机的垃圾回收器以及内存分配策略详解

    概述 垃圾回收器(GC)是什么以及为什么我们需要垃圾回收器?? 垃圾回收是Java语言区别于其他语言的一种最为重要的特性之一, 通过垃圾回收器(Garbage Collection)来实现对我们Jav ...

  3. 学习笔记【Java 虚拟机②】垃圾回收

    若文章内容或图片失效,请留言反馈.部分素材来自网络,若不小心影响到您的利益,请联系博主删除. 总目录 学习笔记[Java 虚拟机①]内存结构 学习笔记[Java 虚拟机②]垃圾回收 学习笔记[Java ...

  4. Java虚拟机之垃圾回收详解一

    Java虚拟机之垃圾回收详解一 Java技术和JVM(Java虚拟机) 一.Java技术概述: Java是一门编程语言,是一种计算平台,是SUN公司于1995年首次发布.它是Java程序的技术基础,这 ...

  5. java虚拟机多久触发垃圾回收_每日一问:讲讲 Java 虚拟机的垃圾回收

    昨天我们用比较精简的文字讲了 Java 虚拟机结构,没看过的可以直接从这里查看: 每日一问:你了解 Java 虚拟机结构么? 今天我们必须来看看 Java 虚拟机的垃圾回收算法是怎样的.不过在开始之前 ...

  6. 20200405——java之jvm 垃圾回收器和内存分配策略 二

    什么叫GC Garbage Collection 什么内存区域需要GC 共享区的都要被回收比如堆区以及方法区. 在进行内存回收之前要做的事情就是判断那些对象是'死'的,哪些是'活'的.常用方法有两种 ...

  7. JVM的垃圾回收与内存分配

    Java是一种内存动态分配和垃圾回收技术的一种语言,不需要显示的进行对象内存的分配,这一切操作都是由JVM来完成的,由于Java是"一切皆对象"的,所以对于内存分配的优化与速度非常 ...

  8. 深入理解Java虚拟机——JVM垃圾回收机制和垃圾收集器详解

    一:概述 说起垃圾回收(Garbage Collection,GC),很多人就会自然而然地把它和Java联系起来.在Java中,程序员不需要去关心内存动态分配和垃圾回收的问题,顾名思义,垃圾回收就是释 ...

  9. 浅析Java虚拟机的垃圾回收机制(GC)

    目录 一.垃圾回收机制(Garbage Collection) 二.对象回收的时机 引用计数法 可达性分析算法 三.垃圾回收算法 标记-清除算法 标记-复制算法 标记-整理算法 新生代.老年代.永久代 ...

最新文章

  1. ODBC更新记录集提示”记录集为只读“
  2. ftp://ftp.cs.wisc.edu/
  3. springboot单元测试通过MockMvc类调用controller接口
  4. java des验证码,Servlet返回验证码
  5. Nacos更新配置参数不生效
  6. python实现数字形式转换
  7. Python编写简易木马程序
  8. json python无效语法_在python中打开无效的json文件
  9. [Wannafly挑战赛2D-Delete]最短路
  10. 源码分析Thread
  11. 在MacOSX上重新安装Python (10.8) python 自然语言处理的前戏
  12. Oracle创建视图、通过视图创建表
  13. 一个合格的程序员,需要哪些必备技能?
  14. 滴滴司机问我会 LRU 吗?
  15. win10卸载电脑管家就蓝屏_新电脑WIN10出现蓝屏 系统重装也不行
  16. python获取小程序手机号并绑定
  17. java参数化查询_【转】参数化查询为什么能够防止SQL注入
  18. ios+html+音频播放器代码,音频播放器代码 - 代码大全
  19. 单片机——LED点阵
  20. npm --save-dev 和 --save的区别

热门文章

  1. VC++删除浮动工具条中“关闭”按钮
  2. 退出功能—session
  3. 【Monkey】Monkey基础概念
  4. GIve Me A Welcome Hug!
  5. SQL Server 2005存储过程示例
  6. 泽元网站内容管理系统 (简称ZCMS)
  7. 网页设计-[CSS+DIV设计实例:超酷的竖排导航栏 ]
  8. 如何修改wampserver中mysql中字符编码的解决方案
  9. data.length 提示undefined 问题解决
  10. 解决vue多个路由共用一个页面的问题