这篇文章主要从如下几个方面介绍垃圾收集的相关知识java
1、判断对象是否已死面试
2、主流垃圾收集算法算法
3、内存分配与回收策略数组
本章节主要从如下几个思考点着手介绍垃圾回收的相关知识:哪些内存须要回收?何时回收?如何回收?这也是经典的学习一个知识点的3h方法:what? when? how?jvm
上一个章节已经介绍jvm运行时数据区的内存分布,垃圾回收主要发生在堆这个区,也就是众多对象实例呆着的地方学习
1、如何判断对象已死?设计
相信面试太高级java的工程师确定遇到过面试官这样的问题:两个对象之间互相循环引用,如何回收?换句话说如何判断这两个对象已死?对象
这里就引出了一个经典算法:引用计数法。是这样设计的:给对象添加一个引用计数器,每当这个对象被引用时,计数器值+1,引用失效时,计数值-1,任什么时候刻,计数为0blog
的对象就是不可能再被使用的对象内存
回到上面的例子,它很难解决互相引用的对象这种状况,因而致使GC没法回收他们
因而出现了另外一个经典算法:可达性分析算法,基本思路是经过一系列的称为"GC Roots"的对象做为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链,
当一个对象到GC Roots没有任何引用链相连,或者称从GC Roots到这个对象不可达时,则证实这个对象是不可用的
做为GC Roots的对象通常有如下几种:虚拟机栈中引用的对象,方法区中类静态属性引用的对象,方法区中常量引用的对象,本地方法栈中JNI引用的对象(这一种不太熟悉)
关于java对引用的概念细分为强引用,软引用,弱引用,虚引用的细节,能够阅读该书了解,在此再也不介绍
2、几种常见的垃圾收集算法
一、Mark-Sweep
标记-清除算法,分为标记,清除两个阶段,首先标记出全部须要回收的对象,标记完成后统一回收
不足:效率不高;回收后产生大量不连续的内存碎片。碎片太多的话,须要分配大对象时,没法找到足够的连续内存而不得不提早触发新一轮的垃圾收集动做
二、Copying
复制算法,大多商业虚拟机都采用这种收集算法回收新生代,具体的在第三点内存分配会讲到
三、Mark-Compact
标记-整理算法。通常使用于老年代
四、Generational Collection
分代收集算法。当前商业虚拟机的垃圾收集都采用分代收集算法,通常是把Java堆分为新生代和老年代,根据各自特色采用最适当的收集算法。
3、内存分配与回收策略
Java的自动内存管理机制能够归结为解决了两个问题:给对象分配内存以及回收分配给对象的内存。Java的内存分配策略并非绝对的或者固定的,这取决于
当前使用的垃圾收集器组合,以及虚拟机中与内存分配相关的参数设置,接下来说的是最为广泛的内存分配规则
(1)对象优先在Eden区分配
新生代区域通常被分为较大的Eden空间和两块较小的Survivor空间(一般称为From和To),HotSpot虚拟机默认Edon和两个Survivor的大小比例是8:1:1,
新建立的对象通常会在Edon和From中,当Edon区没有足够的空间进行分配时,将触发一次Minor GC,前面讲过大多数对象是朝生夕死的,所以Minor GC很是频繁
当一次Minor GC事后,仍然存活的对象会一次性复制到To区域中,而后清理掉Edon和From;这时候注意,From和To将交换角色,如今新的To是清理后的From
所以To区域总能保证每次Minor GC后留有必定的空间容纳尚存活的对象
(2)长期存活的对象将进入老年代
虚拟机给每一个对象都定义了一个对象年龄(Age)计数器,在Edon出生的对象通过第一次Minor GC后仍然存活,并能在Survivor容纳的话Age将设为1,在Survivor区
每熬过一次Minor GC,Age+1,当Age达到设置的参数值-XX:MaxTenuringThreshold(默认值15),将晋升老年代,关于晋升老年代的条件并不是必定要达到这种状况,
java虚拟机有动态对象年龄断定策略,具体可阅读本书3.6.4细节
(3)大对象直接进入老年代
所谓大对象指的是须要大量连续内存空间的对象,最典型的如很长的字符串以及数组,常常出现大对象意味着极可能内存中还有很多空间时就得提早触发垃圾收集
以获取足够的连续空间来安置他们。
(4)触发Full GC的条件
Full GC的速度通常比Minor GC慢10倍以上,触发一次Full GC常常会伴随一次Minor GC,一种触发条件为,一次Minor GC发生后将要晋升为老年代的对象大小超过
老年代现有剩余空间大小,这种情形不难想象。java的老年代空间分配担保细节可细读3.6.5节知晓,此处再也不细说
最后上图一张做为结尾,一目了然: