一、判断对象存活的算法

1、引用计数(Reference Counting)算法

  给对象添加一个引用计数器,每当有一个地方引用时,计数器加1。当引用失效时,计数器减1。当计数器的值为0的时候说该对象不可能再被使用。引用计数器算法的实现简单,效率高,比如微软的COM(Component Object Model)技术,Python等。但是Java虚拟机没有使用该技术,因为该技术难以解决对象循环依赖的问题,即A中有B,B中有A这种情况。

2、可达性分析(Reachability Anlysis)算法

  通过一系列称为"GC Roots"的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到"GC Roots"没有任何引用链相连是,说明该对象不可用了,即该对象被判定为可回收对象。java中可作为GC Roots的对象:

    1) 虚拟机栈(栈帧中的本地变量表)中引用的对象。

    2) 方法区中类静态属性引用的对象。

    3) 方法区中常量引用的对象。

    4) 本地方法栈中JNI(一般指Native方法)引用的对象。

3、Java的几种引用

  主要指4种引用:强引用(Strong Reference)、软引用(Soft Reference)、弱引用(Weak Reference)、虚引用(Phantom Reference),这四种引用强度依次逐渐减弱。

    1)强引用:代码中普遍存在,如Object obj = new Object()这类引用,只要引用还存在,垃圾收集器永远不会回收掉被引用的对象。

    2)软引用:描述一些有用但并非必须的对象。软引用关联的对象,在系统内存溢出之前,将会把这些对象列入回收范围进行第二次垃圾回收。如果这次回收还没有足够的内存,则抛出内存溢出异常。使用SoftReference类来实现。

    3)弱引用:用来描述非必须的对象。弱引用关联的对象,只能生存到下一次垃圾回收之前。下一次垃圾回收的时候,无论当前内存是否足够,都会回收掉被弱引用关联的对象。使用WeakReference类来实现。

    4)虚引用:一个对象是否有虚引用存在,完全不会对其生存时间造成影响。也无法通过一个虚引用来取的对象实例。为一个对象设置虚引用关联,唯一的目的就是在这个对象在被收集器回收时收到一个系通知。使用PhantomReference类来实现。

4、Java对象的生存或者死亡

  在可达性分析中不可达的对象也并非"非死不可",要真正判断一个对象是否死亡,至少要经过两个阶段:第一个阶段是对象没有和GC Roots相连的引用链。那该对象会被第一次标记并进行第一次筛选,筛选条件为该对象有没有必要执行finalize()方法。当没有覆盖finalize()方法,或者finalize()方法已经被调用过,则判定为没有必要执行。

  如果判定有必要执行finalize()方法,则虚拟机会将其放到一个F-Queue的队列中,并在稍后创建一个低优先级的Finalizer去执行它。这里的"执行"指的是触发这个方法,但不承诺等待该方法运行结束。这是为了防止线程一直等待,可能导致虚拟机奔溃。finalize()方法是对象逃脱死亡的最后一次机会,稍后GC会对F-Queue中的对象进行第二次小规模标记。如果对象在此时成功的拯救自己,即把自己和GC Roots引用链连接上。则第二次标记时它将被移出"即将回收集合",如果对象这时候还没有逃脱,基本上就真的被回收了。注:任何对象的finalize()方法都只会被系统调用一次。

5、回收方法区

  Java虚拟机规范说过可以不需要对方法区进行垃圾回收。在方法区中进行垃圾回收性价比比较低,在新生代中一次垃圾回收可以回收70%~95%的空间,而方法区的效率就远低于此了。永久代的回收主要分为两方面:废弃常量(JDK 1.7之前)和无用类。回收废弃常量和堆中的类似,只要没有和GC roots相连则可回收。而判定一个类是否是无用类的条件就比较苛刻了。需要满足三点:

  1)该类所有实例都已被回收,即Java堆中不存在该类的任何实例。

  2)加载该类的ClassLoader已经被回收。

  3)该类对应的java.lang.Class类没有在任何地方被引用,无法在任何地方通过反射再访问该类的方法。

  是否对类进行回收,HotSpot虚拟机提供了-Xnoclassgc参数进行控制,还可以使用-verbose:class和-XX:+TraceClassLoading、-XX:TraceClassUnLoading查看类的加载和卸载信息。其中-verbose:class和-XX:+TraceClassLoading可以在Product版中的虚拟机中使用,-XX:TraceClassUnLoading需要在FastDebug版本的虚拟机才支持。

  在大量使用动态代理、反射、CGLib等大量使用ByteCode的框架,动态生成JSP以及OSGI这个频繁自定义ClassLoader的场景都需要虚拟机具备类卸载的功能,以保证永久代不会溢出。

二、垃圾收集算法:

1、标记-清除(Mark_Sweep)

  该算法分为"标记"和"清除"两个阶段:首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记对象。该算法是最基础的算法,之所以说是最基础的算法是因为后续的算法都是基于该算法的不足之处进行改进得到的。这个算法有两个不足点:

  1) 效率问题,标记和清除两个阶段效率都不高。

  2) 空间问题,标记清除后会产生大量不连续的内存碎片,内存碎片太多可能会导致以后程序运行中,需要分配大对象时无法找到足够的连续内存空间,从而不得不提前触发另一次垃圾回收。

2、复制算法(Copying)

  为了解决效率问题该算法出现了,它将可用内存按容量划分为等量的两块,每次只使用一块。当这块内存使用完了之后,就将存货的对象复制到另一块上,然后把使用过的这块内存一次清理掉。这样使得每次回收都是对半块内存进行回收,就不用考虑内存碎片的问题了。只要移动堆顶的指针,顺序分配内存就可以了,实现简单,运行高效。但是缺点就是造成了空间的浪费,一半的闲置内存代价太高。

  现在的商业虚拟机都采用这种方式来回收新生代,经研究表明新生代的对象98%都是"朝生夕死",所以并不需要按照1:1的比例来划分。而是将内存划分为一块较大的Eden区和两块较小的Survivor区,每次只是用Eden区和一块Survivor区,当进行垃圾回收的时候,将Eden区和正在是用的Survivor区中存活的对象一次性拷贝到另一块Survivor区中,最后再清理掉Eden区和已使用过的Survivor区。HotSpot默认的Eden区和Survivor区的比例是8:1,即每次新生代中可用内存为(80%+10%),也就是说只有10%会被"浪费"。当然98%的对象可回收是一般场景下的数据,我们没法保证每次都只有不多于10%的对象存活,当Survivor不够时,我们需要依靠别的内存(这里指的是老年代)来担保(Handle Promotion)。当另一块Survivor没法存放上一次回收时的存活对象时,这些对象会直接通过分配担保进入老年代。

3、 标记-整理(Mark-Compact)

  复制算法在对象存活率较高的情况下要进行较多的复制,效率比较低下。如果不想浪费50%的空间,就需要额外的空间进行分配担保,以应对被使用的内存中所有对象都100%存货的极端情况,所以老年代都不使用这种算法。根据老年代的特征,有人提出了"标记-整理"算法,该算法和"标记-清除"算法一样,但后续步骤不是直接对可回收对象进行清理,而是先将所有存活对象移动到一端,然后清理掉端边境以外的内存区域。

4、 分代收集算法(Generational Collection)

  当前商业虚拟机都是采用的这种算法,这种算法只是根据对象的存活周期将内存划分为多块。一般是把虚拟机划分为新生代和老年代,这样就可以根据各个年代的特点采用合适的手机算法了。在新生代中,每次回收都会有大量的对象死去,少量存活,这样就可以采用复制算法了,只需要付出少量对象复制的代价就可以完成垃圾回收了。而在老年代中对象存活率高、没有额外的空间对它进行分配担保,即必须使用"标记-清除"或"标记-整理"算法了。

Java虚拟机垃圾收集器的更多相关文章

  1. Java虚拟机垃圾收集器与内存分配策略

    Java虚拟机垃圾收集器与内存分配策略 概述 那些内存须要回收,什么时候回收.怎样回收是GC须要完毕的3件事情. 程序计数器.虚拟机栈与本地方法栈这三个区域都是线程私有的,内存的分配与回收都具有确定性 ...

  2. 深入理解java虚拟机【Java虚拟机垃圾收集器】

    Java堆内存被划分为新生代和年老代两部分,新生代主要使用复制和标记-清除垃圾回收算法,年老代主要使用标记-整理垃圾回收算法,因此java虚拟中针对新生代和年老代分别提供了多种不同的垃圾收集器,JDK ...

  3. (转)《深入理解java虚拟机》学习笔记4——Java虚拟机垃圾收集器

    Java堆内存被划分为新生代和年老代两部分,新生代主要使用复制和标记-清除垃圾回收算法,年老代主要使用标记-整理垃圾回收算法,因此java虚拟中针对新生代和年老代分别提供了多种不同的垃圾收集器,JDK ...

  4. 深入理解java虚拟机----->垃圾收集器与内存分配策略(下)

    1.  前言 内存分配与回收策略 JVM堆的结构分析(新生代.老年代.永久代) 对象优先在Eden分配 大对象直接进入老年代 长期存活的对象将进入老年代 动态对象年龄判定 空间分配担保  2.  垃圾 ...

  5. Java虚拟机-垃圾收集器

    垃圾收集器(Garbage Collection, GC)的诞生引导出了三个问题: 哪些内存需要回收? 什么时候回收? 如何回收? 对于线程独占的三个区域(程序计数器.虚拟机栈.本地方法栈)不用过多的 ...

  6. Java虚拟机—垃圾收集器(整理版)

    1.概述 如果说收集算法是内存回收的方法论,那么垃圾收集器就是内存回收的具体实现.Java虚拟机规范中对垃圾收集器应该如何实现并没有规定,因此不同的厂商.不同版本的虚拟机所提供的垃圾收集器都可能会有很 ...

  7. Java虚拟机--垃圾收集器和内存分配

    垃圾收集器和内存分配 程序计数器.虚拟机栈.本地方法栈这三个区域和线程的生命周期一致,所以方法结束或者线程结束时,内存自然就跟着回收了.Java堆和方法区,只有在程序处于运行期间才能知道会创建哪些对象 ...

  8. 深入理解JAVA虚拟机 垃圾收集器和内存分配策略

    引用计数算法 很多教科书判断对象是否存活的算法是这样的:给对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加1:当引用失效时,计数器值就减1:任何时刻计数器都为0的对象就是不可能再被使用的 ...

  9. Java虚拟机 垃圾收集器与内存分配策略

    说起GC,我们要思考的主要有三件事 哪些内存需要回收 那些已经“死去”的对象,那么哪些对象“死”,哪些对象“活”呢,有个简单的办法 引用计数法,但是没法解决循环依赖问题 所以Java虚拟机采用的是可达 ...

随机推荐

  1. Python相关机器学习‘武器库’

    开始学习Python,之后渐渐成为我学习工作中的第一辅助脚本语言,虽然开发语言是Java,但平时的很多文本数据处理任务都交给了Python.这些年来,接触和使用了很多Python工具包,特别是在文本处 ...

  2. application.properties多环境配置文件、jar包外部配置文件、配置项加密、程序中配置使用

    一.简介 spring boot项目application.properties文件存放及使用介绍 二.方法一多环境配置文件 我们一般都会有多个应用环境,开发环境.测试环境.生产环境,各个环境的配置会 ...

  3. Android为TV端助力 内存溢出与内存泄露

    内存溢出就是软件运行需要的内存,超出了java虚拟机给他分配的可用的最大内存 内存泄露就是在缓存图片文字等等的时候,没有关闭流所导致的内存泄露

  4. Orchard详解--第一篇 介绍

    Orchard是一个开源的内容管理系统(CMS),它提供了简单的向导式的安装方法,用于快速建站(如WordPress).对于.Net的开发者来说,Orchard有更好的学习价值,所以本系列文章将对Or ...

  5. python之模块使用

    1.入口 """ 模块测试入口 """ import show_message as sm # 导入方式一 sm.show(sm.__nam ...

  6. 前后端分离djangorestframework——权限组件

    权限permissions 权限验证必须要在认证之后验证 权限组件也不用多说,读了源码你就很清楚了,跟认证组件很类似 具体的源码就不展示,自己去读吧,都在这里: 局部权限 设置model表,其中的ty ...

  7. 探索SQL Server元数据(三):索引元数据

    背景 在第一篇中我介绍了如何访问元数据,元数据为什么在数据库里面,以及如何使用元数据.介绍了如何查出各种数据库对象的在数据库里面的名字.第二篇,我选择了触发器的主题,因为它是一个能提供很好例子的数据库 ...

  8. 一套简单的git版本控制代码

    对于博客来说,我还是直接实践比较好,理论过多,不方便以后的查看 废话不多,直接开干 功能需求: .公司需要将jenkins打包出来的压缩包通过git上传到git服务器 .而且通过版本控制上传的文件,即 ...

  9. foreach Transform 同时chils.setParent引起的bug

    Transform继承自IEnumerable,可以对它进行迭代.但当你在迭代的同时,又对child进行setParent操作时,会出现意想不到的结果. 下面是我使用foreach和getchild得 ...

  10. C++实现程序单实例运行的两种方式

    简介 在我们编写程序的时候,经常会注意到的一个问题就是如何能够让程序只运行一个实例,确保不会让同一个程序多次运行,从而产生诸多相同进程,给我们的带来不便呢?那么常用的有以下四种方法,第一种方法是通过扫 ...