前言

上篇文章我们一起对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. slua中,绑定lua文件到Monobehavior的一种方法

    slua本身并不提供如何把一个lua文件绑定到一个预制中,就像一个普通的继承自monobehavior的自定义脚本那样,而tolua的框架却采用了拙劣的做法: public class LuaBeha ...

  2. Mongos WoW

    http://blog.csdn.net/yuleslie/article/details/7430094 https://github.com/mangostwo/ https://www.getm ...

  3. MySQL 数据库 查 续

    MySQL 增删查改 必知必会 4.1.13 使用 like 关键字进行模糊查询 -- 说明:模糊查询,使用查询关键字like,like意思是类似于,像...的意思 -- 模糊查询,支持两种字符匹配符 ...

  4. 《神经网络的梯度推导与代码验证》之vanilla RNN前向和反向传播的代码验证

    在<神经网络的梯度推导与代码验证>之vanilla RNN的前向传播和反向梯度推导中,我们学习了vanilla RNN的前向传播和反向梯度求导,但知识仍停留在纸面.本篇章将基于深度学习框架 ...

  5. 本地ubuntu 往阿里云ubuntu服务器传文件

    起因 今天在本地ubuntu搭环境,弄好之后需要把本地的文件传到服务器上去... 本想着用xftp直接拖过去,结果连接不上,可能是我太菜了吧,只好百度找方法··· 这里就简单的记录一下吧~ 步骤 sc ...

  6. 图解冒泡排序及算法优化(Java实现)

    冒牌排序 基本思想 定义:冒泡排序的英文是bubblesort,它是一种基础的交换排序 原理:每次比较两个相邻的元素,将较大的元素交换至右端 (升序排序) 思路:相邻的元素两两比较,当一个元素大于右侧 ...

  7. conda和pip重新配置源

    conda设置源之后出现了问题,报错condaHTTPError: 之前按照网上的一些教程设置了清华源之后,过了一段时间,今天来装新的库时报了以上错误,特此记录一下. conda 源重新设置 重新去清 ...

  8. d3力导图绘制节点间多条关系平行线的方法

    之前用d3做了多条线之间的绘图是曲线表示的,现在产品要求改成平行线的样式,经过在网上的调研和自己的尝试,实践出一个可用的方法,分享给大家,先展示下结果: 事先声明,本方法是在以下参考网站上进行的结合和 ...

  9. 预科班D6

    2020.09.14星期一 预科班D6 学习内容: 自习 发布小游戏 1.配置网络 #查看当前ip ifconfig #关闭NetworkManager systemctl stop NetworkM ...

  10. c/c++ 实现三角函数(不使用库函数) sin/cos/tan/cot

    c/c++ 实现三角函数(不使用库函数) sin/cos/tan/cot #include <iostream> #include <cstdlib> #include < ...