垃圾回收基础

  • 1.判断垃圾是否可被回收
    • 1.1.引用计数法
    • 1.2.可达性分析算法
  • 2.五种引用
    • 2.1.强引用
    • 2.2.软引用
      • 2.2.1.软引用介绍
      • 2.2.2.软引用应用
    • 2.3.弱引用
    • 2.4.虚引用
    • 2.5.终结器引用
    • 2.6.引用队列
    • 2.7.总结
  • 3.垃圾回收算法
    • 3.1.标记清除算法
    • 3.2.标记整理
    • 3.3.复制
    • 3.4.小结

1.判断垃圾是否可被回收

1.1.引用计数法

只要一个对象被其它对象所引用,那么我们就让这个对象的计数加1,如果被引用了两次,计数就变为2.如果某个对象不再引用它了,就让它的计数减1
当某个对象的引用计数变为0的时候,意味着没有人引用它,那么它就可以做垃圾回收。
弊端:循环引用时,两个对象的计数都为1,导致两个对象都无法被释放

Java虚拟机一般采用可达性分析算法

1.2.可达性分析算法

JVM中的垃圾回收器通过可达性分析来探索所有存活的对象
GC Root根对象指的是肯定不会被垃圾回收的对象。
扫描堆中的对象,看能否沿着GC Root对象为起点的引用链找到该对象(根对象),如果找不到,则表示可以回收
即判断每一个对象是否会被根对象直接或间接的引用,如果是,那么这个对象就不能被回收,如果没有,那么这个对象就会被垃圾回收。例如,葡萄串。

可以作为GC Root的对象
虚拟机栈(栈帧中的本地变量表)中引用的对象。 
方法区中类静态属性引用的对象
方法区中常量引用的对象
本地方法栈中JNI(即一般说的Native方法)引用的对象
比如系统类。即核心类(System,Object,String,HashMap)都可以作为GC Root类。synchronized加锁的对象。调用Native方法的对象。活动线程不能当垃圾。线程栈帧内所引用的对象。如下面代码中的ArrayList

public static void main(String[] args) throws InterruptedException, IOException {List<Object> list1 = new ArrayList<>();list1.add("a");list1.add("b");System.out.println(1);System.in.read();list1 = null;System.out.println(2);System.in.read();System.out.println("end...");}

2.五种引用

2.1.强引用

我们平时在用的所有引用基本都属于强引用,比如new对象,通过赋值运算符赋值给了一个变量,这个变量就强引用了这个对象。
只要沿着GC Root的引用链找到它,那么它就不会被垃圾回收
只有GC Root都不引用该对象时,才会回收强引用对象
如上图B、C对象都不引用A1对象时,A1对象才会被回收

2.2.软引用

2.2.1.软引用介绍

当GC Root指向软引用对象时,在内存不足时,会回收软引用所引用的对象
如果B对象直接引用A2对象,则不会被回收。只要没有强引用引用它,即边有软引用,就可能会被回收。
如上图如果B对象不再引用A2对象且内存不足时,软引用所引用的A2对象就会被回收

软弱引用还会配合引用队列来工作,当软引用对象被回收掉了以后,软引用自身也是一个对象,如果在创建时给它分配了一个引用队列,那它所引用的对象被回收时,软引用就会进入这个队列,弱引用也是类似。如果想对它们所占用的内存进一步释放,需要使用引用队列来找到它们来做进一步的处理。

2.2.2.软引用应用

/*** 演示软引用* -Xmx20m -XX:+PrintGCDetails -verbose:gc     //堆内存大小事先设置了20M*/
public class Demo2_3 {private static final int _4MB = 4 * 1024 * 1024;public static void main(String[] args) throws IOException {//如果使用下面代码,不调用soft()函数,最终会报Java.lang.OutOfMemoryError:java heap space错误,堆内存不足。/*List<byte[]> list = new ArrayList<>();for (int i = 0; i < 5; i++) {list.add(new byte[_4MB]);}System.in.read();*/          soft();}//下面使用软引用。public static void soft() {// list --> SoftReference --> byte[]//list引用了软引用对象,软引用对象引用了byte数组List<SoftReference<byte[]>> list = new ArrayList<>();for (int i = 0; i < 5; i++) {SoftReference<byte[]> ref = new SoftReference<>(new byte[_4MB]);System.out.println(ref.get());list.add(ref);System.out.println(list.size());}System.out.println("循环结束:" + list.size());for (SoftReference<byte[]> ref : list) {System.out.println(ref.get());}}
}


如图,当使用soft()函数时,它并没有引起内存溢出,我们可以看到在循环中获得byte数组的内存地址是可以的,但是最后再循环一遍,再次去取的时候,只有最后一个被保留下来了。上面对象被清理了,但是引用没有被清理,引用已经没有意义了。
如果在垃圾回收时发现内存不足,在回收软引用所指向的对象时,软引用本身不会被清理
如果想要清理软引用,需要使用引用队列

public class Demo1 {public static void main(String[] args) {final int _4M = 4*1024*1024;//使用引用队列,用于移除引用为空的软引用对象ReferenceQueue<byte[]> queue = new ReferenceQueue<>();//使用软引用对象 list和SoftReference是强引用,而SoftReference和byte数组则是软引用List<SoftReference<byte[]>> list = new ArrayList<>();SoftReference<byte[]> ref= new SoftReference<>(new byte[_4M]);//遍历引用队列,如果有元素,则移除Reference<? extends byte[]> poll = queue.poll();while(poll != null) {//引用队列不为空,则从集合中移除该元素list.remove(poll);//移动到引用队列中的下一个元素poll = queue.poll();}}
}

2.3.弱引用

只有弱引用引用该对象时,在垃圾回收时,无论内存是否充足,都会回收弱引用所引用的对象

如上图如果B对象不再引用A3对象,则A3对象会被回收
弱引用的使用和软引用类似,只是将 SoftReference 换为了 WeakReference

2.4.虚引用

当虚引用对象所引用的对象被回收以后,虚引用对象就会被放入引用队列中,调用虚引用的方法

虚引用的一个体现是释放直接内存所分配的内存,当引用的对象ByteBuffer被垃圾回收以后,由于它所分配的直接内存并不能被Java垃圾回收所管理,虚引用对象Cleaner就会被放入引用队列中,虚引用所在的引用队列会由一个叫做ReferenceHandler的线程来定时的去引用队列中找,如果有,然后调用Cleaner的clean方法来释放直接内存。clean方法会根据前面记录的直接内存的地址调用Unsafe对象的freeMemory来释放掉直接内存。
如上图,B对象不再引用ByteBuffer对象,ByteBuffer就会被回收。但是直接内存中的内存还未被回收。这时需要将虚引用对象Cleaner放入引用队列中,然后调用它的clean方法来释放直接内存

2.5.终结器引用

所有的类都继承自Object类,Object类有一个finalize方法。当某个对象重写了finalize方法不再被其他的对象所引用时,此时可以被当成垃圾进行回收,会先将终结器引用对象放入引用队列中,然后根据终结器引用对象找到它所引用的对象,然后调用该对象的finalize方法。调用以后,该对象就可以被垃圾回收了

如上图,B对象不再引用A4对象。这是终结器对象就会被放入引用队列中,引用队列会根据它,找到它所引用的对象。然后调用被引用对象的finalize方法。调用以后,该对象就可以被垃圾回收了。

2.6.引用队列

软引用和弱引用可以配合引用队列

在弱引用和虚引用所引用的对象被回收以后,会将这些引用放入引用队列中,方便一起回收这些软/弱引用对象

虚引用和终结器引用必须配合引用队列

虚引用和终结器引用在使用时会关联一个引用队列

2.7.总结

  1. 强引用
    只有所有 GC Roots 对象都不通过【强引用】引用该对象,该对象才能被垃圾回收

  2. 软引用(SoftReference)
    仅有软引用引用该对象时,在垃圾回收后,内存仍不足时会再次出发垃圾回收,回收软引用 对象 可以配合引用队列来释放软引用自身

  3. 弱引用(WeakReference)
    仅有弱引用引用该对象时,在垃圾回收时,无论内存是否充足,都会回收弱引用对象 可以配合引用队列来释放弱引用自身

  4. 虚引用(PhantomReference)
    必须配合引用队列使用,主要配合 ByteBuffer 使用,被引用对象回收时,会将虚引用入队, 由 Reference Handler 线程调用虚引用相关方法释放直接内存

  5. 终结器引用(FinalReference)
    无需手动编码,但其内部配合引用队列使用,在垃圾回收时,终结器引用入队(被引用对象 暂时没有被回收),再由 Finalizer 线程通过终结器引用找到被引用对象并调用它的 finalize 方法,第二次 GC 时才能回收被引用对象

3.垃圾回收算法

3.1.标记清除算法

定义:标记清除算法顾名思义,是指在虚拟机执行垃圾回收的过程中,先采用标记算法确定可回收对象,然后垃圾收集器根据标识清除相应的内容,给堆内存腾出相应的空间

这里的腾出内存空间并不是将内存空间的字节清0,而是记录下这段内存的起始结束地址,下次分配内存的时候,会直接覆盖这段内存

优点:速度快
缺点:容易产生大量的内存碎片,可能无法满足大对象的内存分配,一旦导致无法分配对象,那就会导致jvm启动gc,一旦启动gc,我们的应用程序就会暂停,这就导致应用的响应速度变慢

说明:我们之前曾经探讨过如何判断一个对象是垃圾,我们是沿着GC Root的引用链去找,扫描整个堆对象的过程中,如果发现对象却是被引用了,就保留,有一个对象没有GC Root引用它,那么就可以当成是垃圾。标记清除算法分为两个阶段,第一阶段标记,第二阶段做清除。把垃圾对象所占用的空间做释放。

3.2.标记整理

标记-整理 会将不被GC Root引用的对象回收,清楚其占用的内存空间。然后整理剩余的对象。
可以有效避免因内存碎片而导致的问题,但是因为整体需要消耗一定的时间,所以效率较低

3.3.复制

将内存分为等大小的两个区域,FROM和TO(TO中为空)先将被GC Root引用的对象从FROM放入TO中,此时FROM区就全部都是垃圾了,再回收不被GC Root引用的对象此时然后交换FROM和TO。
这样也可以避免内存碎片的问题,但是会占用双倍的内存空间。




3.4.小结

这三种算法各有优劣,实际在JVM的垃圾回收机制中,都会根据不同的情况来采用,它会结合三种算法共同实现垃圾回收。

JVM学习-垃圾回收基础相关推荐

  1. JVM学习-垃圾回收调优

    目录 1.GC调优预备知识 2.GC调优领域与目标 3.最快的GC是不发生GC 4.新生代调优 5.幸存区调优 5.老年代调优 1.GC调优预备知识 预备知识 掌握 GC 相关的 VM 参数,会基本的 ...

  2. jvm垃圾回收机制_深入理解JVM的垃圾回收机制

    ​如何判断对象已"死" Java堆中存放着几乎所有的对象实例,垃圾回收器在堆进行垃圾回收前,首先要判断这些对象那些还存活,那些已经"死去".判断对象是否已&qu ...

  3. 这么多人问的JVM的垃圾回收到底是个啥?

    文章目录 概述 什么是垃圾 为什么需要GC 早期垃圾回收 Java垃圾回收机制 垃圾回收算法 标记阶段:引用计数算法 标记阶段:可达性分析算法 对象的 finalization 机制 MAT 与 JP ...

  4. JVM与垃圾回收笔记

    JVM与垃圾回收 1.JVM与Java体系结构 1.1 Java虚拟机 1.1.1 简介 1.1.2 作用 1.1.3 特点 1.1.4 JVM的位置 1.2 JVM的整体结构 1.3 java代码执 ...

  5. 细说JVM的垃圾回收机制

    什么是垃圾回收? 从字面看来,按字面意思来理解就是--找到垃圾对象并将他们抛弃掉:事实却正好相反,垃圾回收是把处于活动状态的对象找出来,而将剩余的对象标记为垃圾对象.基于此理论,我们来详细描述java ...

  6. Java虚拟机(三)——初识JVM的垃圾回收机制

    前言 对于程序计数器.虚拟机栈.本地方法栈这三个部分而言,其生命周期与相关线程有关,随线程而生,随线程而灭.并且这三个区域的内存分配与回收具有确定性,因为当方法结束或者线程结束时,内存就自然跟着线程回 ...

  7. JVM—7—垃圾回收概述及相关算法

    文章目录 一.垃圾回收概述 1.概念 2.什么是垃圾 2.1 什么是垃圾? 2.2 磁盘碎片整理 2.3 大厂面试题 3.为什么需要GC 4.早期垃圾回收 5.Java垃圾回收机制 5.1 优点 5. ...

  8. JVM中垃圾回收相关算法 - 值得了解一下的,因为早晚得了解

    JVM中垃圾回收相关算法 - 我想是值得你了解一下的,因为早晚得了解.

  9. JVM之垃圾回收 II ——方法区和堆区的垃圾回收、STW

    垃圾回收 II 一.方法区(jdk1.7)/元空间(jdk1.8) 1.永久代和元空间的关系: 2.方法区/元空间的垃圾回收内容 3.方法区/元空间垃圾回收的方式 二.堆 1.GC堆的划分 2.堆区G ...

最新文章

  1. python1000个常用代码-1000个常用的Python库和示例代码
  2. 转贴 DISCUZ7.0 恢复被删除的会员的UID
  3. http://www.myeclipseide.com/ 官网打不开的问题(转)
  4. 真香!用 4K 高清显示器写代码,包邮送一台!
  5. 微软发布最新开源Blog平台“Oxite”
  6. python函数里面,一个*是可变参数的元祖,两个*是可变参数的字典
  7. 易语言通过服务器发送文件,易语言局域网文件传输带聊天功能例程
  8. ubuntu16.04下FSA-Net环境安装和训练
  9. 数据结构 - AVL木
  10. MSDN2008中文版下载地址
  11. ce修改手游服务器的数据,CE修改器修改游戏数据的方法
  12. @MapperScan和@Mapper的使用
  13. jvm学习——jvm内存区域
  14. 2021年1月6日运行Python脚本的一些说明与教程
  15. python语言求年份的生肖
  16. 一个“点赞”功能的实现代码
  17. 前端——js关闭页面方法
  18. 如何实时计算日累计逐单资金流
  19. 使用Python 封装一个简单的Mysql工具类
  20. Nginx中rewrite的用法详解

热门文章

  1. 锤子不死!还有新手机,但要等很久...
  2. 受上海Model S自燃影响?特斯拉市值周一蒸发18亿美元
  3. 微信上让人反感的5种行为 敢不敢看看你是否也犯过
  4. 卸任四家锤子公司法定代表人后:罗永浩退出聊天宝股东行列
  5. 三地警察抓兔子[转]
  6. 实现⼀个简洁版的promise
  7. 常用的sql语句用法
  8. python get,post提交表单
  9. mongoose c++封装
  10. u-boot移植随笔:u-boot2010.09移植到8MB Nor Flash的S3C2440,第一步搞定(补记)