JVM - 分代收集(都在堆内部) + Minor GC + Full GC

总结

JVM中的堆:

  • jdk1.7之前:一般分为三大部分:新生代、老年代、永久代
  • jdk1.8起:一般分为两大部分:新生代、老年代(永久代已经移除,1.8后用元空间代替,并移出堆)

 

GC算法:JVM - 垃圾回收算法

对象object在堆heap的分配过程

1.Java新对象的会在新生代的Eden区分配内存(但如果新创建的对象占用内存很大,则直接分配到老年代)

2.当新生代的Eden区内存不够的时候,就会触发MinorGC(复制算法),进行一次垃圾回收

  • Eden区,依然存活的对象,copy到ServivorTo区
  • ServivorFrom区,垃圾回收后依然存活的对象,也会copy到ServivorTo区

3.同时把这些对象的年龄+1。如果达到了老年的标准,一般是15,则复制到老年代区

4.当老年代的空间不足时,会触发MajorGC/FullGC (采用“标记—清除算法”+“标记--整理算法” 混合: 标记清除几次之后,再做一次标记整理)

5.当老年代也满了装不下的时候,就会抛出OOM(Out of Memory)异常

 

 

新生代默认的空间占比总空间的 1/3,老年代的默认占比是 2/3。

新生代使用的是复制算法,新生代里有 3 个分区:Eden、To Survivor、From Survivor,它们的默认占比是 8:1:1,它的执行流程如下:

  • 把 Eden + From Survivor 存活的对象放入 To Survivor 区;
  • 清空 Eden 和 From Survivor 分区;
  • From Survivor 和 To Survivor 分区交换,From Survivor 变 To Survivor,To Survivor 变 From Survivor。

每次在 From Survivor 到 To Survivor 移动时都存活的对象,年龄就 +1,当年龄到达 15(默认配置是 15)时,升级为老生代。大对象也会直接进入老生代。

老生代当空间占用到达某个值之后就会触发全局垃圾收回,一般使用标记-整理的执行算法。以上这些循环往复就构成了整个分代垃圾回收的整体执行流程。

 

新生代(Young Generation)

新生代又分为 Eden区、ServivorFrom、ServivorTo三个区。

  • Eden区:Java新对象的出生地(如果新创建的对象占用内存很大,则直接分配到老年代)。当Eden区内存不够的时候就会触发MinorGC,对新生代区进行一次垃圾回收。
  • ServivorTo:保留了一次MinorGC过程中的幸存者。
  • ServivorFrom:上一次GC的幸存者,作为这一次GC的被扫描者。

HotSpot虚拟机默认Eden和Survivor的大小比例是8:1:1

当JVM无法为新建对象分配内存空间的时候(Eden满了),Minor GC被触发。因此新生代空间占用率越高,Minor GC越频繁。

MinorGC(只发生在新生代)的过程:采用复制算法。 

 

 

老年代(Old Generation)

老年代的对象比较稳定,所以MajorGC不会频繁执行。
在进行MajorGC前一般都先进行了一次MinorGC,使得有新生代的对象晋身入老年代,导致空间不够用时才触发。当无法找到足够大的连续空间分配给新创建的较大对象时也会提前触发一次MajorGC进行垃圾回收腾出空间。

 

永久代(Permanent Generation)

指内存的永久保存区域,主要存放Class和Meta(元数据)的信息。
Class在被加载的时候被放入永久区域。它和和存放实例的区域不同,GC不会在主程序运行期对永久区域进行清理。所以这也导致了永久代的区域会随着加载的Class的增多而胀满,最终抛出OOM异常。


在Java8中,永久代已经被移除,被一个称为“元数据区”(元空间)的区域所取代
元空间的本质和永久代类似,都是对JVM规范中方法区的实现。不过元空间与永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存。因此,默认情况下,元空间的大小仅受本地内存限制。类的元数据放入 native memory, 字符串池和类的静态变量放入java堆中. 这样可以加载多少类的元数据就不再由MaxPermSize控制, 而由系统的实际可用空间来控制。

 

Minor GC

1.每次MinorGC后,Eden区域就会清空
2.对于From和To,谁为空,谁就是To
3.MinorGC采用复制算法

 

MinorGC(只发生在新生代)的过程:采用复制算法
1. 首先,把Eden和ServivorFrom区域中存活的对象复制到ServicorTo区域(如果有对象的年龄以及达到了老年的标准,一般是15,则赋值到老年代区)
2. 同时把这些对象的年龄+1(如果ServicorTo不够位置了就放到老年区)
3. 然后,清空Eden和ServicorFrom中的对象;最后,ServicorTo和ServicorFrom互换,原ServicorTo成为下一次GC时的ServicorFrom区。

 

Full GC

大部分发生在老年代,也会在永久代(方法区)。是否在新生代发生得看具体的垃圾收集器
MajorGC(Full GC)采用“标记—清除算法”+“标记--整理算法” 混合(标记清除几次之后,再做一次标记整理)
1. 首先扫描一次所有老年代,标记出存活的对象
2. 然后回收没有标记的对象。
MajorGC的耗时比较长,因为要扫描再回收。MajorGC会产生内存碎片(cuixunxu:不一定准备,需要看老年代采用的是什么垃圾收集器),为了减少内存损耗,我们一般需要进行合并或者标记出来方便下次直接分配。
当老年代也满了装不下的时候,就会抛出OOM(Out of Memory)异常。

 

posted on 2020-12-16 00:29  frank_cui  阅读(131)  评论(0编辑  收藏  举报

导航

levels of contents