参考《深入理解JVM》 。以及张颜渊、徐无忌老师的视频

垃圾回收的历史远比Java久远,在1960年诞生于麻省理工学院的Lisp是第一门使用内存动态分配和垃圾回收技术的语言,其作者就思考过垃圾回收需要完成的三件事情:

  •  哪些内存需要回收
  • 什么时候回收
  • 如何回收

这三个问题也是我们学习JVM垃圾回收机制需要回答的问题

 

一)垃圾回收器在对堆内存进行垃圾回收时,需要判定哪些对象已经“死去”,即需要判断对哪些对象进行回收?

1、引用计数法

在对象中添加一个引用计数器,每当对象被引用时,加1,当对象被释放时-1;任何时候当计数器为0时,代表表对象失效

优点:原理简单,判定效率高

缺点:很难解决对象之间相互引用的问题

(即循环引用,而没有其他对象对其进行引用的时候,对象不会被回收)

如下图:对象A、对象B循环引用,如果没有其他对象引用对象A和对象B时,对象A和对象B不会被回收。

 

JVM -- 垃圾回收GC

注:由于引用计数法的确定,主流的Java虚拟机都没有选用引用技术法来管理内存

 

2、可达性法分析法

通过一系列的"GC Roots"的根对象作为起始结点集,根据引用关系从上往下所搜索,搜索过程过程所走过的路径被称为“引用链”。如果某个对象到GC Roots之间没有任何引用链相连,代表该对象不可达。代表对象可以被回收

 

JVM -- 垃圾回收GC

上图是利用可达性分析法判断对象是否可以被回收

在上图中:object5、object6、object7、表示对象是可以被回收的

 

Java中可以作为GC Roots的对象有:

  • 虚拟机栈(栈帧的中局部变量表)中的引用对象
  • 方法区中的静态属性引用对象
  • 方法区中常量引用对象
  • 本地方法栈中JNI(即一般说的是Native方法)的引用对象
  • 所有被同步锁(synchronized关键字)所持有的对象

 

二)、如何回收?即如何进行垃圾的收集

分代收集理论

当前的商业虚拟机的垃圾收集器,大多数都遵循了“分代收集”(Generation Collectionl)的理论进行设计,

分代收集理论建立在两个分代假说之上:

1)、弱分代假说:绝大多数对象都是朝生夕灭的

2)、强分代假说:熬过多次越多次垃圾收集的过程的对象就越难以消亡。

根据这个两个分代假说共同奠定了多款常用垃圾回收器的一致设计原则:收集器应该将Java堆划分出了不同的的区域,然后将回收对象依据年龄(年龄即对象熬过垃圾收集过程的次数)划分不同的区域之中存储

 

所以在大多数商业Java虚拟机中,设计者一般会把Java堆划分为新生代(Young Generation)和老年代(old Generation)两个区域

 

对象不是孤立的,对象之间可能存在跨代引用,即老年代中的对象可以引用新生代中的对象,如果使用可达性分析法则会给内存回收带来很多额外的负担。

为了解决上述问题,分代收集理论添加了第三条经验法则:

3)、跨代引用假说:跨代引用对于同代引用来说仅占极少数。对于跨代引用的两个对象而言,是应该倾向于同时生存和同时消亡。示例:如果某个新生代对象引用了老年代对象,由于老年代难以消亡,该引用使得新生代对象在垃圾回收的时候得以存活,进而在年龄增长之后晋升为老年代,跨代引用也随之消亡

 

根据跨代引用的假说,只需要在新生代建议一个全局的数据结构(该结构被成为“记忆集”),这个结构把老年代划分若干个小块,标识出老年代的那块内存在跨代引用。在Minor GC时只有包含了跨代引用的小块内存中的对象才被添加到GC Roots当中进行扫描

 

垃圾收集的分类::

1)部分收集(Partial GC):指目标不是完整收集整个Java堆的垃圾收集,其中可分为:

  • 新生代收集(Minor GC/Young GC):新生代中的垃圾收集
  • 老年代收集(Major GC/old GC):老年代垃中的圾收集,目前只有CMS收集器会有单独收集老年代的行为
  • 混合收集(Mixed GC):指目标收集整个新生代以及部分老年代的垃圾收集,目前只用G1收集器会有这种行为

2)整堆收集(Full GC):收集整个Java堆和方法区的垃圾收集

 

垃圾回收算法

1、标记-清除

在GC的时候,先进行扫描,把需要清理的对象进行标记,然后将这些对象直接清除

JVM -- 垃圾回收GC

缺点:1、执行效率不稳定,会随着对象数量的增加,标记和清除的执行效率会降低

2、产生内存碎片。如上图,如果清理了两个1KB的对象,在添加一个2KB对象时,无法放入那两个被清理的对象内存中,需要重新分配内存空间

 

2、标记-整理

在标记-清理的基础上,将内存空间进行整理,使空间紧凑排列,解决内存碎片化问题。

JVM -- 垃圾回收GC

缺点:开销比较大

 

3、标记-复制

将空间一份为二,在清理时,将需要保留的的对象复制到第二块区域上,复制的时候紧凑排列,然后清空原来的区域

JVM -- 垃圾回收GC

缺点:浪费空间

 

三)、什么时候回收?

在详述该问题的时候,先阐述一下堆内存的划分。

JVM根据对象存活时间的长短,将堆内存划分为新生代老年代。其所占内存比例是新生代占1/3。老年代占2/3

JVM -- 垃圾回收GC

新生代由三个部分组成:Eden+(S0+S1)Survivor。其所占比例是8:1:1

JVM -- 垃圾回收GC

何时触发Minor GC?

当Eden区满的时候会触发依次Minor GC。

 

何时触发FULL GC?

当没有足够大的连续空间存储对象的时候会触发一次Full GC。

当触发Minor GC后,新生代中的对象晋升老年代,导致老年代空间不足会触发一次Full GC

 

Full Gc的特点:

老年代对象比较稳定,Full GC 不会频繁执行,Full GC清理的是整个堆内存空间

Full GC会导致Stop The Word

大部分情况下,对象优先分配到Eden区,如果对象太大,新生代放不下,会直接放到老年代中

 

Minor GC详解:

1、当Eden区内存已满时,会触发依次Minor GC,将需要的数据保存到S0当中

JVM -- 垃圾回收GC

2、下一次Eden满了之后,进行Minor GC。会将原来s0和和需要保存的Eden区中的数据数据复制到S1

 

JVM -- 垃圾回收GC

 

相关文章: