前言

上篇文章我们一起对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. 深入了解Netty【三】Netty概述

    1.简介 Netty是一个异步事件驱动的网络应用程序框架,用于快速开发可维护的高性能协议服务器和客户端. Netty是一个NIO客户端服务器框架,它支持快速.简单地开发协议服务器和客户端等网络应用程序 ...

  2. openshift 4.3中安装helm3并通过helm方式部署应用

    openshift 4.3中安装helm3并通过helm方式部署应用 简介 Helm是一个命令行界面(CLI)工具,可简化将应用程序和服务部署到OpenShift Container Platform ...

  3. 求支付表中按id累积和最接近100的那条记录

    此例源自美团的一道SQL面试题 支付表结构: create table hy_payment( id number(4,0) primary key, pay number(3,0) not null ...

  4. 使用HttpUrlConnection访问www.163.com遇到503问题,用设置代理加以解决

    一次我使用如下程序连接到网易,意图获取其网站的html文本: try { String urlPath = "http://www.163.com/"; URL url = new ...

  5. 难道主键除了自增就是GUID?支持k8s等分布式场景下的id生成器了解下

    背景 主键(Primary Key),用于唯一标识表中的每一条数据.所以,一个合格的主键的最基本要求应该是唯一性. 那怎么保证唯一呢?相信绝大部分开发者在刚入行的时候选择的都是数据库的自增id,因为这 ...

  6. leetcode刷题-94二叉树的中序遍历

    题目 给定一个二叉树,返回它的中序 遍历. 实现 # def __init__(self, x): # self.val = x # self.left = None # self.right = N ...

  7. nmap端口扫描工具下载和安装使用

    1.下载地址 https://nmap.org/download.html 2.下载之后进行安装 选择I Agree 后,建议全选,特别是zenmap,这个是图形化界面,不喜欢命令行格式的可以用zen ...

  8. Jmeter测试工具

    jmeter的简单应用 目录 jmeter的简单应用 1.Jmeter 的基本概念 2.我们 为什么 使用 Jmeter 3.Jmeter的作用 4.Jmeter怎么用 5.安装JAVA环境 6.Jm ...

  9. 测试软件—禅道BUG管理工具

    入禅 目录 入禅 1.禅道的基本使用 1.禅道的基本使用 admin(管理员) 部门:创建部门(需求部门,开发部门,测试部门,项目部门,产品部门) 组织:创建用户(产品经理,项目经理,开发人员,测试人 ...

  10. sql中的join

    首先准备数据 有以下数据,三张表:role(角色表).hero(英雄表).skill(技能表),我们以英雄联盟的数据做示例 一个hero对应一个role(我们这里暂定) 一个role可以对应多个her ...