垃圾回收 II
我们在上一篇博客中已经对JVM运行时内存的各个区域分析了他们是否需要垃圾回收。JVM之垃圾回收 I .
栈区/PC区: 它两是线程私有内存,和线程是强相关的,线程创建划分内存,线程结束释放,他是有严格的内存划分的,不需要垃圾回收。
常量池+方法区: 由于Java大量框架的使用(反射、动态代理等),而反射代码会造成方法区中的类急速膨胀,经常调用反射会造成方法区中的类特别多,因此方法区的回收会变得更严格。因此方法区也是垃圾回收考虑的一部分。
堆区: 堆是对象的主要区域,创建的对象大多数在堆上,所以堆是GC考虑的重点。
综上所述,堆区和方法区是垃圾回收的主要区域,现在我们来具体看看。
一、方法区(jdk1.7)/元空间(jdk1.8)
首先,我们要知道JDK1.7的方法区在JDK1.8更名叫做元数据区;只是内存划分不同了。
JDK1.7的方法区在jdk1.8被叫做元数据区,并且元空间已经被移至本地内存中, 见下图所示:
在垃圾回收角度:
- JDK1.7的方法区
在GC中
一般称为永久代(Permanent Generation)
。 - JDK1.8的元空间存在于本地内存,
GC也是即对元空间
垃圾回收。
1、永久代和元空间的关系:
首先,我们先要知道永久代只是HotSpot虚拟机在1.7版本的方法区的一个实现方式!永久代是一种实现方式!不等同于方法区!!
我们理清几个概念:
1)HotSpot虚拟机:
- HotSpot虚拟机是Java虚拟机的一种!
2)永久代:
永久代只存在于HotSpot虚拟机,并且只存在于jdk7和之前的版本中
,并不是所有的jvm中都有永久代,IBM的j9,oracle的JRocket都没有永久代。- HotSpot虚拟机在jdk8中已经彻底移除了永久代,jdk8中引入了一个新的内存区域叫元空间(metaspace)。
- JDK1.7的方法区
在GC中
一般称为永久代(Permanent Generation)
。 - JDK1.8的元数据区
在GC中
一般称为元空间(metaspace)
。
- JDK1.7的方法区
- 永久代是
实现层面
的东西。类似于1.8的metaspace。
3)方法区:
- 方法区是规范层面的东西,规定了这一个区域要存放哪些东西
- 方法区是jvm规范里面的
运行时内存区域
的一个组成部分,其他jvm运行时内存区域还包括:栈区、pc区、堆区等 - 方法区在
JDK1.8被叫做元数据区
,并且被移到本地内存中。 - 方法区/元空间里边还包括运行时常量池和类常量池,所以它存储的内容主要有:字面量、符号引用、class等。
重点!!!综上:
- JDK1.7的永久代和JDK1.8的metaspace都是实现层面的东西。
HotSpot虚拟机在1.7版本的方法区是通过永久代来实现的,而HotSpot虚拟机1.8版本的元数据区是metaspace来实现的
。
2、方法区/元空间的垃圾回收内容
方法区中包括运行时常量池区和类常量池区,所以方法区或元空间的垃圾收集主要回收两部分内容:废弃常量和无用的类(主要是针对于类相关的变量/方法,也就是static修饰的
)。此区域进行垃圾收集的“性价比”一般比较低。
但是Java大量框架的使用(反射、动态代理等),而反射代码会造成方法区中的类急速膨胀,经常调用反射会造成方法区中的类特别多,因此方法区的回收会变得更严格。
3、方法区/元空间垃圾回收的方式
此处我们以jdk1.8的元空间为例
HotSpot虚拟机提供了一些参数可以设置垃圾回收:
- “-XX:MaxMetaspaceSize” (JDK8):指定类元数据区的最大内存大小;
- “-XX:MetaspaceSize” (JDK8):指定类元数据区的内存阈值------超过将触发垃圾回收;
二、堆
Java堆是垃圾收集器管理的主要区域,因此很多时候也被称做“GC堆”(Garbage CollectedHeap)。
1、GC堆的划分
从内存回收的角度来看,由于现在收集器基本都采用分代收集算法,所以Java堆中还可以细分为:新生代(Young Generation)、老年代(Old Generation)
1) 新生代(Young Generation):又可以分为Eden空间、Survivor区(From Survivor空间(S0)、To Survivor空间(S1) )
。
- 新生代的垃圾回收又称为
Young GC(YGC)、Minor GC
。 - 指发生在新生代的垃圾收集动作,因为Java对象大多都具备朝生夕灭的特性,
所以Minor GC非常频繁,一般回收速度也比较快
。 - Eden区,对象生成的区域,即:
对象一开始诞生都是在伊甸区的
2) 老年代(Old Generation、Tenured Generation)
- 老年代垃圾回收又称为
Major GC
。 - 指发生在老年代的GC,出现了Major GC,经常会伴随至少一次的MinorGC(但非绝对的,在Parallel Scavenge收集器的收集策略里就有直接进行Major GC的策略选择过程)。
- Major GC的速度一般会比Minor GC慢10倍以上。
Full GC:一般指影响比较大的、暂停用户线程时间比较久的GC。
有可能是老年代GC,也有可能是老年代和新生代一起的GC(整个全堆GC),也有可能是针对全堆和方法区整个区域的全部GC,都有可能的。但是至少一般包括老年代GC。
在不同的语义条件下,Full GC还可能指有用户线程暂停(Stop-The-World)的垃圾回收(如GC日志中)。Full GC一般根据语义来定垃圾回收的内存区域,—般都存在老年代GC。
2、堆区GC回收过程
前置知识:
- 对象的年龄计算:经过一次GC,对象年龄+1
- 对象一开始创建都是在新生代的Eden区
1)Minor GC(新生代GC):
Minor GC发生条件:当创建对象时,如果新生代的Eden区空间不足,就会触发Minor GC(新生代GC)。
Minor GC过程: 发生Minor GC时,是使用复制算法
将Eden区和Survivor区存活对象复制到另一个Survivor区.
Minor GC步骤:
- (1)复制存活对象(Eden区和一块Suvivor区的存活对象)到另一块Suvivor区——>存活对象年龄+1
- (2)清空Eden区和前一块Survivor区
- (3)给需要创建的对象分配内存(在Eden区)
异常情况:如果留空的S区空间不足存放GC后存活对象时,所有存活对象,通过分配担保机制,进入老年代
2)Major GC(老年代GC):
对象进入老年代的条件:
- (1)
大对象
直接进入 - (2)
长期存活的对象
进入老年代——对象年纪>阈值 - (3)Minor GC时,留空S区空间不足存放GC后的存活对象,所有存活对象,进入老年代。
Major GC(老年代GC)发生的条件:
当对象进入老年代空间不足的情况,就会触发Major GC。
3、STW (Stop The World)
1)认识STW
Stop-The-World:
垃圾回收工作是在垃圾回收线程中执行的,在很多情况下,执行垃圾回收工作,或是执行垃圾回收其中某一步骤时,需要暂停用户线程
,也就是Stop-The-World(STW)。
- STW是JVM在后台自动发起和自动完成的。在用户不可见的情况下,把用户正常的工作线程全部停掉。
- STW和哪款GC无关,所有的GC都有这个事件。
- 被STW中断的应用程序线程会在完成GC之后恢复,频繁的STW会让用户感觉像是看电视卡顿一样,所以我们要减少STW的发生
- 但是所有的垃圾回收器都不能不能完全避免Stop一the一world情况发生,只有垃圾回收器越来越优秀,回收效率越来越高,才尽可能地缩短了暂停时间。
2)需要暂停的原因: 用户线程和GC线程并发、并行的执行,标记可回收的对象,在用户线程并发执行时,可能重新有引用指向该对象(已经被标记的对象)。
因此,只有当用户线程暂停,才不会有新的引用指向已经被标记的对象。
3)STW对GC的影响:
用户暂停会对GC有一定的影响,我们来看看对哪些GC产生影响:
- 新生代GC: 都会发生STW——影响:
因为MinorGC时间非常快,几乎忽略不计,影响很小
- 老年代GC︰ 根据阶段来看是否会STW——影响:
非常大。(老年代空间大,需要回收的对象多,耗时也多)
- FullGC: 一般指影响比较大的、暂停用户线程时间比较久的GC;
影响很大(STW)
,所以一般都包含Major GC。可能出现Minor Gc