JVM相关知识点

2023-11-09

目录

​编辑一、JVM 内存区域划分

1、栈 

2、堆

3、元数据区(方法区)

二、JVM 类加载机制

1、类加载机制介绍

2、双亲委派模型(经典问题)

三、JVM 垃圾回收机制 GC

1、了解 GC

2、GC 实际工作过程:

(1)找到垃圾 / 判定垃圾

(2)对象的释放


一、JVM 内存区域划分

JVM 也就是启动的时候,会申请到一整个很大的内存区域

JVM 是一个应用程序,要从操作系统这里申请内存(相当于租了个写字楼)

JVM 就根据需要,把整个空间,分成几个部分,每个部分各自有不同的功能作用

1、栈 

2、堆

堆,是整个 JVM 空间最大的区域

new 出来的对象,都是在 堆 上,类的成员变量,也是在堆上面

堆是一个进程只有一份的,栈是每个线程有一份,一个进程有多个

堆,多个线程用的都是同一个堆,栈,每个线程用自己的栈

每个 jvm ,就是一个 java 进程,如果弄两个 java 进程,就是两个 jvm 了


3、元数据区(方法区)

类对象,常量池,静态成员等都存放在这里


如何判断某个变量在哪个区域上?

1、局部变量:在 栈 上

2、普通成员变量:在 堆 上

3、静态成员变量:在 方法区 / 元数据区

二、JVM 类加载机制

1、类加载机制介绍

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

加载:把 .class 文件找到(找的过程),打开文件,读文件,把文件内容读到内存中

验证:根据 jvm 虚拟机规范,检查下 .class 文件格式是否正确

准备:给类对象分配内存空间(现在元数据区占个位),此时内存初始化成全为0,也会使静态成员设置成 0 值

解析:初始化字符串常量,把符号引用转为直接引用

初始化:调用构造方法,进行成员的初始化,执行代码块,静态代码块,加载父类 .....

把符号引用转为直接引用:字符串常量,得有一块内存空间,来存这个字符的实际内容,还得有一个引用,来保存这个内存空间的起始地址

在类加载之前,字符串常量,此时是处在 .class 文件中的,此时 “引用” 记录的并非字符串常量的真正的地址,而是它在文件中的 “偏移量” 这个东西(或者是个占位符)

类加载之后,才真正把这个字符串常量给放到内存中,此时才有 “内存地址”,这个引用才能真正被赋值成指定内存地址(直接引用)

类加载到底什么时候会触发?

不是 java 程序一运行,就把所有的类都加载的,而是真正用到才加载(懒汉模式)

1、构造 类 的实例

2、调用这个类的静态方法 / 使用静态属性

3、加载子类,就会先加载其父类

一旦加载过之后,后续再使用,就不必重复加载了


2、双亲委派模型(经典问题)

双亲委派模型,描述的是这个加载,找 .class 文件的基本过程

JVM 默认提供了三个类加载器,他们三个各有分工

1、BootstrapClassLoader ,负责加载标准库中的类(Java 规范,要求提供哪些类,无论是哪种 JVM 的实现,都会提供一样的类)

2、ExtensionClassLoader ,负责加载 JVM 扩展库中的内容(规范之外,由实现 JVM 的厂商 / 组织,提供的额外的功能)

3、ApplicationClassLoader ,负责加载用户提供的第三方库 / 用户项目代码中的类

上述三个类,存在 “父子关系”,(不是父类子类,相当于每个 classLoader 有一个 parent 属性,指向自己的父 类加载器

上述三个类加载器如何配合工作?

首先,加载一个类的时候,是先从 ApplicationClassLoader 开始,但是 ApplicationClassLoader 会把加载任务交给父亲,让父亲去进行

于是 ExtensionClassLoader 要去加载了,但是也不是真加载,而是再委托自己的父亲

于是 BootstrapClassLoader 要去加载了,也是想委托自己的父亲,结果发现自己的父亲是 null,没有父亲 / 父亲加载完了,没找着类,才由自己进行加载

此时,BootstrapClassLoader 就会搜索自己负责的标准库目录相关的类,如果找到就加载,如果没找到,就继续由子 类加载器 进行加载,于是 ExtensionClassLoader 真正搜索扩展库相关的墓库,如果找到就加载,如果没找到就还是由子 类加载器 进行加载

ApplicationClassLoader 真正搜索用户项目相关的目录,没找到就由子类加载器进行加载(由于当前没有 子 了,就只能抛出 类找不到 这样的异常)

为什么要有上述这个顺序?

上述这套顺序,其实出自于 jvm 实现代码的逻辑,这段代码大概是类似于 “递归” 的方式写的

这个顺序,最主要的目的就是为了保证 BootstrapClassLoader 能够先加载,ApplicationClassLoader 能够后加载,这就可以避免用户创建了一些奇怪的类,引起不必要的 bug

假设用户在自己的代码中,写了个 java.lang.String 这个类,按照上述加载流程, jvm 加载的还是标准库的类,不会加载到用户自己写的类

这样就能保证,即使出现上述问题,也不会让 jvm 已有代码混乱,最多是用户自己写的类不生效罢了

再另一方面,类加载器其实是可以用户自定义的,上述三个类加载器是 jvm 自带的,用户自定义的类加载器,也可以加入到上述流程,就可以和先有的加载器配合使用

站在 jvm 的角度,类加载的核心应该是:解析 .class 文件,解析每个字节是干什么的

破坏双亲委托模型:

自己写的类加载器,可以去遵守也可以不遵守,是否遵守,主要是看需求

tomcat ,,去加载 webapp 这里就是单独的类加载器,不遵守双亲委派模型


三、JVM 垃圾回收机制 GC

1、了解 GC

什么是垃圾?

垃圾指的是不再使用的内存

垃圾回收就是把不用的内存帮我们自动释放了

C 语言中有 malloc ,C++ 有 new ,这些属于动态申请内存(在堆上申请一块内存空间)

上述内存空间需要手动方式进行释放:free,delete

如果不手动释放,这块内存的空间就会一直存在,一直存在到进程结束(堆上的内存生命周期比较长,不像 栈,栈的空间会随着方法的执行结束,栈帧自动销毁而自动释放,堆则默认不能自动释放

这可能会导致一个很严重的问题:内存泄露

如果内存一直占着,又不释放,就会导致剩余空间越来越少,进一步导致后续的内存申请操作申请失败

因为内存泄露是一个很严重的问题,因此大佬们就想了一些办法来解决这个问题,垃圾回收(GC)是其中最主流的一种方式

GC 的好处:非常省心,让程序员写代码简单点,不容易出错

GC 的坏处:需要消耗额外的系统资源,也有额外的性能开销

另外,GC 这里还有一个比较关键的问题:STW 问题(stop the world)

1、如果有时候,内存中的垃圾已经很多了,此时触发一次 GC 操作,开销可能非常大,大到可能就把系统资源吃了很多

2、另一方面,GC 回收垃圾的时候,可能会涉及一些 锁 操作,导致业务代码无法正常执行

JVM 里面有很多块内存区域,GC 主要是针对其中的 堆 进行释放

GC 是以 “对象” 为基本单位进行回收的!!!(而不是字节)

GC 回收的是整个对象都不在使用的情况,而一部分使用,一部分不使用的对象,暂时先不回收

要回收,就是回收整个对象,而不会回收半个对象,因此我们说 GC 是以对象为基本单位进行回收的

这样设定,目的就是 “简单”


2、GC 实际工作过程:

(1)找到垃圾 / 判定垃圾

判定哪个对象以后一定不用了,哪个对象后面还可能使用

关键思路:抓住这个对象,看看到底有没有 “引用” 指向它

在 java 中,只有一条路:通过引用来使用!!!

如果一个对象,有引用指向它,就可能被使用到,如果一个对象没有引用指向,就一定不会再被使用了

具体如何知道对象是否有引用指向?

两种典型实现:

(1)引用计数 [不是 java 的做法]

给每个对象,都分配了一个计数器(整数)

每次创建引用指向该对象,计数器就 +1,每次该引用被销毁了,计数器就 -1

缺点:

1、内存空间浪费的多(利用率低)

每个对象都得分配一个计数器,如果对象特别多,占用的额外空间就会很多(尤其是对象特别小的情况)

2、循环引用的问题

接下来,如果 a 和 b 引用销毁,此时 1 号对象和 2 号对象引用计数都 -1,但是结果都还是 1,不是 0

虽然不是 0,不能释放内存,但是实际上这两个对象已经没有办法被访问到了


(2)可达性分析 [ java 的做法]

java 中的对象,都是通过引用来指向并访问的

经常,是一个引用指向一个对象,这个对象里的成员,又指向别的对象

整个 java 中所有的对象,就通过类似于上述的关系,通过这种 链式 / 树形 结构,整体给串起来

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

每次你 new 一个对象,jvm 都会记录下来,jvm 会知道一共哪些对象,每个对象的地址...

相当于 jvm 自己捏着一个所有对象的名单,通过上述遍历,把可达的标记出来,剩下的不可达的就可以作为垃圾进行回收了

可达性分析,需要进行类似于 “树遍历” 这个操作,相比于引用计数来说,肯定要更慢一些,但是空间利用率提高了,同时解决了循环引用的问题

但是速度慢没关系,上述可达性分析遍历操作,并不需要一直执行,只需要每隔一段时间,分析一遍就可以了(虽迟,但到)

进行可达性分析遍历的起点,称为:GCroots,主要有以下几种:

1、栈上的局部变量

2、常量池中的对象

3、静态成员变量

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


(2)对象的释放

如何清理垃圾?,主要是三种基本方法

1、标记清除

简单粗暴,但是存在内存碎片问题

被释放的内存空间是零散的,不是连续的,而申请空间要求的是连续空间

这就可能会导致总的空闲空间很大,但是每一个具体空间都很小,此时申请大一点的空间就会失败了


2、复制算法

复制算法,就是把不是垃圾的对象,复制到另外一半,然后把整个空间删除掉

每次触发算法,都是向另一侧复制,内存中的数据拷贝过去

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


3、标记整理

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

缺点:效率不高,如果要搬运的空间比较大,开销也很大


上述做法,并不完美,基于上述这些基本策略,我们又有了一个复合策略:“分代回收”

把垃圾回收,分成不同的常见,有的常见使用这个算法,有的场景使用那个算法,各展所长

分代是怎么分的? 

基于一个经验规律:如果一个东西存在的时间比较长了,大概率会继续长时间持续存在下去

java 对象,要么就是生命周期特别短,要么就是特别长,我们就根据生命周期的长短,分别使用不同的算法

我们给对象引入一个概念:年龄(熬过 GC 的轮次)

我们可以认为:年龄越大,对象存在的时间就越久

伊甸区:刚 new 出来的,年龄是 0 的对象

熬过一轮 GC,对象就要被放到幸存区了

虽然看起来幸存区很小,伊甸区很大,但是一般够放(根据经验规律,大部分的 java 对象都是 “朝生夕死”,生命周期特别短)

伊甸区 => 幸存区 使用的是 “复制算法”

到了幸存区域之后,也要周期性的接受 GC 的考验,如果变成垃圾,就要被释放,如果不是垃圾,就拷贝到另一个幸存区(这两个幸存区同一时刻只存在一个,两个之间使用复制算法来回拷贝)

由于幸存区体积不大,此处的空间浪费,也能接受

如果这个对象,已经在两个幸存区中,来回拷贝很多次,就要进入老年代

老年代,都是年纪大的对象,生命周期普遍更长

针对老年代,也要周期性GC 扫描,但是频率更低了,如果老年代的对象是垃圾了,使用标记整理的方式进行释放

上述是 GC 中典型的垃圾回收算法

实际上,JVM 在实现的时候,会有一些差异,事实上,JVM 有很多的 “垃圾回收实现”,称为 垃圾回收器

回收期具体的实现做法,会按照上述算法思想展开,但是会有一些 变化 / 改进,不同的垃圾回收器,侧重点不同,这里就不再介绍了

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

JVM相关知识点 的相关文章

随机推荐

  • IsBadReadPtr函数和异常处理

    起因是优化代码性能 注意到这个函数 搜了一下发现是微软弃用的函数 说是有线程安全问题 经过一系列操作发现 处理大文件时这个函数会导致耗时变长 于是就研究一下这个函数 首先看函数开头 mov edi edi push ebp mov ebp
  • 右脑记忆法的个人理解

    先写个提纲 右脑记忆法 王峰 袁文魁等的记忆方法基础 也是大脑锦标赛 记忆大师的通用方法学 说是右脑记忆 其实就是图像记忆 因为形象化的信息 更容易记忆 最强大脑节目 记忆是很关键的一项能力 走进科学 记忆有魔方 http tv peopl
  • linux安装南大通用数据库 GBase 8s V8.8

    linux安装南大通用数据库 GBase 8s V8 8 1 操作系统 数据库 2 下载链接 3 安装文档 4 安装前准备 4 1 以root用户创建 gbasedbt 组和用户 4 2 创建 GBase 8s 数据库安装目录 4 3 上传
  • 缠论的基本原理

    缠论的基本原理 飞吻 短期均线略略走平后继续按原来趋势进行下去 14课 唇吻 短期均线靠近长期均线但不跌破或升破 然后按原来趋势继续下去 14课 湿吻 短期均线跌破或升破长期均线甚至出现反复缠绕 如胶似漆 14课 女上位 短期均线在长期均线
  • 【Unity技巧】Unity中的优化技术

    原文地址 写在前面 这一篇是在Digital Tutors的一个系列教程的基础上总结扩展而得的 Digital Tutors是一个非常棒的教程网站 包含了多媒体领域很多方面的资料 非常酷 除此之外 还参考了Unity Cookie中的一个教
  • 【论文学习】Fast R-CNN

    论文地址 Fast R CNN 1 Abstract Fast R CNN也是主要应用在目标检测的一种方法 它建立在前人工作的基础上 利用深度卷积网络更加高效地对目标进行分类 与之前的工作相比 Fast R CNN采用了一些在提升检测精度的
  • springBoot的分页插件

    目录 1 加入依赖 2 配置分页插件 3 使用分页插件 1 加入依赖
  • 初识小熊派——小熊派功能简介

    小熊派功能简介 小熊派IoT开发板一款由南京小熊派智能科技有限公司联合华为技术有限公司基于STM32L431RCT6设计的高性能物联网开发板 开发板充分考虑物联网感知层设备的多样性 具有强大的可扩展性 用于提供给开发者评估及快速设计相关物联
  • UDP和TCP的区别

    UDP User Datagram Protocol 和 TCP Transmission Control Protocol 是两种常见的传输层协议 它们在设计和用途上有很大的区别 以下是它们的主要差异 连接性 TCP 是一个连接导向的协议
  • mysql安装教程【安装版】

    一 下载 进入mysql官方网站MySQL MySQL Downloads 下载社区版 MySQL Community GPL Downloads MySQL Installer for Window 第一个 大小是10多M 是联网在线安装
  • IDEA中控制台乱码解决方法

    文章目录 1 在设置中的 文件编码 中将3个位置设为UTF 8 注 此处设置与控制台乱码无关 3处可均设为UTF 8或均设为系统默认值 2 在Tomcat的 编辑配置 中 将VM options设为 Dfile encoding GBK 与
  • git创建空白分支,并将本地文件上传到分支

    git创建空白分支 并将本地文件上传到分支 1 创建一个空白的分支的需求 在Git中创建分支 是必须有一个父节点的 也就是说必须在已有的分支上来创建新的分支 如果工程已经进行了一段时间 这个时候是无法创建空分支的 但是有时候就是需要创建一个
  • 分布式系统的时间

    分布式系统的时间 事件的顺序 大家都知道 Linearizability在一些系统 譬如分布式数据库 里面是非常重要的 我们不能允许数据更新之后仍然能读到原先的值 譬如银行转账 用户A有100元 转给用户B 10元 这个操作之后用户A只可能
  • 开发提效小技巧分享(二)

    前言 在日常开发中 影响我们开发效率的因素很多 有时候不仅仅会影响开发效率 还会搞崩我们的心态 为了提高工作效率 保持愉悦心情 腾出更多的时间来进行摸鱼大业 前辈们折腾出了很多有用的小技巧 我在这里记录一下自己常用的几个小技巧 快速拉取 G
  • 卷积层计算量(FLOPS)和参数量的计算

    1 卷积参数量的计算 若卷积层的输入featuremap的维度为Cin Hin Win 卷积核的大小为K1 K2 padding P1 P2 stride S1 S2 卷积核 filter 的数量为Cout 则输出特征图的大小为Cout H
  • 【前端】Vue项目:旅游App-(15)home:网络请求house数据、动态并组件化展示house列表信息

    文章目录 目标 过程与代码 content组件 请求数据 houseList request store 控制台输出 动态加载更多列表数据 house item组件 阶段1 数据传送 阶段2 对着目标写样式 house item v9 ho
  • Ubuntu开发环境搭建详细笔记

    最近想要搭建一套 linux 的开发环境 因此想要在现有的 ThinkPad T470s 上安装 ubuntu 18 04 双系统 之前一直用的都是用 virtualbox 虚拟机 同学建议直接安装linux系统一直没装 结果就是各种不方便
  • 如何判断/获取屏幕的状态(亮屏已解锁,亮屏未解锁,黑屏)

    最近自己在做一个统计手机使用时间 频次的时间管理软件 遇到了这个问题 网上资料有些杂乱 有些还是错误的 于是我参考了一些资料 查阅了官方的API 将不同的方法的作用与区别总结在此 方法大致区别如下图 1 PowerManager的isInt
  • Python 随机数函数 random 七种常用方法解说

    目录 一 random random 返回 0 与 1 之间的随机浮点数N 二 random uniform a b 返回 a 与 b 之间的随机浮点数N 三 random randint a b 返回一个随机的整数N 四 random r
  • JVM相关知识点

    目录 编辑一 JVM 内存区域划分 1 栈 2 堆 3 元数据区 方法区 二 JVM 类加载机制 1 类加载机制介绍 2 双亲委派模型 经典问题 三 JVM 垃圾回收机制 GC 1 了解 GC 2 GC 实际工作过程 1 找到垃圾 判定垃圾