前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【GC系列】JVM垃圾定位及垃圾回收算法浅析

【GC系列】JVM垃圾定位及垃圾回收算法浅析

作者头像
行百里er
发布2020-12-02 15:36:02
6630
发布2020-12-02 15:36:02
举报
文章被收录于专栏:JavaJourneyJavaJourney

0x01 什么是垃圾

很简单,没有引用指向的任何对象都叫做垃圾(garbage)。

什么是garbage

在某一内存空间中,Java程序制造了很多对象被引用,有的对象还引用别的对象,中途有对象不被需要了就没有指向他的引用了,这些没有引用指向的东西就是垃圾

这些垃圾不需要自己回收,JVM中有类似于街道上那些勤劳的环卫工的人,在帮忙回收垃圾。

0x02 如何找到垃圾

那么,帮忙回收垃圾的人是如何找到垃圾的呢?JVM中一般有两种算法:

  • Reference Count 引用计数
  • Root Searching 根可达算法

2.1 引用计数

引用计数法设定给对象中添加一个引用计数器,每当有一个地方引用它时,计数器值加1;当引用失效时,计数器值减1,引用数量为0的时候,则说明对象没有被任何引用指向,,可以认定是“垃圾”对象。

引用计数法

但是引用计数法不能解决循环引用的问题,比如O1->O2->O3->O1,当没有引用指向他们任何一个的时候,他们的reference count都是1,按照引用计数法,他们都不是垃圾。

而事实上,没有任何引用指向O1、O2、O3这一坨了,他们都是垃圾。

JVM(看向O1):坚决不能容忍垃圾! O1:看什么,我的引用计数为1,不是0,我不是垃圾。 JVM:不好意思,我不是针对你,我是说你们一坨都是垃圾!

引用计数法无法确定垃圾的情况

2.2 根可达算法

引用计数法不能解决循环引用的问题,可采用根可达算法(Root Searching)。

其算法思路就是通过一系列名为 GC Roots 的对象作为根,从根上开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到 GC Roots 没有任何引用链相连时,则证明此对象是不可用的。

那么哪些是 Roots 呢?

  • 线程栈变量

Java程序从main方法开始执行,main方法会开启一个线程,这个线程里有线程栈,里面有栈帧。

从main开始这个线程栈帧里面的这些个叫做根对象。

  • 静态变量

一个class被load到内存之后,马上就对静态变量进行初始化,所以静态变量访问到的对象也是根对象。

  • 常量池

如果一个class能够用到其他的class的对象,那么他就是根对象。

  • JNI指针

本地方法用到本地的对象也是根对象。

总之,当一个程序起来之后马上需要的对象叫做根对象。

0x03 常见的垃圾回收算法

垃圾找到了之后就要回收,那么JVM怎么进行垃圾回收呢?

如何进行垃圾清除,常用的算法有3种:

  • Mark-Sweep 标记清除
  • Coping 拷贝算法
  • Mark-Compact 标记压缩

3.1 标记清除

先标记垃圾,然后清除垃圾。通过该算法,先找到那些有用的对象,没有用的并出来然后把它们清除掉。

从图中可以看出,标记清除算法将垃圾标记并清除后,内存中原先不可用的内存变成了空闲的可用的,但是这些内存有些有些不连续了,也就是说产生了碎片

再一个,如果存活的对象比较多,这种情况标记清除算法的执行效率比较高。相反执行效率就稍微低一点,因为需要两遍扫描,第一次扫描找到那些有用的,第二次扫描把那些没用的找出来清理掉。

3.2 拷贝算法

拷贝算法,就是把内存一分为二,比如A、B两个区域,分开之后,把A区域中有用的拷贝到B区域,拷贝完成后,把A区域全部清除,下次再分配内存的时候先往B区域分配,如此往复。

1) 把内存一分为二,分为A、B两个区域,分配内存先往A区域分配

2) 拷贝A区域中存活的有用的对象到B区域

拷贝完成的状态:

3) 将A区域全部清除(内存全部释放)

4) 之后再分配内存的时候往B区域分配

清除B之后在继续往A区域分配,如此往复,拷贝来拷贝去。

以上过程就是拷贝算法。

该算法适用于存活对象较少的情况,只扫描一次,效率有所提高并且没有产生内存碎片。需要注意的是,移动复制对象时须调整对象引用。

缺点也显而易见,得准备两份内存,浪费空间

3.3 标记压缩

对象分配到内存之后,需要回收的时候,先把没有引用指向的对象标记为垃圾,然后把后面存活的对象拷贝到标记的那个地方,不仅如此,最后凡是有用的对象全部移到前面,无论这个内存是没有使用,都压缩整理到前面,最后剩下的大块空间就全部清理出来了。

可以看到,标记压缩进行回收垃圾之后,空间连续,没有碎片。

我们分析一下该算法的实现思路,

1)通过GC Roots找到那些有用的不可回收的

2)把不可回收的有用对象往前移动

所以肯定需要扫描两次内存,而且还要移动对象,第一次扫描先找有用对象,第二次扫描移动对象。

移动的过程中,如果是多线程还要考虑线程同步,所以标记压缩算法效率上要低一些。

该算法的优点在于对象分配不会产生内存减半,而且不会产生内存碎片。

0x04 小结

  1. 没有任何引用指向的对象是垃圾
  2. 找到垃圾的算法有引用计数法(Reference Count)和根可达算法(Root Searching)
  3. 引用计数法无法解决循环引用的问题
  4. GC Roots:线程栈变量、静态变量、常量池、JNI指针
  5. 垃圾回收算法:标记清除(Mark Sweep)、拷贝(Copying)、标记压缩(Mark Compact)
  6. 标记清除算法容易产生内存碎片使内存空间不连续
  7. 拷贝算法没有内存碎片产生但是浪费空间
  8. 标记压缩算法效率虽然有点低但没有内存碎片产生且不浪费内存空间

往期推荐

synchronized连环问-java并发编程基础知识储备

Java最强大的技术之一:反射

Object o = new Object()在内存中占多少字节?

深入底层探析网络编程之多路复用器(select,poll,epoll)

通过Java Socket编程观察内核级TCP的三次握手

内核中PageCache和java文件系统IO/NIO以及内存中缓冲区的作用

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-10-29,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 行百里er 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 0x01 什么是垃圾
  • 0x02 如何找到垃圾
    • 2.1 引用计数
      • 2.2 根可达算法
      • 0x03 常见的垃圾回收算法
        • 3.1 标记清除
          • 3.2 拷贝算法
            • 3.3 标记压缩
            • 0x04 小结
            相关产品与服务
            文件存储
            文件存储(Cloud File Storage,CFS)为您提供安全可靠、可扩展的共享文件存储服务。文件存储可与腾讯云服务器、容器服务、批量计算等服务搭配使用,为多个计算节点提供容量和性能可弹性扩展的高性能共享存储。腾讯云文件存储的管理界面简单、易使用,可实现对现有应用的无缝集成;按实际用量付费,为您节约成本,简化 IT 运维工作。
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档