前言

对于垃圾收集器回收内存应该有以下几点思考:

  1. 哪些内存需要回收?
  2. 什么时候被回收?
  3. 如何回收?

哪些内存需要被回收?

在Java内存运行时区域中,虚拟机栈、本地方法栈、程序计数器3个区域随线程而生,随线程而亡。所以它们不需要垃圾收集器来管理。

方法区则不一样,堆中存放的对象实例和方法区中的内存只有在运行期间才知道,这部分内存的分配和回收都是动态的,垃圾收集器关注的就是这部分内存。

内存什么时候被回收?

在堆中存放着Java世界中几乎所有的对象实例,垃圾收集器在对堆进行回收之前,需要知道哪些对象还“存活着”,哪些已经“死去”。

而判断对象的存活还是死去有以下两种方式:引用计数算法可达性分析算法

引用计数算法

引用计数算法是通过在对象中添加一个引用计数器,每当有一个地方引用该对象时,计数器值就加1;当引用失效时,计数器值就减1;任何时刻计数器为0的对象就表示永远不会再被引用的对象,当GC到了回收内存时,就会回收这部分对象。

该算法实现简单,判定效率也很高,但是主流的Java虚拟机里都没有选用引用计数算法来管理内存,因为它很难解决对象之间相互引用的问题。

 
/**
* testGC()方法执行后,objA和objB会不会被GC呢?
*/
public class ReferenceCountingGC {public Object instance = null;private static final int _1MB = 1024 * 1024;/**
* 这个成员属性的唯一意义就是占点内存,以便在能在GC日志中看清楚是否有回收过
*/
private byte[] bigSize = new byte[2 * _1MB];public static void testGC() {
ReferenceCountingGC objA = new ReferenceCountingGC();
ReferenceCountingGC objB = new ReferenceCountingGC();
objA.instance = objB;
objB.instance = objA;objA = null;
objB = null;// 假设在这行发生GC,objA和objB是否能被回收?
System.gc();
}
}

在该示栗中,可以发现第一个实例化的ReferenceCountingGC对象同时被objA和objB.instance所引用,第二个实例化的对象同理,当将objA和objB设为null后,由于这两个对象还持有其他引用,因此无法被垃圾收集器回收。

可达性分析算法

在主流的商用程序语言(Java、C#)的主流实现中,都是通过可达性分析来判定对象是否存活。

该算法的思想是通过一系列称为“GC Roots”的对象作为起始点,从这些节点向下搜索,搜索所走过的路径称为引用链,当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的。

上图中可以发现,object1、object2、object3都可以到达GC Roots,因此它们是存活的对象,而object5、object6、object7虽然它们互联,但它们却无法到达GC Roots,因此它们三兄弟是可以被收回的对象。

在Java中,可以作为GC Roots的对象有全局性引用(例如常量或类静态属性)与执行上下文(例如栈帧中的本地变量表)。

当然,对于该算法中不可达的对象也不是“非死不可的”。在下面的这种情况下一个对象也可以做到起死回生。

在执行该算法时,真正要宣告一个对象的死亡,需要经历两次标记过程。如果发现一个对象没有直达GC Roots的引用链时,会对该对象进行第一次标记,并且会进行一次筛选操作,筛选的条件是此对象是否有必要执行finalize()方法。

如果一个对象没有覆写finalize()方法或是finalize()方法已经被虚拟机调用过,则表示没必要执行finalize()方法。

如果一个对象被判定为有必要执行finalize()方法。那么该对象将会被放在一个F-Queue的队列中,并在稍后由一个虚拟机自动建立的、低优先级的Finalizer线程去执行该对象的finalize()方法。如果在finalize()方法中该对象成功的拯救了自己(重新与引用链上的一个对象建立关联),那么在该对象在进行第二次标记时则可以被移除出“即将回收”的集合。否则在进行第二次标记时它就真的完成了它的使命然后被收回了。

任何一个对象的finalize()方法只会被系统自动调用一次。

方法区中的垃圾回收

对于方法区中的垃圾收集,垃圾收集器主要回收废弃的常量无用的类

判断一个常量是否废弃只需要看常量是否还会被使用。拿常量池举例,如果一个字符串“abc”存在于常量池中,而当前Java程序中没有任何一个对象叫“abc”,即没有任何一个String对象引用常量池中的“abc”常量,则表示这是一个废弃的常量。

对于无用的类的判定则需要通过以下条件判断。

  1. 该类的所有实例都已经被回收。
  2. 加载该类的ClassLoader已经被回收。
  3. 该类对应的java.lang.Class对象没有在任何地方被引用,即无法通过反射访问该类的方法。

如何回收内存?

对于垃圾收集算法的实现有标记-清除算法复制算法标记-整理算法等,这里主要介绍这三种算法。

标记-清除算法

标记-清除算法分为两个阶段,分别是“标记”和“清除”阶段。首先标记出所有需要被回收的对象,在标记完成后统一回收所有被标记的对象。是否被标记就看对象是否能到达GC Roots。

缺点:

  1. 效率问题。标记以及清除两个过程的效率都不高。
  2. 内存碎片。在标记清除后内存中会存在大量的内存碎片。

复制算法

复制算法通过将内存区分为两个大小相等的区域。每次只使用其中的一块,当这块内存即将耗尽时,JVM将程序暂停,开始GC线程执行复制算法。将这块即将耗尽内存中存活的对象复制到另一块内存中,并且严格的按照内存地址排列。最后将已使用过的内存一次性清理掉,这样又留出一半的内存空间等待下次复制使用。

以上是执行复制算法之前的内存,可以看出只使用了左边的一块内存,右边的内存用于下一次内存复制。当执行复制之后的结果如下所示

复制完成之后,左边内存中的所有对象被一次性清理,并且存活的对象被复制到右边的内存中按照内存空间整齐的排列着。

缺点:

  1. 一次性只能使用一半的内存,比较奢侈。
  2. 如果当前内存中对象的存活率十分高,那么意味着对象的复制操作也比较多,效率将会变低。

标记-整理算法

标记-整理算法跟标记-清除算法类型,只不过标记对象后不是直接对可回收的对象进行清理,而是让所有存活的对象向一端移动,然后清除掉死去的对象。

算法的使用

如今商业虚拟机的垃圾收集器都采用“分代收集”算法。这种算法就是针对不同情况的对象使用不同的算法。例如针对新生代的对象,由于其总是会有大量的对象死去,只有少部分存活,那么就使用复制算法。而对于那些老年代的对象因为其存活率高,就使用标记-清除算法或标记-整理算法。

垃圾收集器的内存回收机制相关推荐

  1. JVM学习四:垃圾收集器与内存回收策略

    一.经典垃圾收集器 如果垃圾收集算法是内存回收的方法论,那么垃圾收集器就是内存回收的实践者.<Java虚拟机规范>对于垃圾收集器的实现没有任何规定. 这里介绍的经典垃圾收集器," ...

  2. 《深入理解Java虚拟机》-----第3章 垃圾收集器与内存分配策略

    Java与C++之间有一堵由内存动态分配和垃圾收集技术所围成的"高墙",墙外面的人想进去,墙里面的人却想出来. 3.1 概述 说起垃圾收集(Garbage Collection,G ...

  3. 第三章 垃圾收集器与内存分配策略

    第三章 垃圾收集器与内存分配策略 前言: 3.1 概述 3.2 对象已死? 3.2.1 引用计数算法 3.2.2 可达性分析算法 3.2.3 再谈引用,四种引用类型 3.2.4 生存还是死亡 3.3 ...

  4. 读书笔记——Java虚拟机垃圾收集器与内存分配策略

    本文章已授权微信公众号郭霖(guolin_blog)转载. 本文章讲解的内容是Java虚拟机垃圾收集器与内存分配策略. 概述 说起垃圾收集(Garbage Collection),也就是GC,大部分人 ...

  5. 深入理解Java虚拟机——垃圾收集器与内存分配策略

    文章目录 对象已死? 引用计数算法 可达性分析算法 再谈引用 强引用: 软引用: 弱引用: 虚引用: 回收方法区 垃圾收集算法 标记 - 清除算法 缺点: 标记 - 复制算法 标记 - 整理算法 分代 ...

  6. java内存分配和垃圾回收_深入理解java虚拟机(二)垃圾收集器与内存分配策略...

    垃圾收集器与内存分配策略 垃圾收集,三个步骤 什么时候收集,收集那些,怎么收集 1.收集那些 我们会将一些不使用的对象进行收集,进行回收内存空间,我们怎么知道呢 1.引用计数法 如果这个实例被其他地方 ...

  7. 《深入理解JAVA虚拟机》学习日志----一、自动内存管理机制(2.垃圾收集器与内存分配策略)

    二.垃圾收集器与内存分配策略 前言:讨论的区域集中在Java堆和方法区中,而其他几个区域的内存分配和回收都具备确定性,所以不需过多考虑回收 的问题,因为方法结束或者线程结束时,内存自然就跟随着回收了. ...

  8. 七种垃圾收集器和垃圾回收、分代收集、GCROOTS相关概念、GC如何判断一个对象可以被回收

    文章目录 垃圾收集器概述 垃圾回收算法 1)标记-清除算法(Mark-Sweep)(DVM 使用的算法) 2)复制算法(Copying) 3)标记-整理算法(Mark-Compact) 4)分代收集( ...

  9. jvm(3)-垃圾收集器与内存分配策略

    [0]README 0.1)本文部分文字转自:深入理解jvm,旨在学习 垃圾收集器与内存分配策略 的基础知识: [1]垃圾回收概述 1)GC(Garbage Collection)需要完成的3件事情: ...

最新文章

  1. 上海交大张拳石:神经网络的可解释性,从经验主义到数学建模
  2. web.py搭建服务器
  3. linux fedro版本查看命令,Fedora查看内核及发行版本号
  4. (转)Oracle 临时表用法
  5. 常用公有云接入——腾讯
  6. Agc019_F Yes or No
  7. CSS Hide(隐藏元素)
  8. Python+django建站入门篇:Hello world
  9. linux box 信息发布,使用Instantbox快速搭建一个开箱即用的Web端临时Linux系统
  10. UNIX 环境高级编程(四)—— dirent.h
  11. Windows手动添加开机启动项
  12. 推荐 7 款免费开源的 BBS 论坛软件
  13. 资产证券化为什么需要区块链技术?专访趣链科技揭开“区块链+ABS”迷雾
  14. 自动泊车之AVM环视系统算法框架
  15. Mac OS X 10.8.5升级到更高版本的方法
  16. ubuntu 18.04+GTX30系列显卡+TensorFlow-gpu1.15
  17. 这次把怎么做好一个PPT讲清-动画篇
  18. TypeScript实现小游戏---贪吃蛇(超详细)
  19. 设计原则:里式替换原则(LSP)
  20. 什么是python的内置函数_什么是python内置函数

热门文章

  1. Electron系列教程——第一篇:入门
  2. [置顶] 【稀饭】react native 实战系列教程之热更新原理分析与实现
  3. Ps高手之路基础篇视频教程
  4. wpf 语音通话_WPF+WCF一步一步打造音频聊天室(二):文字聊天和白板共享
  5. php 快速读取文件夹下文件列表
  6. CMD运行命令下ping一个网段下的所有ip
  7. swf文件格式解析入门(文件头解析)
  8. DELMIA软件:机器人仿真动画视频生成功能介绍与使用方法
  9. windows server2012中创建密码重设盘
  10. 串口调试工具xshell的配置