所有分类
  • 所有分类
  • 未分类

JVM-内存模型/垃圾回收流程

简介

本文介绍Java各个代的关系(内存模型)及垃圾收集流程。

内存模型

JDK8的内存模型

在Java中所有的垃圾收集问题几乎都是针对堆内存空间完成的,要想充分理解垃圾的收集流程,必须首先掌握Java堆内存的内存模型成。如下图所示:

内存模型的变化

JDK1.8以前提共用永久代,而从JDK1.8后永久代被替换为元空间(MetaSpace)。

在JDK1.8之前,HotSpot都在努力改变永久代的存储位置,例如,在JDK1.6时提供有永久代,到了JDK1.7时又将永久代的部分操作移交给了堆内存,而在JDK1.8时使用元空间代替了永久代。如下图所示:

可以发现,在JDK1.8之前都会提供有永久代,此部分内存是不受GC控制的。在最初的设计中,都将方法区保存在了永久代,所以一旦方法执行中出现了内存不足的情况,将会抛出:“OutOfMemoryError:PermGen space”错误。同时Oracle也在考虑将HotSpot与JRockit(此虚拟机不存在永久代)两个虚拟机合二为一,所以此内存空间被元空间所替代。

垃圾收集流程

在整个Java內存模型中,主要有3块内存区:年轻代(Young)、老年代(Tenured)、元空间(MetaSpace),同时还会有几块动态调整的内存伸缩区(当几个内存区空间不足时动态扩充)。而JVM的内存回收就是对这几块空间的回收处理操作,对于内存分配与GC的执行流程如图3所示。

上图中的垃圾回收主要是针对年轻代(Eden+Survivor)与老年代(Tenured)完成的。

具体流程如下:

  1. 当使用关键字new创建一个新对象时,JVM会将新对象保存在Eden区,此时需要判断Eden区是否有空余空间。
    1. 如果没有,则会执行“MinorGC(年轻代GC)”。
    2. 如果有,则直接将新对象保存在Eden区之内;
  2. 执行完“MinorGC”后会清除不活跃的对象,从而释放Eden区的内存空间,随后对Eden空间再次判断。
    1. 如果此时Eden区的空间依然不足,则会将部分活跃对象保存在Survivor区。
    2. 如果此时剩余空间可以直接容纳新对象,则会直接为新对象申请内存空间;
  3. 由于Survivor区也有对象会存储在内,所以在保存Eden区发送来的对象前首先需要判断其空间是否充足。
    1. 如果Suivivor有足够的空余空间,则直接保存Eden区晋升的对象。此时Eden区空间得到释放,随后可以在Eden区为新的对象申请内存空间。
    2. 如果Survivor区空间不足,则需要将Survivor区的部分活跃对象保存到Tenured区。
  4. Tenured区如果有足够的内存空间,则会将Survivor区发送来的对象进行保存。
    1. 如果此时Tenured区的内存空间也已经满了,则将执行“FullGC”(完全GC或称为MajorGC,其包括年轻代和老年代,相当于使用“Runtime.getRuntime().gc()”处理)以释放年轻代和老年代中保存的不活跃对象。
    2. 如果在释放后有足够的内存空间,Survivor将保存Eden发送来的对象,这样就可以在Eden区内有足够的内存保存新的对象。
  5. 此时,如果老年代的内存区也己经被占满,则会抛出“OutOfMemoryError(OOM错误)”,程序将中断运行。

可见,Java在每次创建对象时如果发现内存不足都会自动向其他区域延伸。为了提高性能,在实际应用中可能会开辟尽量大的内存空间,以实现更加合理的GC控制。

14

评论3

请先

  1. 膜拜大佬
    是你呀! 2024-04-09 0
  2. 则直接将新对象保存在Edeti区之内 Edeti 是打错字了吗?
    2024-04-04 0
    • 是打错了,已修复。
      自学精灵 2024-04-04 2
显示验证码
没有账号?注册  忘记密码?

社交账号快速登录