上篇文章咱们一块儿对jvm的内存模型有了比较清晰的认识,小伙伴们能够参考JVM内存模型再也不是秘密这篇文章作一个复习。java
本篇文章咱们将针对jvm堆内存的分代模型作一个详细的解析,和你们一块儿轻松理解jvm的分代模型。算法
相信看过其余文章的小伙伴们可能都知道,jvm的分代模型包括:年轻代、老年代、永久代。jvm
那么它们分别表明着什么角色呢?咱们先来看一段代码ide
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对象实例。线程
以下图:3d
到这里上篇文章都讲解过,相信你们都能看懂。对象
如今咱们思考一下会发现,这个SysUser对象实际上属于一个短暂存活的对象,由于在load()方法执行完毕后,load()方法的栈帧就会出栈。blog
而一旦出栈,就没有了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将内存区分为两个区域:年轻代和老年代。
年轻代就是咱们的第一种局部变量的示例,建立和使用完毕后会被垃圾回收掉。
老年代就是第二种静态变量的示例,建立后须要长期在堆内存中存活。
相信到这里你们就应该理解了什么样的对象是短时间存活的对象,什么样的对象是长期存活的对象,那么它们是如何分别存在年轻代和老年代中的呢?为何要这么区分呢?
其实这与垃圾回收机制是密不可分的。
对于年轻代里的对象,他们的特色是建立后很快就会被回收,而对于老年代里的对象,他们的特色是须要长期存活,因此这两种对象是不能用一种垃圾回收算法进行回收的,因此须要区分红两个。
对于长期存在的静态变量sysUser,其实刚开始的时候也是在年轻代的,那它是何时进入老年代的呢?咱们下文会讲解这个问题。
那永久代又是什么呢?其实永久代就是咱们说的jvm的方法区,用于存放一下类信息的,这部分以后的文章涉及到会详解,如今理解到这就能够了。
前文咱们了解了,当load方法执行完毕出栈后,里面的局部变量sysUser就没了,堆内存中的SysUser对象就没有引用了,因此会被垃圾回收掉。
那么问题来了,是没有引用后就会当即发生垃圾回收,回收掉没有被引用的对象实例吗?
其实不是这样的,垃圾回收是有触发条件的。
有一个比较常见的场景是这样的,假设咱们的代码中建立了大量的对象,致使堆内存中囤积了大量的对象,而后这些对象如今都没有人引用了。
这个时候,若是新生代预先分配的内存空间被占满了,那么咱们的代码此时要新建立一个对象的时候,发现新生代空间满了,怎么办?
这个时候就会触发一次新生代的垃圾回收,也称为“Minor GC”或"Young GC",它会尝试把新生代中没有人引用的对象给回收掉,释放空间。
下图表达了这一过程:
接下来咱们谈论一个话题,静态变量引用的长期存活的对象是何时进入老年代的。
上文咱们了解到,新生代的对象会经历一次次的垃圾回收,而被静态变量引用的对象由于一直被引用,因此一直不会被回收,因此此时jvm就有了一条规定。
若是新生代中的对象,在经历了15次垃圾回收后,依然坚挺的存活着,那就证实它是个"老年人"了,而后它会被转移到老年代中。
老年代就是存放这些年龄比较大的对象的。
那么老年代中的对象会被垃圾回收吗?
答案是确定的,由于老年代里的对象随着代码的运行,也是能够再也不被任何人引用的,就须要垃圾回收了。
或者说,随着愈来愈多的对象进入老年代,老年代的内存也会被占满,因此必定是要对老年代进行垃圾回收的。
咱们暂时不用考虑具体是怎么回收的,这个内容在以后的文章中咱们会有详细的解析。
今天就给你们准备了这么多内容,可能有些小伙伴以为还没看够,这些内容都比较简单,我已经会了,有没有更深刻的东西呢?
别急,学习是按部就班的事情,王子是想要用最简单的大白话来和小伙伴们一块儿讨论jvm的原理的,同时也想找一些案例来和你们一块儿探讨,印象会更深入。
因此今天小伙伴们了解到这里就能够了,让咱们在后续的文章中不见不散,深刻讨论些更深层的内容吧。