在之前Java 运行期数据区一文中,介绍了运行时内存的各个部分。其中程序计数器、虚拟机栈、本地方法栈都随线程消亡,所以,这几个区域的内存分配和回收都具备确定性。而 Java 堆和方法区不同,我们只有在程序运行期间才能知道会创建哪些对象,这部分的内存分配和回收都是动态的,这也正是垃圾回收器关注的部分。

对象的生与死

垃圾回收器要在进行回收前,需要确定哪些对象的状态,是“存活”还是”死亡“。

引用计数法

给对象添加一个引用计数,每当有一个地方引用对象时,引用计数加 1 ;当引用实现时,引用计数减 1 ;引用计数位 0 的对象不能再背引用。

C++ 中的 unique_ptr 内部就是通过引用计数来实现的,不过该方法有个弊端,对象间可能存在循环引用。Java 没有采取改方法。

可达性分析(Reachability Analysis)算法

Java、C# 都通过该方法来判定对象是否存活。可达性分析算法的思路是通过一系列称为“GC Roots”的对象作为起始点,从这些点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到 GC Roots 没有任何引用相连(从 GC Roots 到这个对象不可达)时,则证明这个对象是不可用的。



在 Java 中,以下几种对象可以作为 GC Roots :

  1. 虚拟机栈(栈帧中的本地变量表)中引用的对象;
  2. 方法区中类静态属性引用的对象;
  3. 方法区中常量引用的对象;
  4. 本地方法栈中 JNI(Native 方法) 引用的对象。

方法区的回收

Java 运行期数据区一文中,我们介绍过,方法区是各个线程共享的内存区域,用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。

永久代的垃圾收集主要回收两部分内容:废弃常量和无用的类。回收废弃常量与回收 Java 堆中的对象非常类似。而判定一个类是否是“无用的类”的条件则相对严苛。类需要同时满足3个条件才能算是“无用的类”:

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

虚拟机可以堆满足上述 3 个条件的无用类进行回收,注意,跟对象不一样,不是“无用就一定会回收”。

垃圾回收算法

标记-清除(Mark-Swipe)算法

算法氛围“标记”和“清除”两个阶段:首先标记处所有需要回收的对象,在标记完成后统一回收所有被标记的对象。该算法主要有两个不足:一是效率问题,笔记和清除两个过程的效率够不高;二是空间问题,标记清除之后会产生大量不连续的内存碎片,空间碎片太多可能会导致以后在程序运行过程中需要分配较大对象时,无法找到足够的连续内存而不得不提前出发另一次垃圾收集动作。

复制(Copying)算法

为了解决效率的问题,“复制“算法出现了,该算法将可用内存按容量氛围大小相等的两块,每次只使用其中的一块。当这一块内存用完了,就将存活着的对象复制到另一块上面,然后再把已使用过的内存空间一次清理掉。这样使得每次都是对整个半区进行回收,内存分配时久不用考虑内存碎片等复杂情况,只要一动堆顶置针,按顺序分配即可,实现简单,运行高效。该算法的缺点是内存被缩小位原来的一半。

IBM 的研究表明,新生代中的对象 98% 是“朝生夕死”的,所以并不需要按照 1:1 的比例来划分内存空间,而是将内存分为一块较大的 Eden 空间和两块较小的 Survivor 空间,每次使用 Eden 和其中一块 Survivor 。当回收时,将 Eden 和 Survivor 中还存活着的对象一次性地复制到另外一块 Survivor 空间上,最后清理掉 Eden 和 刚才用过的 Survivor 空间。

当然,我们无法保证一块 Survivor 的空间足够容纳所有的存活对象,所以需要依赖其他内存(老年代)进行分配担保(Handle Promotion)。分配担保的内容后边会介绍到。

标记-整理(Mark-Compact)算法

根据老年代的特点,产生了“标记-整理”算法,标记过程与“标记-清除”算法一样,但后续步骤不是直接对可回收对象进行清理,二是让所有存活的对象都向一段移动,然后直接清理掉端边界以外的内存。

分代收集(Generational Collection)算法

算法思想是:根据对象存活周期的不同将内存划分为几块。一般是把堆分为新生代和老年代,这样就可以根据各个年代的特点采用最合适的收集算法。

在新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,那就选用复制算法,只需要付出少量存活对象的复制成本就可以完成收集。而老年代因为对象存活率高、没有额外空间对它进行分配担保,就必须使用“标记-清理”或者“标记-整理”算法来进行回收。

Java 垃圾回收算法的更多相关文章

  1. 深入理解java垃圾回收算法

    Java虚拟机的内存区域中,程序计数器.虚拟机栈和本地方法栈三个区域是线程私有的,随线程生而生,随线程灭而灭:栈中的栈帧随着方法的进入和退出而进行入栈和出栈操作,每个栈帧中分配多少内存基本上是在类结构 ...

  2. Java垃圾回收算法和内存分配策略

    垃圾回收算法和内存分配策略 Java垃圾回收 垃圾收集,也就是GC并不是Java的伴生物,而对于GC的所需要完成任务主要就是: 1.哪些内存是需要回收的? 2.何时去回收这些内存? 3.以何种方式去回 ...

  3. java垃圾回收算法和垃圾收集器

    垃圾收集算法.垃圾回收算法.java垃圾收集器 目录1. 垃圾收集算法1)引用计数法2)根搜索法2. 垃圾回收算法1)复制算法2)标记-清除算法3)标记-整理算法4)分代收集算法3. java垃圾收集 ...

  4. Java垃圾回收算法和垃圾回收器

    基本上 jvm内存回收有三种 基本算法 标记-清除 标记清除的算法最简单,主要是标记出来需要回收的对象,然后然后把这些对象在内存的信息清除.如何标记需要回收的对象,在上一篇文章里面已经有说明. 标记- ...

  5. 一个来自红帽的Java垃圾回收算法,试图把停顿时间降到10毫秒以下 原创 2017-01-10 薛命灯

    转自 微信聊聊架构 GC... 早在三年前,Red Hat就启动了Shenandoah项目.Shenandoah是一种新的Java虚拟机GC算法,目标是利用现代多核CPU的优势,减少大堆内存在GC方面 ...

  6. java垃圾回收算法

    1.标记-清除 2.标记-复制 3.标记-整理 4.分代混合算法

  7. JAVA虚拟机垃圾回收算法原理

    除了释放不再被引用的对象外,垃圾收集器还要处理堆碎块.新的对象分配了空间,不再被引用的对象被释放,所以堆内存的空闲位置介于活动的对象之间.请求分配新对象时可能不得不增大堆空间的大小,虽然可以使用的总空 ...

  8. 深入理解java虚拟机【垃圾回收算法】

    Java虚拟机的内存区域中,程序计数器.虚拟机栈和本地方法栈三个区域是线程私有的,随线程生而生,随线程灭而灭:栈中的栈帧随着方法的进入和退出而进行入栈和出栈操作,每个栈帧中分配多少内存基本上是在类结构 ...

  9. (转)《深入理解java虚拟机》学习笔记3——垃圾回收算法

    Java虚拟机的内存区域中,程序计数器.虚拟机栈和本地方法栈三个区域是线程私有的,随线程生而生,随线程灭而灭:栈中的栈帧随着方法的进入和退出而进行入栈和出栈操作,每个栈帧中分配多少内存基本上是在类结构 ...

随机推荐

  1. 1.Google Chrome浏览器 控制台全解析

    Google Chrome浏览器 控制台全解析 在Google Chrome浏览器出来之前,我一直使用FireFox,因为FireFox的插件非常丰富,更因为FireFox有强大的Firebug,对于 ...

  2. const vector<int> 和 vector<const int>问题讨论

    1.const vector <int> vec(10) —— 与const int a[10]是一回事,意思是vec只有10个元素,不能增加了,里面的元素也是不能变化的 vector&l ...

  3. PVM的安装和编译PVM程序

    最近刚开始学习并发编程,学习到了PVM这一块.关于在linux系统中PVM的安装,真是要我的命,繁琐死了,最关键是我对linux也是刚开始学,还在继续学习<鸟哥的linux私房菜>一书.好 ...

  4. BootStrap dateRangePicker时间范围控件

    BootStrap dateRangePicker时间范围控件 1 安装引用 1.1 下载zip Github:https://github.com/dangrossman/bootstrap-dat ...

  5. html的基本标记符号

    文本标记:<h1><h2><h3><h4><h5><h6>: 段落标记:<p>: 空格:&nbsp: 换行: ...

  6. 基于REM的移动端响应式适配方案

    视口 在前一段时间,我曾经写过一篇关于viewport的文章.最近由于在接触移动端开发,对viewport有了新的理解.于是,打算重新写一篇文章,介绍移动端视口的相关概念. 关于这篇文章说到的所有知识 ...

  7. Luogu 1060 开心的金明 / NOIP 2006 (动态规划)

    Luogu 1060 开心的金明 / NOIP 2006 (动态规划) Description 金明今天很开心,家里购置的新房就要领钥匙了,新房里有一间他自己专用的很宽敞的房间.更让他高兴的是,妈妈昨 ...

  8. java Script 用if else 实现从大到小指定输出,升序排列

    我只是一个小白 各位大神看到不要介意 var a = Number(prompt("请输入你需要排列的第一个数字")) var b = Number(prompt("请输 ...

  9. js模块加载详解

    看着java中各种import加载,在回过头来看看javascript还在自己造轮子,写各种XX的模块加载框架,ECMASCRIPT6不知什么时候能够普及.不过DT归DT,该学的还是要学. 一 同步加 ...

  10. 51nod_1240:莫比乌斯函数

    题目链接 面向题意编程.. #include<bits/stdc++.h> using namespace std; typedef long long LL; int cal(int n ...