详解JVM的分代模型

前言

上篇文章我们一起对jvm的内存模型有了比较清晰的认识,小伙伴们可以参考JVM内存模型不再是秘密这篇文章做一个复习。

本篇文章我们将针对jvm堆内存的分代模型做一个详细的解析,和大家一起轻松理解jvm的分代模型。

相信看过其他文章的小伙伴们可能都知道,jvm的分代模型包括:年轻代、老年代、永久代。

那么它们分别代表着什么角色呢?我们先来看一段代码

public class Main {
  public static void main(String[] args) {
    while (true){
      load();
    }
  }

  public static void load(){
    SysUser sysUser = new SysUser();
    sysUser.setAvatar("1");
  }

}

这段代码本身没有什么特殊的含义,主要是理解jvm的运行机制。

首先一旦执行main()方法,就会把main()方法的栈帧压入main线程的虚拟机栈,然后调用load()方法后,又会把load()方法的栈帧压入虚拟机栈。

接着在执行load()方法时,会在java堆内存中创建一个SysUser对象实例,而栈帧中会有sysUser局部变量引用堆内存中的SysUser对象实例。

如下图:

详解JVM的分代模型

到这里上篇文章都讲解过,相信大家都能看懂。

变量的存活时间

现在我们思考一下会发现,这个SysUser对象实际上属于一个短暂存活的对象,因为在load()方法执行完毕后,load()方法的栈帧就会出栈。

而一旦出栈,就没有了sysUser这个局部变量来引用SysUser这个对象的实例。

所以,其实这个SysUser对象已经没有用了,但是它还在占用着堆内存的空间,那么对于这种没有引用的对象实例jvm是如何处理的呢?

这就要说到jvm的垃圾回收机制了,jvm本身是有垃圾回收机制的,它是一个后台线程,会把没有人引用的SysUser对象实例给回收掉,不断的释放内存空间。

所以这个SysUser对象实例是一个存活时间很短的对象,可能在执行load()方法的时候被创建出来,执行之后就被垃圾回收掉了。

而这种对象在我们平时的开发中是很常见的,占绝大多数比例。

现在我们将上边的代码改造一下:

public class Main {
  private static SysUser sysUser = new SysUser();
  public static void main(String[] args) {
    while (true){
      load();
    }
  }

  public static void load(){
    sysUser.setAvatar("1");
  }
}

其实就是把局部变量sysUser变成了静态变量,这样修改后,sysUser不在作为局部变量保存在栈中,而是和class类文件一起保存在方法区中,这样SysUser对象实例就会一直被这个静态变量引用,所以不会被垃圾回收,一直保存在堆内存中。如下图:

详解JVM的分代模型

分代模型

接下来我们进入核心内容,就是jvm的分代模型了。

上文中我们发现,根据我们的编码方式的不同,采用不同的方式创建和使用对象,对象的存活时间是不同的。

所以jvm将内存区分为两个区域:年轻代和老年代。

年轻代就是我们的第一种局部变量的示例,创建和使用完毕后会被垃圾回收掉。

老年代就是第二种静态变量的示例,创建后需要长期在堆内存中存活。

相信到这里大家就应该理解了什么样的对象是短期存活的对象,什么样的对象是长期存活的对象,那么它们是如何分别存在年轻代和老年代中的呢?为什么要这么区分呢?

其实这与垃圾回收机制是密不可分的。

对于年轻代里的对象,他们的特点是创建后很快就会被回收,而对于老年代里的对象,他们的特点是需要长期存活,所以这两种对象是不能用一种垃圾回收算法进行回收的,所以需要区分成两个。

对于长期存在的静态变量sysUser,其实刚开始的时候也是在年轻代的,那它是什么时候进入老年代的呢?我们下文会讲解这个问题。

那永久代又是什么呢?其实永久代就是我们说的jvm的方法区,用于存放一下类信息的,这部分之后的文章涉及到会详解,现在理解到这就可以了。

新生代的垃圾回收

前文我们了解了,当load方法执行完毕出栈后,里面的局部变量sysUser就没了,堆内存中的SysUser对象就没有引用了,所以会被垃圾回收掉。

详解JVM的分代模型

扫一扫手机访问