JAVA复习笔记之GC部分

    前言:垃圾回收机制,大家都知道JAVA的垃圾回收都是JVM自动回收的,不需要程序员去管理。但是我们还是得知道原理才能在适当时机进行JVM调优

  原理:当我们new 一个对象时JVM堆区就会分配一块内存(地址,大小)给这个对象,当这个对象“不可达”的时候(即程序无法访问的时候),GC就需要回收这块空间。

  Java语言规范没有明确地说明JVM使用哪种垃圾回收算法,但是任何一种垃圾回收算法一般要做2件基本的事情:(1)发现无用信息对象;(2)回收被无用对象占用的内存空间,使该空间可被程序再次         使用,

  算法包括:

  1. 引用计数(Reference Counting):给每个对象添加一引用计数器,每当有一个地方引用它的时候,计数器+1;引用失效的时候,计数器-1

  2.可达性分析算法(Rearchability Analysis):通过一系列的“GC roots”的对象作为起点,从这些节点向下搜索,搜索所超过的路径称为引用链,当一个对象到GC roots没有任何引用链相连的时候则此对         象是不可用的,不可用的对象则是JVM判定为可回收的对象。目前Java中可以作为GC ROOT的对象有:

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

                    2、方法区中静态属性引用的对象

                    3、方法区中常亮引用的对象

                    4、本地方法栈中引用的对象(Native对象)

  

  3. 标记-清除(Mark-Sweep):从引用的根节点开始标记所有引用的对象,遍历整个堆内存,把未标记的对象清楚(老年代)

   4. 复制(Copying):把内存空间划为两个相等的区域,每次只使用其中一个区域。垃圾回收时,遍历当前使用区域,把正在使用中的对象复制到另外一个区域中(年轻代)

   5. 标记-整理(Mark-Compact :第一阶段从根节点开始标记所有被引用对象,第二阶段遍历整个堆,把清除未标记对象并且把存活对象 “压缩”到堆的其中一块,按顺序排放(老年代)

  6. 分代(Generational Collecting 

  首先我们得先清除JVM堆内存的结构(如下图),可以看到一共有3个区域:年轻代(Young Generation)、老年代(Old Generation)、永久代(Permanent Generation,也就是方法区)

  

      年轻代(Young Generation)

  包括一个Eden区和2个Survivor 区(From 和 to  ),绝大多数的对象刚创建时都被分配在Eden区(Eden区是连续的内存空间,因此在其上分配内存极快),当第一次Eden区空间满了的时候进行第一            次Minor GC:回收掉“消亡”的对象,存活的对象复制到From内存 中(To内存此时是空的),Eden区域为空;下次Eden满的时候,又会触发Minor GC,回收掉消亡对象,存活的对象还是复制到From内存          中,当From内存满了的时候,GC会把From中还存活的对象复制到To内存中吗,清空From,这就确保了From和To永远至少有一个区是空的;重复以上步骤依然存活的对象会被默认放到老年代中。在年轻          代发生的GC只有Minor GC,发生的频率比较高(不一定是Eden满的时候)

     老年代(Old Generation)

       存放经历N次Minor GC依然存活的对象,当老年代的内存空间满的时候会触发Full GC,Full GC发生频率比较低,老年代对象存活时间比较长,存活率标记高

     永久代(Permanent Generation也就是方法区

     用于存放静态文件,如Java类、方法等。持久代对垃圾回收没有显著影响,但是有些应用可能动态生成或者调用一些class,例如Hibernate 等,在这种时候需要设置一个比较大的持久代空间来存放这些运         行过程中新增的类

  总结的一些常见问题:

  Q:JVM GC回收哪个区域内的垃圾?

   JVM GC只回收堆区和方法区内的对象。而栈区的数据,在超出作用域后会被JVM自动释放掉,所以其不在JVM GC的管理范围内

 Q:JVM GC怎么判断对象可以被回收了?

     对象没有引用

     作用域发生未捕获异常

     程序在作用域正常执行完毕

     程序执行了System.exit()

     程序发生意外终止(被杀线程等)

   PS:上面那个是网上的答案,我个人觉得应该回答从GC root搜索不到,而且经过第一次标记、清理后,仍然没有复活的对象被回收更加合理

 Q:GC策略都有哪些分类?

   引用计数:难解决对象之间相互引用的问题

   可达性分析算法:基本所有GC算法都引用根搜索算法这种概念

   标记-清除 :会有内存碎片

 复制:是对象的存活率非常低

    标记-整理(Mark-Compact)缺点:成本大

   JVM为了优化内存的回收,使用了分代回收的方式,对于新生代内存的回收(Minor GC)主要采用复制算法。而对于老年代的回收(Major GC),大多采用标记-整理算法 

  Q:什么时候一个对象会被GC ?

 把minor GC和Full GC介绍下即可

  Q:如果老年代的对象需要引用新生代的对象,会发生什么呢?

   为了解决这个问题,老年代中存在一个 card table ,它是一个512byte大小的块。所有老年代的对象指向新生代对象的引用都会被记录在这个表中。当针对新生代执行GC的时候,只需要查询 card table 来决     定是否可以被回收,而不用查询整个老年代。这个 card table 由一个write barrier 来管理。write barrier给GC带来了很大的性能提升,虽然由此可能带来一些开销,但完全是值得的。

 总结:其实GC只要了解了三个问题就行 “什么时候”,“对什么东西”,‘“做了什么”

  

posted @ 2018-01-20 23:45  等待九月  阅读(346)  评论(0编辑  收藏  举报