前言

上篇文章我们一起对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对象实例。

如下图:

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

变量的存活时间

现在我们思考一下会发现,这个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将内存区分为两个区域:年轻代和老年代

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

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

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

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

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

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

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

新生代的垃圾回收

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

那么问题来了,是没有引用后就会立即发生垃圾回收,回收掉没有被引用的对象实例吗?

其实不是这样的,垃圾回收是有触发条件的。

有一个比较常见的场景是这样的,假设我们的代码中创建了大量的对象,导致堆内存中囤积了大量的对象,然后这些对象现在都没有人引用了。

这个时候,如果新生代预先分配的内存空间被占满了,那么我们的代码此时要新创建一个对象的时候,发现新生代空间满了,怎么办?

这个时候就会触发一次新生代的垃圾回收,也称为“Minor GC”或"Young GC",它会尝试把新生代中没有人引用的对象给回收掉,释放空间。

下图表达了这一过程:

长期存活的对象什么时候进入老年代

接下来我们谈论一个话题,静态变量引用的长期存活的对象是什么时候进入老年代的。

上文我们了解到,新生代的对象会经历一次次的垃圾回收,而被静态变量引用的对象因为一直被引用,所以一直不会被回收,所以此时jvm就有了一条规定。

如果新生代中的对象,在经历了15次垃圾回收后,依然坚挺的存活着,那就证明它是个"老年人"了,然后它会被转移到老年代中。

老年代就是存放这些年龄比较大的对象的。

那么老年代中的对象会被垃圾回收吗?

答案是肯定的,因为老年代里的对象随着代码的运行,也是可以不再被任何人引用的,就需要垃圾回收了。

或者说,随着越来越多的对象进入老年代,老年代的内存也会被占满,所以一定是要对老年代进行垃圾回收的。

我们暂时不用考虑具体是怎么回收的,这个内容在之后的文章中我们会有详细的解析。

总结

今天就给大家准备了这么多内容,可能有些小伙伴觉得还没看够,这些内容都比较简单,我已经会了,有没有更深入的东西呢?

别急,学习是循序渐进的事情,王子是想要用最简单的大白话来和小伙伴们一起讨论jvm的原理的,同时也想找一些案例来和大家一起探讨,印象会更深刻。

所以今天小伙伴们了解到这里就可以了,让我们在后续的文章中不见不散,深入讨论些更深层的内容吧。

往期文章推荐:

大白话谈JVM的类加载机制

JVM内存模型不再是秘密

轻松理解JVM的分代模型的更多相关文章

  1. JVM系列-分代收集垃圾回收

    Java自动垃圾回收(Automatic Garbage Collection)是自动回收堆上不再使用的内存,new的对象在程序中没有引用指向它,就会被回收.回收的实现很多,有Reference Co ...

  2. Java虚拟机:JVM内存分代策略

    版权声明:本文为博主原创文章,转载请注明出处,欢迎交流学习! Java虚拟机根据对象存活的周期不同,把堆内存划分为几块,一般分为新生代.老年代和永久代(对HotSpot虚拟机而言),这就是JVM的内存 ...

  3. JVM的分代思想

    Java虚拟机根据对象存活的周期不同,把堆内存划分为几块,一般分为新生代.老年代和永久代(对HotSpot虚拟机而言),这就是JVM的内存分代策略. 永久代是HotSpot虚拟机特有的概念,它采用永久 ...

  4. 转载:JVM内存分代策略

    Java虚拟机根据对象存活的周期不同,把堆内存划分为几块,一般分为新生代.老年代和永久代(对HotSpot虚拟机而言),这就是JVM的内存分代策略. 为什么要分代? 堆内存是虚拟机管理的内存中最大的一 ...

  5. 深入理解JVM(6)——Java内存模型和线程

    Java虚拟机规范中定义了Java内存模型(Java Memory Model,JMM)用来屏蔽掉各种硬件和操作系统的内存访问差异,以实现让Java程序在各种平台下都能达到一致的内存访问效果(“即Ja ...

  6. 理解JVM之java内存模型

    java虚拟机规范中试图定义一种java内存模型(JMM)来屏蔽掉各种硬件和操作系统内存访问差异,以实现让java程序在各种平台都能打到一致的内存访问效果.所以java内存模型的主要目标是定义程序中各 ...

  7. JVM调优-Java垃圾回收之分代回收

    为什么要进行分代回收? JVM使用分代回收测试,是因为:不同的对象,生命周期是不一样的.因此不同生命周期的对象采用不同的收集方式. 可以提高垃圾回收的效率. Java程序运行过程中,会产生大量的对象, ...

  8. JVM | 分代垃圾回收策略的基本概念以及过程

    一.为什么要分代 分代的垃圾回收策略,是基于这样一个事实:不同的对象的生命周期是不一样的.因此,不同生命周期的对象可以采取不同的收集方式,以便提高回收效率. 在Java程序运行的过程中,会产生大量的对 ...

  9. 深入理解JVM虚拟机

    JVM平台上还可以运行其他语言,运行的是Class字节码.只要能翻译成Class的语言就OK了.挺强大的. JVM厂商很多 垃圾收集器.收集算法 JVM检测工具 关于类的加载: Java代码中,类型( ...

随机推荐

  1. Photon PUN 三 RPCs & RaiseEvent

    官方文档地址 https://doc.photonengine.com/en-us/pun/current/manuals-and-demos/rpcsandraiseevent 一, RPC   P ...

  2. Black & White(尺取)

    链接:https://ac.nowcoder.com/acm/contest/893/F来源:牛客网 * 第一行一个整数 T ,表示接下来有 T 个样例.* 首先输入n,m,表示S串的长度n和操作次数 ...

  3. 小程序开发-使用npm包

    微信小程序引用npm包 微信小程序官方支持使用npm包,地址为 https://developers.weixin.qq.com/miniprogram/dev/devtools/npm.html 实 ...

  4. iptables实用知识 ,一文学会配置linux防火墙

    目录 1.防火墙的概念 2. linux防火墙 3.linux数据包处理流程 3.1 linux 防火墙将以上流程,固定区分为5个流程节点 3.2 数据流程 4 linux防火墙的实现机制 4.1 i ...

  5. AI研讨会直播:《人工智能开发前沿》实战系列公开课第1期

    报名链接:https://www.slidestalk.com/m/276 活动背景 业务需求.数据.算法.算力等因素,决定人工智能技术走向产业落地面临各种挑战.博客园联合示说网以及产业内人工智能技术 ...

  6. odoo10甘特图gantt view

    odoo10中的gantt图示例 1.Gantt属性说明 甘特图视图的根元素是<gantt />,它没有子节点但可以采用以下属性: date_start (required) 提供每条记录 ...

  7. path.resolve和path.join的区别

    // test.js const path = require('path') let x1 = path.resolve('/目录1/目录2', '/目录3/目录4/') let x2 = path ...

  8. HTML页面的基本信息

    1.python中生成的html页面,每一段的基本解释,以及header中的应用 2.body中的应用 2.1.a href链接点击baidu直接跳转百度网址,如果需要重新打开一个页面,详情看2.16 ...

  9. Linux下find与exec的联手干大事

    在Linux下工作,find命令绝对是一个非常高频的命令.我们可以用find命令来找到符合某些关键词的文件,找到某些日期的文件,也可以设定一些正则表达式,找到一系列满足该条件的文件. 但是,如果只有一 ...

  10. Linux常用的三种软件安装方式

    一:Linux源码安装    1.解压源码包文件    源码包通常会使用tar工具归档然后使用gunzip或bzip2进行压缩,后缀格式会分别为.tar.gz与.tar.bz2,分别的解压方式:   ...