最近发生了一些C#程序运行时的一些问题,发现是GC导致的问题,然后稍微研究了一下GC,因为知道Java的GC要比.NET稍微复杂一点,所以我觉得要是能弄懂Java的GC的原理,对.NET的GC的理解也能更深入一些。所以把研究到的整理做了个笔记,以免以后忘记。

什么样的对象会被GC判定要回收的对象:
     主流JVM采用可达性分析算法来判断一个对象是否需要回收。基本思想是通过称为“GC Roots”的对象作为起始点,从这些节点向下搜索,搜索走过的路径称为引用链,当一个对象不与任何引用链相连的时候,说明此对象是不可用的。
     引用计数算法也是用来判断一个对象是否应该被GC回收的一个算法。基本思想是给对象添加一个引用计数器,每当有地方引用它时,计数器加1,引用失效则减1.在任何时刻计数器为0的对象就是不可用对象。优点:实现简单,判定效率高。缺点:不能解决对象之间循环引用的问题。比如objA.instance = objB;objB.instance=objA;此时双方计数都不为0,则无法通知GC去回收他们。所以此方法并没有被主流JVM所采用。
 
可达性分析中,如何检查和处理其中包含的引用:
     首先需要了解检查过程中的问题:
  • 现在很多引用光方法区就会有数百兆,如果要逐一检查其中的引用,必然会花费大量时间。
  • 分析过程中,会出现GC停顿,如果在分析过程中引用关系不断变化,那么分析结果的准确性得不到保证,所以必须GC过程中必须要停顿所有Java执行线程,既Stop The World。
     为了解决上述问题,主流JVM采用准确式GC:采用一组称为OopMap的数据结构达到这个目的,在类加载完成、JIT编译过程中,会在特定位置记录下栈和寄存器中哪些是引用。
     但是还有一个问题,除了上述情况之外,很多指令都有可能改变引用关系,如果为每个指令都生成相应的OopMap,那会需要大量额外空间,所以JVM采用SafePoint来生成OopMap。SafePoint是按照“是否具有让程序长时间执行的特征”为标准进行选定的,明显特征是指令序列的复用,如方法调用,循环跳转,异常跳转等,除此之外,在类加载完成、JIT编译过程中记录下栈和寄存器中哪些是引用的地方也会生成SafePoint。
     对于SafePoint,如何让所有线程都跑到最近的SafePoint是一个需要考虑的问题。对于这个问题,采用的是主动式中断。中心思想是当GC需要中断线程时,不直接对线程进行操作,而是设置一个标志,每个线程执行时主动轮询这个标志,当发现中断标志为真时就在运行到SafePoint时自己中断挂起。
     有一种情况,有些线程本身就没有被分配CPU时间,如Sleep或Blocked状态,这样的线程无法轮询标志,也无法执行到SafePoint上自行中断。对于这种情况,JVM视此线程为安全区域,是指在这段代码片段中,引用关系不会发生变化,在这个区域中任意地方开始GC都是安全的。
    
 
引用的生命周期:
  • 强引用:代码中普遍存在的,类似Object obj = new Object();只要强引用还在,被引用的对象就不会被回收。
  • 软引用:有用但是非必须的引用,只有在内存将要发生内存溢出异常之前,才会被列进回收范围中,如果回收之后依     然内存不足,才会抛出内存溢出异常。
  • 弱引用:非必须对象,比软引用更弱一些,只能生存到下次GC之前,无论内存是否足够,GC工作时都会回收。
  • 虚引用:无法通过虚引用获得对象实例,唯一作用是在GC时收到系统通知。

被判定要被回收的对象,在哪些情况下可以自救(不被回收):
     当一个对象没有覆盖finalize()方法,或者finalize()已经被调用过的话,GC都会将其回收。
当不满足上述条件时,被GC判定要回收的对象会放入F-Queue的队列中,当一个对象在finalize()方法中重新和GC Roots建立连接,就可以达到自救的目的。
     注意:如果该对象在finalize()方法中实现自救,在下次被判定要被回收的时候,因为之前finalize()被调用过,所以此次回收finalize()不会被调用,故无论怎样,都会被GC回收。finalize()方法最初是为了满足C++程序员设计的方法,此方法运行代价高,不确定性大,所以在开发过程中避免使用此方法。
 
在方法区中,什么条件下会被GC回收:
  • 该类所有的实例都被回收,Java堆中不存在该类实例。
  • 加载该类的ClassLoader已经被回收。
  • 该类对应的java.lang.Class 对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。
 
堆中在被判定要回收的对象后,如何进行回收:
HotSpot采用的是分代收集算法来完成回收后的内存分配问题。基本思想是:将堆分为4部分,1个Eden空间、2个Survivor空间(From,To)和1个老年代空间。默认Eden:Survivor = 8:1.
 
 
步骤:
  1. 开始状态,对象会存在与Eden和From的空间中,在GC发生时,通过可达性分析算法判断要回收的对象,并按照回收规则进行回收,在Eden中存活的对象会放入To中,From中存活的对象的年龄加1,放入To,此时Eden和From空间是被清空的。
  2. To和From倒置,原来的To变为From,From变为To。运行一段时间后,Eden区和From区又新添加了一些对象,GC发生时,按照上述规则将Eden区存活的对象放入To中,From中存活的年龄加1放入To中。
  3. 以此反复,当From中年龄加1后到达一个阈值(年龄阈值,可以通过-XX:MaxTenuringThreshold来设置)时,将此对象放入老年代中。
  4. 当To区被填满时,无论年龄如何,都会清空To区,将其中所有的对象放入老年代中。
 
老年代中的对象通过什么方式进行回收:
     因为老年代中对象存活率高,没有额外空间进行分配担保,所以老年代使用“标记-清理”或者“标记-整理”算法进行回收。
     标记-清理:对堆中需要回收的对象进行标记,在标记完成后统一回收要回收的对象。效率不高,会产生碎片。
     标记-整理:在标记清理后,对堆进行一次压缩,让对象连续存于内存中。
 
回收采用的收集器都有哪些,各有什么特点:
 

Java GC随笔的更多相关文章

  1. java GC是何时对什么东西做什么事情

    之前学习了javaGC的原理机制,有了一定的了解,现在做一个整理总结,便于理解记忆,包括三个问题: 1. java GC是什么时候做的? 2. java GC作用的东西是什么? 3. java GC具 ...

  2. 详解Java GC的工作原理+Minor GC、FullGC

    详解Java GC的工作原理+Minor GC.FullGC 引用地址:http://www.blogjava.net/ldwblog/archive/2013/07/24/401919.html J ...

  3. Java GC回收机制

    优秀Java程序员必须了解的GC工作原理 一个优秀的Java程序员必须了解GC的工作原理.如何优化GC的性能.如何与GC进行有限的交互,因为有一些应用程序对性能要求较高,例如嵌入式系统.实时系统等,只 ...

  4. JAVA GC 简单总结

    GC分代 GC的英文全拼是Garbage Collection,意思是垃圾收集. Java 将堆内存分为三代来管理: - 年轻代 (Young Generation) - 年老代 (Old Gener ...

  5. Java GC收集器配置说明

    根据Java GC收集器具体分类,我们可以看出JVM根据需求不同提供了三种选择:串行收集器.并行收集器.并发收集器. 串行收集器只适用于小数据量的情况,我们主要了解一下并行收集器和并发收集器.默认情况 ...

  6. java gc的工作原理、如何优化GC的性能、如何和GC进行有效的交互

    java gc的工作原理.如何优化GC的性能.如何和GC进行有效的交互 一个优秀的Java 程序员必须了解GC 的工作原理.如何优化GC的性能.如何和GC进行有效的交互,因为有一些应用程序对性能要求较 ...

  7. Java GC系列(4):垃圾回收监视和分析

    本文由 ImportNew - lomoxy 翻译自 javapapers. 目录 垃圾回收介绍 垃圾回收是如何工作的? 垃圾回收的类别 垃圾回收监视和分析 在这个Java GC系列教程中,让我们学习 ...

  8. 应用JConsole学习Java GC

    应用JConsole学习Java GC 关于Java GC的知识,好多地方都讲了很多,今天我用JConsole来学习一下Java GC的原理. GC原理 在我的上一篇中介绍了Java运行时数据区,在了 ...

  9. 成为Java GC专家(3)—如何优化Java垃圾回收机制

    为什么需要优化GC 或者说的更确切一些,对于基于Java的服务,是否有必要优化GC?应该说,对于所有的基于Java的服务,并不总是需要进行GC优化,但前提是所运行的基于Java的系统,包含了如下参数或 ...

随机推荐

  1. 使用webdriverwait封装查找元素方法

    对于selenium原生的查找元素方法进行封装,在timeout规定时间内循环查找页面上有没有某个元素 这样封装的好处: 1.可以有效提高查找元素的效率,避免元素还没加载完就抛异常 2.相对于time ...

  2. mySubmit.js

    function mySubmit(theForm,url,result){ function default_callback(res){ result.html(res.info); if(res ...

  3. onSaveInstanceState

    我们已经分析过Activity的启动流程,从中也分析了Activity的生命周期.而其中有一个生命周期方法:onSaveInstanceState方法,今天我们主要讲解一下onSaveInstance ...

  4. TClientDataSet数据源设置

    TClientDataSet数据源设置   TClientDataSet数据源设置

  5. MyBatis笔记——EhCache二级缓存

    介绍 ehcache是一个分布式缓存框架. 我们系统为了提高系统并发,性能.一般对系统进行分布式部署(集群部署方式)  不使用分布缓存,缓存的数据在各各服务单独存储,不方便系统开发.所以要使用分布式缓 ...

  6. div img span 垂直居中问题

    div设置height,line-height影响块元素内的文本垂直居中,块下的a,img,span等行内元素是没能垂直居中,用了vertical-align:middle;可以处理div等块级元素下 ...

  7. 【教程】AI画放射图

    第一步:画矩形作图宇宙键shift 第二步:分为网格 第三步:直接选择工具 第四步:填充交叉色,这步不再敖述: 第五步:视图--轮廓:快捷键ctrl+y; 第六步:直接选择工具选择除边框以外的所有节点 ...

  8. Sass之二(进阶篇)

    源码链接:http://pan.baidu.com/s/1o8M51hC 1. 数据类型 1.1 Number 数字类型,小数类型,带有像素单位的数字类型,全部都属于Number类型 Number类型 ...

  9. lsof,fuser,xargs,print0,cut,paste,cat,tac,rev,exec,{},双引号,单引号,‘(字符串中执行命令)

    cut用来从文本文件或标准输出中抽取数据列或者域,然后再用paste可以将这些数据粘贴起来形成相关文件. 粘贴两个不同来源的数据时,首先需将其分类,并确保两个文件行数相同.paste将按行将不同文件行 ...

  10. ui-router 视图嵌套时指定二级视图显示默认页面

    当跳转到user页面时,右边的uiview是为空的,要点击了左侧的导航才能插入模板,如何在路由中设置二级视图的默认显示页面呢? app.config(function($stateProvider, ...