什么是垃圾?

对于程序汇总分配的内存,当使用完成后,这部分内存就会成为垃圾,需要对其进行释放,否则,这部分内存将无法被重复利用,最终造成内存泄漏。

什么是GC?

GC是一种自动的存储管理机制。当一些被占用的内存不再需要时,就应该予以释放。这种存储资源管理,称为垃圾回收。

对于java而言,是自动进行垃圾回收的。

如何发现垃圾?

既然要实现垃圾的自动回收,那么第一件事就是找到垃圾,那么如何发现垃圾呢?其实就是判断这个对象是否存活。

常见的两种方式判断

  • 1)引用计数法(reference count)
  • 2)根可达性算法(root searching)
名称 实现思想 优点 缺点
引用计数法 给每个对象添加一个引用计数器,当存在一个引用时,就加1,当一个引用失效时,就减1。 判定效率高 1、无法解决相互引用、循环引用的问题。 2、存储空间开销:需要空间存储计数器。 3、时间开销:需要处理计数器的增加和减少。
根可达性算法 通过一系列名为”GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的。 解决了循环引用的问题 实现较复杂,增加了计算成本。

引用计数法(reference count)的循环引用、相互引用: 没有外部引用,但是本身的计数器又不为0。

根可达性算法 由于引用计数法存在的问题,所有主流的jvm都不使用引用计数法,而是采用根可达性算法

如上图,带有GCRoots的对象表示正在被引用,而其他的对象虽然相互间有引用,但是没有根节点,仍然会被删除。

GCRoots对象: 哪些对象可以成为GCRoots呢?jvm中主要针对堆内的内存进行垃圾回收,而在虚拟机栈、本地方法栈和方法区内的对象则不会被回收,通常选择这三个区域的对象作为GCRoots。

在jvm中主要有以下四种,在方法区存在两种:

  • 1)虚拟机栈中引用的对象:虚拟机栈帧中的局部变量表所引用的对象
  • 2)本地方法栈中引用的对象:JNI (Native方法)引用的对象
  • 3)方法区中类静态和常量对象:静态变量常量引用的对象

以下图来展示在JVM内存模型(JMM)的GCRoots:

 在根可达性算法中,所有的引用都是强引用,下面具体分析下jvm中的四种引用。

四种引用: 参考:分享JVM 的四种引用方式

名称 定义 特点 回收
强引用 强引用就是引用了通过new 的方式创建的对象。是指创建一个对象并把这个对象赋给一个引用变量;在root搜索算法的里面,说的引用都指的是强引用关系。 GC时,永远不会被回收,导致OOM的主要原因 1、引用消失(比如方法执行完) 2、将引用变量设置为null
软引用 如果一个对象具有软引用,内存空间足够,垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高速缓存,比如网页缓存、图片缓存等。使用软引用能防止内存泄露,增强程序的健壮性。Java中,用SoftRefrence表示弱引用。 内存不足时(自动触发GC),会被回收 内存不足时,触发自动回收
弱引用 引用也是用来描述非必需对象的,当JVM进行垃圾回收时,无论内存是否充足,都会回收被弱引用关联的对象。在java中,用java.lang.ref.WeakReference类来表示 无论内存是否充足,只要进行GC,都会被回收 只要进行GC,都会被回收
虚引用 虚引用和前面的软引用、弱引用不同,它并不影响对象的生命周期。在java中用java.lang.ref.PhantomReference类表示。如果一个对象与虚引用关联,则跟没有引用与之关联一样,在任何时候都可能被垃圾回收器回收。 要注意的是,虚引用必须和引用队列关联使用,设置虚引用关联的唯一目的,就是在这个对象被收集器回收的时候收到一个系统通知或者后续添加进一步的处理。Java技术允许使用finalize()方法在垃圾收集器将对象从内存中清除之前做必要的清理工作。 如同虚设,和没有引用没什么区别 任何时候都可能被回收

垃圾如何处理?

我们通过上面学到的根可达性算法可以发现垃圾的所在,那么jvm是如何进行垃圾回收的呢?通过jvm提供的垃圾收集器(GC) 。

目前有以下种类的垃圾收集器,其中虚线表示垃圾收集器可以进行组合使用:

常见的垃圾收集算法

标记清除(mark sweep) :位置不连续 产生碎片 效率偏低(两遍扫描) 拷贝算法 (copying) :没有碎片,浪费空间 标记压缩(mark compact) :没有碎片,效率偏低(两遍扫描,指针需要调整)

标记清除(mark sweep)

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

标记: Collector从引用根结点开始遍历,标记所有被引用的对象。一般是在对象的Header中记录为可达对象。

清除: Collector对堆内存从头到尾进行线性的遍历,如果发现某个对象在其Header中没有标记为可达对象,则将其回收。

对所有能找到根节点引用的内存空间进行标记,清除没有找到根节点的内存空间,其大概实现过程如下:

缺点 :

  • 1)STW(stop the word),回收时,应用挂起。
  • 2)内存越大,效率越多,需要扫描的时间越长。
  • 3)内存碎片化,会导致无法装下新申请的对象,整体内存是足够的,但并非连续的。

拷贝算法 (copying)

拷贝算法将内存空间划分为两个区间,在任意时间点,所有动态分配的对象都只能分配在其中一个区间(称为活动区间),而另外一个区间(称为空闲区间)则是空闲的。

当活动区间的内存占满时,接下来GC线程会将活动区间内的存活对象,全部复制到空闲区间,且严格按照内存地址依次排列,与此同时,GC线程将更新存活对象的内存引用地址指向新的内存地址。

其大概过程如下图所示:

缺点 浪费内存,并且存活对象越多的情况下,效率越低。

标记压缩/标记整理(mark compact)

标记过程仍然和标记-清除一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理端边界以外的内存。

实现过程大概如下:

缺点 效率不高,除了要标记存活对象,还要整理存活对象的引用地址,效率低于复制算法。

总结 以上三种算法都是根据根可达性算法实现的。当开始GC时,三种算法都会造成STW(stop the world)。

JVM的内存模型如何实现垃圾回收?分代模型

文章前面介绍了简单很多种垃圾收集器,不同的垃圾收集器有不同的分代模型:

  • 1)除Epsilon、ZGC、Shenandoah之外的GC都是使用逻辑分代模型
  • 2)G1是逻辑分代,物理不分代
  • 3)除1)2)之外不仅逻辑分代,而且物理分代

分代模型:

上图中的分代模型有些需要特别关注的点:

  • 1)整个分代模型的组成:新生代 老年代 永久代(jdk1.7)/元空间(jdk1.8) 永久代、元空间:Class 永久代:必须指定大小限制 元空间:可以设置大小,也可以不设置,无上限(受限于物理内存) 字符串常量池: jdk1.7 - 永久代,jdk1.8 - 堆 MethodArea(方法区)逻辑概念:永久代、元数据
  • 2)新生代: Eden 2个suvivor区 YGC回收之后,大多数的对象会被回收,活着的进入s0 再次YGC,活着的对象eden s0 -> s1 再次YGC,活着的对象eden s1 -> s0 年龄足够 -> 老年代 (通常15,对象头Mark Word 的age只有4bit,最大是15 、CMS 6) suvivor区装不下 -> 老年代
  • 3)老年代 老年代满了FGC Full GC(STW)

GC Tuning 尽量减少FGC MinorGC = YGC MajorGC = FGC

到此这篇关于JVM GC 垃圾收集梳理总结的文章就介绍到这了,更多相关JVM GC 垃圾收集 内容请搜索Devmax以前的文章或继续浏览下面的相关文章希望大家以后多多支持Devmax!

JVM GC 垃圾收集梳理总结的更多相关文章

  1. ios – 绘制一个填充的半圆

    在UIView中绘制填充(!我玩了CGContextAddArc函数,但它似乎没有提供填充.这是我想画的东西–两个填充的半圆圈.解决方法

  2. android – 基于JVM的语言,没有语言运行时

    ProGuard可以删除特定程序中未使用的部分运行时.如果速度和时间非常关键,另一种方法是使用NDK.

  3. android – 如何在gradle中调整dex内存的jvm args?

    我有一个Android项目,它在dex步骤中当前没有堆空间:我想在gradle中提高jvmmin/max设置,就像我们以前使用Maven插件一样:但是在gradle中的android插件的文档中我只看到这些选项:有办法吗?

  4. android – Timer()作为守护进程与非守护进程

    什么时候应该在Android应用程序中作为守护进程启动计时器?

  5. JVM的常用命令汇总

    监测java应用,最方便的就是直接使用jdk提供的现成工具,在jdk的安装的bin目录下,已经提供了多种命令行监测工具。本文为大家总结了几个JVM的常用命令,需要的可以参考一下

  6. Java JVM虚拟机调优详解

    JVM是JavaVirtualMachine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的,本文主要介绍了jvm调优,感兴趣的小伙伴们可以参考一下<BR>

  7. Java JVM中线程状态详解

    这篇文章主要介绍了Java JVM中线程状态详解,文章围绕主题展开详细的内容介绍,具有一定的参考价值,感兴趣的朋友可以参考一下

  8. JVM jstack实战之死锁问题详解

    如果在生产环境发生了死锁,我们将看到的是部署的程序没有任何反应了,这个时候我们可以借助 jstack进行分析,下面我们实战操作查找死锁的原因

  9. java自旋锁和JVM对锁的优化详解

    这篇文章主要为大家介绍了java自旋锁和JVM对锁的优化示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

  10. Java 对象在 JVM 中的内存布局超详细解说

    这篇文章主要介绍了Java 对象在 JVM 中的内存布局超详细解说,文章围绕主题展开详细的内容介绍,具有一定的参考价值,需要的小伙伴可以参考一下

随机推荐

  1. 基于EJB技术的商务预订系统的开发

    用EJB结构开发的应用程序是可伸缩的、事务型的、多用户安全的。总的来说,EJB是一个组件事务监控的标准服务器端的组件模型。基于EJB技术的系统结构模型EJB结构是一个服务端组件结构,是一个层次性结构,其结构模型如图1所示。图2:商务预订系统的构架EntityBean是为了现实世界的对象建造的模型,这些对象通常是数据库的一些持久记录。

  2. Java利用POI实现导入导出Excel表格

    这篇文章主要为大家详细介绍了Java利用POI实现导入导出Excel表格,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  3. Mybatis分页插件PageHelper手写实现示例

    这篇文章主要为大家介绍了Mybatis分页插件PageHelper手写实现示例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

  4. (jsp/html)网页上嵌入播放器(常用播放器代码整理)

    网页上嵌入播放器,只要在HTML上添加以上代码就OK了,下面整理了一些常用的播放器代码,总有一款适合你,感兴趣的朋友可以参考下哈,希望对你有所帮助

  5. Java 阻塞队列BlockingQueue详解

    本文详细介绍了BlockingQueue家庭中的所有成员,包括他们各自的功能以及常见使用场景,通过实例代码介绍了Java 阻塞队列BlockingQueue的相关知识,需要的朋友可以参考下

  6. Java异常Exception详细讲解

    异常就是不正常,比如当我们身体出现了异常我们会根据身体情况选择喝开水、吃药、看病、等 异常处理方法。 java异常处理机制是我们java语言使用异常处理机制为程序提供了错误处理的能力,程序出现的错误,程序可以安全的退出,以保证程序正常的运行等

  7. Java Bean 作用域及它的几种类型介绍

    这篇文章主要介绍了Java Bean作用域及它的几种类型介绍,Spring框架作为一个管理Bean的IoC容器,那么Bean自然是Spring中的重要资源了,那Bean的作用域又是什么,接下来我们一起进入文章详细学习吧

  8. 面试突击之跨域问题的解决方案详解

    跨域问题本质是浏览器的一种保护机制,它的初衷是为了保证用户的安全,防止恶意网站窃取数据。那怎么解决这个问题呢?接下来我们一起来看

  9. Mybatis-Plus接口BaseMapper与Services使用详解

    这篇文章主要为大家介绍了Mybatis-Plus接口BaseMapper与Services使用详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

  10. mybatis-plus雪花算法增强idworker的实现

    今天聊聊在mybatis-plus中引入分布式ID生成框架idworker,进一步增强实现生成分布式唯一ID,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

返回
顶部