java—JVM

JVM垃圾回收

堆内存

1.7之前:

在这里插入图片描述

1.8

在JDK1.8版本废弃了永久代,替代的是元空间(MetaSpace),元空间与永久代上类似,都是方法区的实现,他们最大区别是:元空间并不在JVM中,而是使用本地内存。

VM垃圾收集算法:

分区收集算法

分区算法将整个堆空间划分为连续的不同小区间, 每个小区间独立使用, 独立回收. 这样做的好处是可以控制一次回收多少个小区间 , 根据目标停顿时间, 每次合理地回收若干个小区间(而不是整个堆), 从而减少一次 GC 所产生的停顿。

分代收集算法

分代收集法是目前大部分 JVM 所采用的方法,其核心思想是根据对象存活的不同生命周期将内存
划分为不同的域
,一般情况下将 GC 堆划分为年老代(Tenured/Old Generation)和年轻代(Young Generation)。年老代的特点是每次垃圾回收时只有少量对象需要被回收,年轻代的特点是每次垃圾回收时都有大量垃圾需要被回收,因此可以根据不同区域选择不同的算法。

年轻代

在这里插入图片描述

Eden 区

Java 新对象的出生地(如果新创建的对象占用内存很大,则直接分配到年老代)。当 Eden 区内存不够的时候就会触发 MinorGC,对新生代区进行一次垃圾回收。

Servivor

  • ServivorFrom:上一次 GC 的幸存者,作为这一次 GC 的被扫描者。
  • ServivorTo:保留了一次 MinorGC 过程中的幸存者。

MinorGC 的过程(复制->清空->互换)

  • 复制:首先,把 Eden 和 ServivorFrom 区域中存活的对象复制到 ServicorTo 区域(如果有对象的年龄>15,则赋值到老年代区),同时把这些对象的年龄+1(如果ServicorTo 不够位置了就放到年老代区);
  • 清空 eden、servicorFrom
  • ServicorTo 和 ServicorFrom 互换:原 ServicorTo 成为下一次 GC 时的 ServicorFrom
    区。

涉及的算法:复制算法

一般按(8:1:1)将新生代划分为一块较大的 Eden 空间和两个较小的 Survivor 空间(From Space, To Space),每次使用Eden 空间和其中的一块 Survivor 空间,当进行回收时,将该两块空间中还存活的对象复制到另一块 Survivor 空间中。

复制算法:

按内存容量将内存划分为等大小的两块。每次只使用其中一块,当这一块内存满后将尚存活的对复制到另一块上去,把已使用的内存清掉,如图:

在这里插入图片描述

缺点:这种算法虽然实现简单,内存效率高,不易产生碎片,但是最大的问题是可用内存被压缩到了原本的一半。且存活对象增多的话,Copying 算法的效率会大大降低。

年老代

主要存放应用程序中生命周期长的内存对象。

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

涉及算法:

  • 标记整理:标记阶段和 Mark-Sweep 算法相同,标记后不是清理对象,而是将存活对象移向内存的一端。然后清除端边界外的对象。如图:

在这里插入图片描述

总结:

1.对象的内存分配主要在新生代的 Eden Space 和 Survivor Space 的 From Space(Survivor 目前存放对象的那一块),少数情况会直接分配到老生代。

2.当新生代的 Eden Space 和 From Space 空间不足时就会发生一次 GC,进行 GC 后,Eden Space 和 From Space 区的存活对象会被挪到 To Space,然后将 Eden Space 和 FromSpace 进行清理。

3.如果 To Space 无法足够存储某个对象,则将这个对象存储到老生代。

4.调换To和From区,为下一次复制做准备。

5.当对象在 Survivor 区躲过一次 GC 后,其年龄就会+1。默认情况下年龄到达 15 的对象会被移到年老代中。

三个GC

  • Minor GC : 清理年轻代
  • Major GC : 清理老年代
  • Full GC : 清理整个堆空间,包括年轻代和永久代所有GC都会停止应用所有线程。

猜你喜欢

转载自blog.csdn.net/qq_40896788/article/details/108431999