JVM(内存区域划分、类加载机制、垃圾回收机制)

news2024/11/18 15:33:16

目录

一. 内存区域划分

1.本地方法栈(Native Method Stacks)

2.虚拟机栈(JVM Stacks)

3.程序计数器(Program Counter Register)

4.堆(Heap)

5.元数据区(Metaspace)

二.类加载机制

1.加载

2.验证

3.准备

4.解析

5.初始化

"双亲委派模型"

三. GC 垃圾回收机制

GC实际工作过程

1.找到垃圾/判定垃圾

(1).引用计数(Java没有使用)

(2).可达性分析(Java使用的)

2.如何清理垃圾

(1).标记清除​编辑

(2).复制算法​编辑

(3).标记整理​编辑

"分代回收"


一. 内存区域划分

正如一个公司的运作模式,首先公司需要一块场地,再接着需要根据公司的各个部分,将这块场地划分成一块一块的部门,每个部门都有独立的功能。

JVM的内存区域划分也是如此,JVM启动的时候会申请一整个很大的内存区域(JVM是个应用程序,要从操作系统中申请内存),接着根据需求,将整个空间分成几个部分。

1.本地方法栈(Native Method Stacks)

native表示JVM内部的C++代码(JVM是用C++写的),所以本地方法栈就是给调用native方法(JVM内部方法)准备的栈空间

问题:C++代码是不是不用跑在虚拟机上 答:像C、C++、Go、Rust都是把代码变成成native code,也就是可以直接被cpu识别的机械指令 而Java、Python、PHP为了跨平台,都是统一翻译成指定的字节码(字节码都是一样的),然后由对应的虚拟机转换成机械指令

2.虚拟机栈(JVM Stacks)

给Java代码使用的栈

注意:stack栈,在之前数据结构中也学习过,数据结构中的“栈”是“后进先出”的,和这里所说的栈不是一个东西。

这里所提的栈,是JVM中的一个特定的空间。

  • 对于JVM虚拟机栈,这里存储的是方法之间的调用关系
  • 对于本地方法栈,存储的是native方法之间的调用关系

整个栈空间内部,可以认为是包含很多个元素,每个元素表示一个方法,把这里的每个元素,称之为“栈帧”,这一个栈帧里,会包含这个方法的入口地址、方法参数、返回地址、局部变量.....

 

但是由于函数调用中,也有“后进先出”的特点,因此此处的栈也是后进先出的,只是数据结构中的栈是一个更通用的,更广泛的概念,而JVM中的栈是特指JVM上的一块内存空间。

从内存划分图中也可以看出,这里的栈有多个,每个线程有一个(线程是一个独立的执行流),使用jconsole(jdk下bin目录中)可以查看java进程内部的情况,点击线程就可以看到线程调用栈的情况。

问题:这些线程每次创建都分相同空间,那么栈会不会被很快就用完了? 答:栈的整体大小一般都不会很大,但是每个栈帧的大小也是比较小的,遇到无限递归的代码会遇到栈溢出的情况,就正常写代码,创建销毁线程一般是不需要担心这个问题的。

3.程序计数器(Program Counter Register)

记录当前线程执行到哪条指令(很小的一块存一个地址),也是每个线程有一份。

4.堆(Heap)

堆是整个JVM空间的最大的区域,new出来的对象,都是在堆上的,类的成员对象也就在堆上了

  • 堆 是一个进程只有一份的

  • 栈 是每个线程有一份,一个进程有多个线程

  • 堆,多个线程用的都是同一个堆

  • 栈,每个线程用自己的栈

每个jvm就是一个java进程

5.元数据区(Metaspace)

以前这个区域叫做方法区,java8开始改为了元数据区 这里存储的有类对象、常量池、静态成员

元数据区也是一个进程一份,多个线程共用这一份

问题:final修饰的变量在这里存储吗? 答:不一定,有一定概率被优化成字面值常量,也有可能没优化

总结

  • 局部变量在栈
  • 普通成员变量在堆
  • 静态成员变量在方法区/元数据区

二.类加载机制

准确的来说,类加载就是将.class文件,从文件(硬盘)被加载到内存中(元数据区)这样的过程。

1.加载

  • (1).通过一个类的全限定名来获取这个类的二进制字节流
  • (2).将这个字节流多代表的静态存储结构转化成方法区的运行时数据结构
  • (3).在内存中生成一个代表这个类的java.lang.Class对象

但是对应上述的要求并不是具体的,JVM的实现过程和应用是比较灵活的,比如获取类的二进制字节流,并没有说明如何获取,所以就有了从压缩包中获取(jar、war、ear),从网路中获取(Applet),运行时计算生成(动态代理)

对于不是数组的类的加载我们可以自定义去控制字节流的获取方式,而数组类就不一样了,因为数组类本身不是通过类加载进行创建的,而是由JVM直接创建

2.验证

根据jvm虚拟机规范,检查.class文件的格式是否符合要求

3.准备

给类对象分配内存空间(此时内存初始化成全0),在这个阶段里,为静态变量分配内存并设置静态变量初始值。这里说的初始值通常情况下,不是代码中写的初始值,而是数据类型的零值。代码中写的初始值,是在初始化阶段赋值的。如果是静态常量(被final修饰),这个阶段就会被直接赋值为代码中写的初始值

4.解析

针对字符串常量进行初始化,把符号引用转为直接引用

字符串常量,需要有一块内存空间存这个字符的实际内容,还需要一个引用,来保存这个内存空间的起始地址。 在类加载之前,字符串常量是处在.class文件中的,这个时候没有地址这个概念,此时这个"引用"记录的并非是真正的地址,而是他在文件中的"偏移量"(或者是个占位符)。 在类加载之后,才真正把这个字符串常量给放到内存中,此时才有"内存地址",这个原因才能真正赋值成指定的内存地址。

5.初始化

真正针对类对象里面的内容进行初始化,加载父类、执行静态代码块的代码.....

总结

问题:一个类,什么时候会被加载呢? 答:不是java程序一运行就将所有的类都加载了,而是真正用到的时候才加载(赖汉模式),比如构造类的实例、调用这个类的静态方法/使用静态属性、加载子类,就会先加载父类。 而一旦加载过后,后续再使用就不需要重新加载了。

"双亲委派模型"

JVM 默认提供了三个类加载器

  • BootstrapClassLoader 负责加载标准库中的类
  • ExtensionClassLoader 负责加载JVM扩展库中的类
  • ApplicationClassLoader 负责加载用户提供的第三方库,用户项目代码中的类

而上述三个类存在"父子关系",BootstrapClassLoader为最大,ApplicationClassLoader为最小。

此处的"父子关系",不是父类、子类,而是每个ClassLoader中都有一个parent属性指向自己的父 类加载器

上述三个类加载器的工作模式:

首先加载一个类的时候,先从ApplicationClassLoader开始

但是ApplicationClassLoader会把加载任务交给父亲,于是ExtensionClassLoader就会去加载,但是也不是真加载,而是再委托自己的父亲去加载

于是BootstrapClassLoader就去加载,他也想委托给父亲,但是发现父亲为null,所以就由自己加载,那么BootstrapClassLoader就会搜索自己负责的标准库中的类,如果找到就加载,如果没找到就交给自己的子类进行加载

于是ExtensionClassLoader就去加载,搜索自己负责的JVM扩展库中的类,找到则加载,找不到就交给子类

于是ApplicationClassLoader进行加载,搜索用户用的第三方库和项目中的类,找到则加载,找不到的话由于自己没有子类,就只能抛出类找不到这种异常

使用这个顺序进行,主要的目的据说为了保证Bootstrap能够先加载,Application能够后加载,这就避免了用户创建一些奇怪的类而导致的bug

比如用户写了一个java.lang.String这个类,此时能保证JVM加载的还是标准库中的类,而不会加载到用户写的这个类,这样就能保证即使出现上述问题,也不会让jvm的代码混乱,最多就是用户写的代码不生效

三. GC 垃圾回收机制

首先要知道的是在JVM中进行垃圾回收的是"堆",并且GC是以"对象"为基本单位进行回收的

GC回收的是整个对象都不再使用的情况,而那种一部分使用,一部分不使用的对象先不回收了(一个对象里有很多属性,可能其中10个属性后面要用,10个属性后面再也不用了)

GC实际工作过程

1.找到垃圾/判定垃圾

关键思路在于看看到底有没有"引用"指向它,Java中使用对象只有一条路,通过引用来使用,如果一个对象有引用指向它那么就有可能被用到,如果没有引用指向它那么就不可能被用到

那么怎么知道对象是否有引用指向?

(1).引用计数(Java没有使用)

给每个对象分配一个计数器(整数),每次创建一个引用指向该对象,计数器就+1,每次该引用被销毁了计数器就-1

问题:这种方法很好理解,但是为什么Java不使用呢?

答: 1.引用计数内存空间浪费的多,每个对象都要分配一个计数器,计数器按照4个字节算,代码中对象非常少无所谓,怕的就是对象特别多,并且每个对象都比较小,那么这个时候占用的额外空间就会很多(一个对象的体积是1k,多4个字节无所谓,一个对象体积是4个字节,再多4个字节相当于体积扩大一倍)

2.存在循环引用问题

(2).可达性分析(Java使用的)

Java中的对象,都是通过引用来指向并访问的,经常是一个引用指向一个对象,这个对象里的成员又指向别的对象

整个Java中的所有对象,就通过类似这种树形/链式结构,整体串起来

可达性分析,就是把所有这些对象组织的结构视为树,从根节点出发,遍历树,所有能被访问到的对象就标记为"可达"(不能被访问到的,就是不可达)

此处的图中虽然只有root引用,但是上述6个对象都是可达的

  • root-->a
  • root.left-->b
  • root.right-->c
  • root.left.left-->d
  • root.left.left.left-->e

小结:可达性分析需要进行类似于"树遍历"整个操作相比于引用计数来说肯定要慢一些的,但是速度慢没关系,上述遍历操作,并不需要一直执行,只需要每隔一段时间分析一遍即可

进行可达性分析遍历的起点,称为GCroots

1.栈上的局部变量

2.常量池中的对象

3.静态成员变量

一个代码中可能有很多这样的GCroots,把每个起点都往下遍历一遍就完成了扫描过程

2.如何清理垃圾

(1).标记清除

标记清理的特点是简单粗暴,但是会导致内存碎片化问题,被释放的空闲空间是零散的,不是连续的

但是我们申请内存的要求是需要连续的空间,总的空闲空间可能很大,但是每个具体的内存空间可能很小,可能导致申请大一点的内存就失败了

例如总的空闲空间是10k,分成1k一个,一共十个,此时如果需要申请2k的空间,那么就会申请失败

(2).复制算法

复制算法解决了内存的碎片化问题

复制算法把整个内存分为两半,用一半丢一半

把不是"垃圾"的对象复制到另外一半内存空间,然后把之前的一半整个空间删掉

若是后续再触发GC,那么就在右边这一半进行复制算法到左边即可

缺点:空间利用率低下,如果垃圾少,有效对象多,那么复制成本就比较大了

(3).标记整理

标记整理解决了复制算法的缺点

类似于顺序表中的删除中间元素,有元素搬运的操作,这样既保证了空间利用率,也解决了内存碎片化问题

但是,很明显,这种做法,效率也不高,元素搬运如果需要搬运的空间大,那么开销也大

上述的三种方法都有其对应的缺点,那么有没有比较完美的办法呢?

"分代回收"

把垃圾回收,分成不同的场景,对应不同的场景使用上述不同的办法

那么如何定义上述的不同场景呢?也就是分代是如何分的?

这里的分,是基于一个经验规律:如果一个东西(对象),存在的时间长了,那么大概率还会继续的长时间存在下去(比如C语言从197x年就开始诞生了,到如今还依然存在并且活跃,那么我们就有理由相信他还会存在很长时间),而上述规律对应Java中的对象也是有效的,java对象要么就是生命周期特别短,要么就是特别长

根据上方的规律,我们给对象引入一个概念"年龄"(这个年的单位是熬过的GC的轮次),年龄越大那么存在的时间就越久

刚new出来的对象,年龄是0的对象,放到伊甸区(出自圣经,上帝在伊甸园造小人)

熬过一轮GC,对象就要被放到幸存区了,那么伊甸区-->幸存区就使用复制算法(将有效对象复制到幸存区,伊甸区整体释放)

到幸存区后,也要接受GC的考验,如果变成垃圾就要被释放,如果不是垃圾,那么就拷贝到另一个幸存区(这两个幸存区,同一时刻值用一个,在两个幸存区反复横跳),所有这里也是使用复制算法(由于幸存区的体积不大,此处浪费的空间也能接受)

如果这个对象在幸存区中GC了很多次了,那么这个时候就进入到老年代,老年代都是年龄大的对象,生命周期普遍更长,那么针对老年代进行的GC扫描频率就低了,如果老年代的对象是垃圾了,那么就使用标记整理的方式进行释放

小结:

上述的GC中典型的垃圾回收算法:如何确定垃圾、如何清理垃圾,这里的策略,实际上在JVM实现的时候,会有一定的差异,JVM有很多的"垃圾回收实现"称为"垃圾回收器",回收器的具体实现做法,会按照上述算法思想展开,不同的垃圾回收器侧重点不同,有的追求GC扫描快、有点追求扫描好、有点追求用户打扰少(STW短).....

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1704307.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

[自动驾驶技术]-7 Tesla自动驾驶方案之算法(AI Day 2022)

特斯拉在2022年AI Day上更新了感知规控算法模型,核心引入了Occupancy技术。下图是特斯拉活动日展示的主题内容,本文主要解读Planning和Neural Network部分。 1 规划决策 Interaction search-交互搜索 特斯拉在自动驾驶规划中使用了一种高度复杂和优化的…

SpringCloud整合Seata1.5.2

Windows下部署Seata1.5.2可参照博文&#xff1a;Windows下部署Seata1.5.2&#xff0c;解决Seata无法启动问题-CSDN博客 1. 引入依赖 <!-- 分布式事务 --> <dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-st…

echarts配置记录,一些已经废弃的写法

1、normal&#xff0c;4.0以后无需将样式写在normal中了 改前&#xff1a; 改后&#xff1a; DEPRECATED: normal hierarchy in labelLine has been removed since 4.0. All style properties are configured in labelLine directly now. 2、axisLabel中的文字样式无需使用te…

近五年营收和净利润大幅“败北”,尚品宅配今年押注扩张加盟

​ 《港湾商业观察》廖紫雯 两个月前经历过高管公开信的尚品宅配&#xff08;300616.SZ&#xff09;&#xff0c;无论是2023年年报&#xff0c;还是今年一季报&#xff0c;虽然公司净利润表现尚佳&#xff0c;但收入端的持续承压仍然备受关注。 今年一季报&#xff0c;尚品宅…

Mac免费软件推荐

1. iTerm2 - 功能强大的终端 iTerm2 是一个功能强大且灵活的终端仿真器&#xff08;可替代系统默认终端&#xff09;&#xff0c;适合需要在 macOS 上进行大量终端操作的用户。其丰富的功能和高可定制性使得 iTerm2 成为许多开发者和系统管理员的首选工具。无论是处理多个会话…

RabbitMQ 之 死信队列

目录 ​编辑一、死信的概念 二、死信的来源 三、死信实战 1、代码架构图 2、消息 TTL 过期 &#xff08;1&#xff09;消费者 &#xff08;2&#xff09;生产者 &#xff08;3&#xff09;结果展示​编辑 3、队列达到最大长度 &#xff08;1&#xff09;消费者 &…

百度发布代码辅助工具,超强

不会用AI的程序员&#xff0c;会跟不会用智能手机的人一样 百度这个代码助手助手感觉还是不错的 https://comate.baidu.com/?inviteCodeijmce7dj 目前看下来这个代码助手是比较强的&#xff0c;比阿里的那个灵码好用&#xff0c;他可以引用到当前的文件&#xff0c;并且能分…

Spring Cache基本使用

Spring 从 3.1 版本开始定义缓存抽象来统一不同的缓存技术&#xff1b;在应用层面与后端存储之间&#xff0c;提供了一层抽象&#xff0c;这层抽象目的在于封装各种可插拔的后端存储( ehcache, redis, guava)&#xff0c;最小化因为缓存给现有业务代码带来的侵入。 一、Spring…

DRKCT复现

Osint 羡慕群友每一天 MISC 签到 扫码关注公众号&#xff0c;回复一下行 &#xff08;眼神要好&#xff0c; 我做题时没看见有个二维码&#xff09; 神秘的文字 把代码js运行一下 (用js的原因是前面给的动物代表的字符类似jsfuck代码) &#x13142;![]; &#x13080;!…

Daisy Chain

菊花链是双向和半双工的&#xff0c;因此在 COMH 和 COML 接口上有一个发送器 (TX) 和一个接收器 (RX)。TX 和 RX 功能由硬件根据器件的基底/堆栈检测自动控制。当接收到 WAKE ping/音调时&#xff0c;通信方向由 CONTROL1[DIR_SEL] 和 COMM_CTRL[TOP_STACK] 配置进行设置。 对…

如何处理网安发出的网络安全监督检查限期整改通知

近期&#xff0c;很多客户都收到了网安发出的限期整改通知。大家都比较关心的问题是&#xff0c;如何应对处理这些限期整改通知。后续是否有其他的影响&#xff0c;需要如何做进一步的优化整改和调整。今天就这些问题给大家做一些分享。 一. 为什么会有网安的网络安全检查 主…

系统管理、磁盘分区

系统管理 业务层面&#xff1a;为了满足一定的需求所做的特定操作。 硬盘是什么&#xff0c;硬盘的作用&#xff1a; **硬盘&#xff1a;**计算机的存储设备&#xff0c;机械硬盘是由一个或者多个磁性的盘组成&#xff0c;可以在盘片上进行数据的读写。 连接方式&#xff1a…

谈谈BlueStore的BitmapAllocator

背景 BlueStore在ceph里面承担了数据在底层磁盘上的读写任务&#xff0c;那它的功能里自然就有一块是管理磁盘空间使用的。说白了&#xff0c;就是记录&管理磁盘里哪些空间已经使用了&#xff0c;哪些空间还没有被使用。 目前官方的ceph里使用BitmapAllocator来管理磁盘空…

冯喜运:5.27黄金短线看震荡,今日黄金原油走势分析

【黄金消息面分析】&#xff1a;黄金作为传统的避险资产&#xff0c;在经济不确定性中扮演着至关重要的角色。近期&#xff0c;国际黄金价格经历了显著的波动。从5月9日的低点2325.19美元/盎司反弹至2340美元/盎司以上&#xff0c;尽管金价曾一度触及2449.89美元/盎司的历史高点…

C++ 数据结构算法 学习笔记(33) -查找算法及企业级应用

C 数据结构算法 学习笔记(33) -查找算法及企业级应用 数组和索引 日常生活中&#xff0c;我们经常会在电话号码簿中查阅“某人”的电话号码&#xff0c;按姓查询或者按字母排 序查询&#xff1b;在字典中查阅“某个词”的读音和含义等等。在这里&#xff0c;“电话号码簿”和…

高弹性架构的微服务设计模式

长期以来&#xff0c;开发人员一直使用单片架构&#xff0c;而且长期以来&#xff0c;这种架构一直有效。不幸的是&#xff0c;这些架构使用的部件较少&#xff0c;但体积较大&#xff0c;这意味着如果一个部件发生故障&#xff0c;它们更有可能整体失效。通常&#xff0c;这些…

《书生·浦语大模型实战营》第1课 学习笔记:书生·浦语大模型全链路开源体系

文章大纲 1. 简介与背景智能聊天机器人与大语言模型目前的开源智能聊天机器人与云上运行模式 2. InternLM2 大模型 简介3. 视频笔记&#xff1a;书生浦语大模型全链路开源体系内容要点从模型到应用典型流程全链路开源体系 4. 论文笔记:InternLM2 Technical Report简介软硬件基础…

HR招聘面试测评,哪些工作岗位需要测评创新能力?

什么是创新能力&#xff1f; 创新能力指在现有的物质基础上&#xff0c;通过某些特定的条件&#xff0c;促成满足未来社会发展的新事物。无论是个人还是国家都需要巨大的创新能力&#xff0c;因为创新是一切发展的根基&#xff0c;离开了创新&#xff0c;所有的发展都是原地踏…

Windows安全基础——Windows WMI详解

Windows安全基础——WMI篇 1. WMI简介 WMI&#xff08;Windows Management Instrumentation, Windows管理规范&#xff09;是Windows 2000/XP管理系统的核心&#xff0c;属于管理数据和操作的基础模块。设计WMI的初衷是达到一种通用性&#xff0c;通过WMI操作系统、应用程序等…

设计模式16——策略模式

写文章的初心主要是用来帮助自己快速的回忆这个模式该怎么用&#xff0c;主要是下面的UML图可以起到大作用&#xff0c;在你学习过一遍以后可能会遗忘&#xff0c;忘记了不要紧&#xff0c;只要看一眼UML图就能想起来了。同时也请大家多多指教。 策略模式&#xff08;Strategy…