【JavaEE】JVM 剖析

news/2024/5/18 22:06:24/文章来源:https://blog.csdn.net/zxj20041003/article/details/134216632

JVM

  • 1. JVM 的内存划分
  • 2. JVM 类加载机制
    • 2.1 类加载的大致流程
    • 2.2 双亲委派模型
    • 2.3 类加载的时机
  • 3. 垃圾回收机制
    • 3.1 为什么会存在垃圾回收机制?
    • 3.2 垃圾回收, 到底实在做什么?
    • 3.3 垃圾回收的两步骤
      • 第一步: 判断对象是否是"垃圾"
      • 第二步: 如何回收垃圾

1. JVM 的内存划分

  • JVM 其实就是一个Java的进程
  • 一个进程运行过程中, 就要从操作系统这里申请到一些内存资源, JVM当然也是如此, 搞一大块内存, 供Java代码执行时使用
  • JVM把这一块内存, 又划分出几个区域来, 作为不同的用途

在这里插入图片描述

例题

在这里插入图片描述

2. JVM 类加载机制

  • 类加载机制就是将类从磁盘加载到内存当中
  • Java 程序, 最开始写的是一个 .java 文件, 编译成 .class 文件 (字节码)
  • 运行 java 程序, JVM就会读取 .class 文件, 把文件的内容, 放到内存中, 并且构造成 class 对象 (类对象)

2.1 类加载的大致流程

  1. 加载 找到 .class 文件, 打开并读取文件内容, 并且尝试解析格式
  2. 验证, 检验一下当前 .class 文件是否符合格式要求
  3. 准备 给对象分配内存
    • 最终目标, 是构造出完整的类对象, 分配内存 + 初始化
    • 分配出来的空间, 内容都是 0 此时此刻, 类对象上static也是0
  4. 解析
    • 主要是初始化类对象中涉及到的一些字符串常量
    • 其实字符串常量已经在 .class 文件中有了, 直接读到内存中就行了
    • 将常量池内的符号引用替换为直接引用的过程,也就是初始化常量的过程。
      • 符号引用: 此时.class 文件中, 不知道字符串真实的内存地址在哪里的, 只能知道一个相对偏移量, 知道字符串的内容在 .class 文件的哪个地方, 等到把字符串内容加载到内存中之后, 就可以把 真实的地址, 替换 到刚才的符号引用这里了
  5. 初始化 堆类对象进行更具体的初始化操作, 初始化静态成员, 执行静态代码块, 加载父类 …

2.2 双亲委派模型

什么是双亲委派模型

  • 类加载的时候, 需要先找到 .class 文件
  • 双亲委派模型就是用来描述找 .class 文件的过程的

JVM 中的三个类加载器

在这里插入图片描述

  • 此处的"父子" 不是父类, 子类的继承关系, 而是一种组合关系
  • "子类"对象中存储了"父类"的引用

双亲委派模型描述找 .class 文件具体过程

1.

  1. 从 Application ClassLoader 开始
    • 不会立即就搜索第三方库的目录, 而是先把加载任务委派给父亲, 让父亲先尝试加载
  2. 到了 Extendsion ClassLoader
    • 也不会立即就搜索扩展库的目录, 也是把加载任务委派给父亲, 让父亲先尝试加载
  3. 到了 Bootstrap ClassLoader
    • 也不想立即搜索标准库, 而是也想把任务委派给父亲, 但是 Bootstrap ClassLoader 没有父亲, 就只能自己动手来搜索了
    • 如果找到了诶这个类, 就会进行后续的加载(也就是和 Extension 和 Application 都没关系了)
    • 如果没有找到这个类, 任务就会仍然交回给 孩子 完成
  4. 任务回到了 Extend ClassLoader
    • 就要搜索扩展库的目录, 看有没有匹配的 .Class 文件
    • 如果找到了诶这个类, 就会进行后续的加载(也就是和 Extension 没关系了)
    • 如果没有找到这个类, 任务就会仍然交回给 在 孩子 完成
  5. 任务回到了 Application ClassLoader
    • 就要搜索第三方库的目录 (往往就是你项目目录, 以及和JVM 的一些配置项有关 -classpath 有关)
    • 如果找到类, 就进行后续加载;
    • 如果没找到, 就会抛出一个异常

双亲委派的目的

  • 明确了优先级
  • 标准库的类最先加载, 扩展库其次, 第三方库最低
  • 避免了程序的代码对标准库产生的负面影响

2.3 类加载的时机

  1. 创建类的实例
  2. 使用了类的静态方法/静态属性
  3. 子类的加载就会触发父类

类加载的之后, 就不需要再次加载了;

所以类加载是以懒汉模式进行加载的~

类卸载 (把类对象干掉)

  • 特殊情况

  • 一般来说类加载了之后, 就不必考虑卸载, 一直保持到程序运行结束

  • 有特定的场景, 可能需要用到 卸载 操作

    • 有的服务器, 需要打 “热补丁”
    • 代码有bug, 正常操作应该是修改代码, 重新编译, 用新版本的程序替代就版本 – 重启服务器
    • 有些情况, 服务器不方便重启, 就可以 “打补丁”, 通过一些特殊手段, 把需要提黄的类给卸载掉, 直接用加载好的 类 替换 (新版代码), 这样不重启也能更新代码

3. 垃圾回收机制

3.1 为什么会存在垃圾回收机制?

内存泄漏

  • 垃圾就是进程向操作系统申请内存, 使用完后没有释放, 导致内存泄漏
  • 内存泄漏是一件很危险的事情, 短时间看起来程序运行正常, 而内存泄漏一段时间后, 服务器就会出现明明正在使用的内存挺少的, 但是就是无法再次申请内存了!
  • 内存泄漏就像一个不定时的定时炸弹一样, 十分的危险

各个语言的解决方法

  • C语言: 完全靠程序员手动释放(不靠谱)
  • C++: 引入了只能指针问题
  • java / Python / Ruby / PHP / Go: 引入了垃圾回收 (GC)
    • 最大程度释放了程序员
    • 但是需要消耗额外的系统资源, 消耗一定的时间, 可能会带有 STW 问题
  • Rust 另辟蹊径, 强编译期检查
    • 号称是能够做到更高效, 更强大的释放内存的效果, 内存安全
    • 但是语法复杂, 与C++的语法有过犹而不及, 上手困难

3.2 垃圾回收, 到底实在做什么?

对于java来说, 回收其实是 “对象” 而不是 “字节”, GC 并非判定几个字节是不是垃圾, 进一步的进行回收的

JVM 中有好几个内存区域, GC 回收的是哪里的对象?

  • 栈空间不需要 GC 回收, 栈里面包含很多 “栈帧”, 每个栈帧对应一个方法, 该方法执行结束, 此时这个栈帧就销毁了; 栈帧上的局部变量是啥的自然销毁;
  • 程序计数器同理, 线程销毁, 自然跟着销毁
  • 方法区, 类对象, 很少会涉及到对象的卸载
  • 所以, 堆是GC的主要战场!!

3.3 垃圾回收的两步骤

第一步: 判断对象是否是"垃圾"

"垃圾"的定义

  • 如果一个对象, 在后续代码中, 不会被继续使用到了, 就可以视为是垃圾了
  • 也就是说, 一个对象, 如果没有任何引用指向他, 就可以认为是垃圾了
  • 所以, 判断一个对象是否是垃圾的关键, 就是看这个对象是否有引用指向它

判读是否是垃圾的思路一: 引用计数

  • 原理

    • 给对象里面安排一个计数器, 每次有引用指向它, 就把计数器 +1, 每次引用被销毁, 计数器 -1, 当计数器 为0 的时候, 意味着该对象就是垃圾了
  • 缺点

    1. 空间利用率比较低, 浪费更多个内存空间
      • 如果引用计数分配了 2 哥字节, 对象本体才 4 个字节的话, 引用计数就浪费了 50 % 的空间, 如果代码中都是这种小对象, 并且数量众多, 此时浪费就非常明显了
    2. 存在循环引用的问题, 导致对象不能正确识别为垃圾
  • 注意: 该思路不是JVM采用的方案

判读是否是垃圾的思路二: 可达性分析 – JVM 采用的方案

  • 原理
    • JVM 首先会从现有代码中的能直接访问到的引用出发, 尝试遍历所有能够访问的对象, 只要对象能访问到, 就会标记成 “可达”, 完成整个遍历之后, 可达之外的对象, 也就是不可达, 也就相当于是垃圾了
    • 直接访问到的引用:
      1. 栈上的局部变量
      2. 常量池里的引用
      3. 方法区中的静态成员
  • 缺点
    • 存在遍历扫描, 所以需要消耗一定的时间, 但是不会引入额外的空间
  • 代码执行过程中, 一个对象是否是垃圾, 这件事, 往往是 “动态变化” 的;
  • 上述可达性分析的扫描,是持续的, 周期性的

第二步: 如何回收垃圾

方法一: 标记清除(直接释放)

在这里插入图片描述

  • 直接释放对象, 就可能引起 “内存碎片化”
  • 申请内存的时候, 都是申请的 “连续” 内存空间
  • 释放内存, 就可能会破坏原有的连续性 – 导致 “有内存, 但是申请不了”
  • 这种问题, 还是挺严重的, 内存碎片化随着程序的运行越来越多, 越来越碎片化, 内存就更难申请了

方法二: 复制算法

  • 通过冗余的内存空间, 把有效对象复制到另一部分空间, 避免内存碎片化
  • 把一个对象, 分成两份, 用一份, 丢一份
  • 把左侧区域中, 有效的对象, 复制到右侧
  • 接下来就可以使用右侧区域了, 用了一段时间, 也会有很多对象, 也是同理, 把有效对象复制会左边, 把右侧区域统一释放
  • 但是复杂的内容多, 开销很大, 而且内存利用率也不高

方法三: 标记整理

  • 顺序表删除元素, 搬运
  • 这样的方法, 避免了刚才复制算法内存利用率低的问题
  • 但是, 搬运元素的成本, 也是比较高的

JVM 采用的方法 – 集百家之长 : 分代回收

  • 结合上述方案, 做了一个综合性质的方案
  • 在不同的场景下, 使用不同的回收方式, 扬长避短

分代回收

  • 一个基本的"经验规律"(通过实验观察, 总结出来的规律) : 一个东西存在的越久, 继续存在的可能性就很高

  • Java 中, 对象也就分成两大类

    1. 生命周期特别特别短的
    2. 生命周期特别特别长的
    • 当然也有不长不短的(出现概率更低)
  • Java中对象的年龄是依据 GC 扫描次数来增长的, 挺过一次扫描就增长一次

  • 按照对象的年龄来指定不同的回收策略

在这里插入图片描述

  • 新生的对象

    • 伊甸区
      • 虽然空间看起来不小, 实际上这里的对象, 绝大部分无法活过一轮 GC – 第一轮 GC 扫描到达的时候, 这个对象就已经是垃圾了
      • 第一轮 GC 经过之后, 剩下的还没挂的对象, 就会通过复制算法, 复制到 幸存区
    • 幸存区
      • 这里是大小相同的两部分空间, 每次只有其中一块
      • 继续使用复制算法
      • 如果一个对象在幸存区中, 经过了好多轮, 仍然坚挺没有挂, 则就说明这个对象应该是 "生命周期特别长"的了, 这个时候, 对象来到了老年代了
  • 老年代对象

    • 采用标记整理的方法来释放内存
  • 新生代, 扫描频次是比较高的; 每一轮 GC 留下的有效对象都不多, 复杂算法的开销不大

  • 老年代, 扫描频次就降低了; 不太会有对象真销毁, 此时标记整理的开销也不大

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

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

计算机网络第4章-网络层(1)

引子 网络层能够被分解为两个相互作用的部分: 数据平面和控制平面。 网络层概述 路由器具有截断的协议栈,即没有网络层以上的部分。 如下图所示,是一个简单网络: 转发和路由选择:数据平面和控制平面 网络层的作用…

webgoat-(A1)injection

SQL Injection (intro) SQL 命令主要分为三类: 数据操作语言 (DML)DML 语句可用于请求记录 (SELECT)、添加记录 (INSERT)、删除记录 (DELETE) 和修改现有记录 &#xff…

【C++】详解IO流(输入输出流+文件流+字符串流)

文章目录 一、标准输入输出流1.1提取符>>&#xff08;赋值给&#xff09;与插入符<<&#xff08;输出到&#xff09;理解cin >> a理解ifstream&#xff08;读&#xff09; >> a例子 1.2get系列函数get与getline函数细小但又重要的区别 1.3获取状态信息…

升级Python版本后,anaconda navigator启动失败

anaconda navigator启动失败&#xff0c;尤其是重装不解决问题的&#xff0c;大概率是库冲突 1.通过anaconda-navigator的图标启动&#xff0c;没有反应 2.在命令窗口&#xff0c;输入anaconda-navigator&#xff0c;报错如下 anaconda-navigator 3.错误来自这里 File &quo…

小程序day02

目标 WXML模板语法 数据绑定 事件绑定 那麽問題來了&#xff0c;一次點擊會觸發兩個組件事件的話&#xff0c;該怎么阻止事件冒泡呢&#xff1f; 文本框和data的双向绑定 注意点: 只在标签里面用value“{{info}}”&#xff0c;只会是info到文本框的单向绑定&#xff0c;必须在…

Python---排序算法

文章目录 前言一、pandas是什么&#xff1f;二、使用步骤 1.引入库2.读入数据总结 前言 Python中的排序算法用于对数据进行排序。排序算法可以使数据按照一定的规则进行排列&#xff0c;以便于数据的查找、统计、比较等操作。在数据分析、机器学习、图形计算等领域&#xff0c…

gcc -static 在centos stream8 和centos stream9中运行报错的解决办法

gcc -static 在centos stream8 和centos stream9中运行报错的解决办法&#xff1a; 报/usr/bin/ld: cannot find -lc 我们下载glibc-static&#xff1a; 选择x86_64的。 还有一个是libxcrypt-static&#xff0c;依旧在这个网站里搜。 rpm -ivh glibc-static-2.28-239.el8.x…

【数字三角形】

题目描述 上图给出了一个数字三角形。从三角形的顶部到底部有很多条不同的路径。对于每条路径&#xff0c;把路径上面的数加起来可以得到一个和&#xff0c;你的任务就是找到最大的和。 路径上的每一步只能从一个数走到下一层和它最近的左边的那个数或者右 边的那个数。此外…

线扫相机DALSA软件开发套件有哪些

Win10和Win7系统完整SDK目录截图&#xff1a; Sapera Configuration 缓存与内存管理&#xff0c;以及通信端口配置工具&#xff0c;部分功能等效于Detection(查找相机)内的Settings。 Sapera Log Viewer 打开Log Viewer后会显示之前发生过的所有与Sapera LT软件有关的运行信息…

无需专线、无需固定公网IP,各地安防数据如何高效上云?

某专注于安防领域的企业&#xff0c;供机场、金融、智慧大厦等行业&#xff0c;包括门禁系统、巡更系统、视频监控在内的整体解决方案。 在实际方案交付过程中&#xff0c;往往需要在多地分支机构分别部署相应的安防设备&#xff0c;并将产生的数据实时统一汇总至云平台进行管理…

【云服务器】对比传统服务器,为什么说云服务器更具优势?

个人主页&#xff1a;【&#x1f60a;个人主页】 系列专栏&#xff1a;【❤️其他领域】 文章目录 前言云服务器云服务器的优势成本可扩展性可靠性和安全性 总结 前言 2006年搜索引擎大会上&#xff0c;“云服务器”的概念孕育而生&#xff0c;时至今日云服务器与传统服务器的…

ChinaSoft 论坛巡礼 | 安全攸关软件的智能化开发方法论坛

2023年CCF中国软件大会&#xff08;CCF ChinaSoft 2023&#xff09;由CCF主办&#xff0c;CCF系统软件专委会、形式化方法专委会、软件工程专委会以及复旦大学联合承办&#xff0c;将于2023年12月1-3日在上海国际会议中心举行。 本次大会主题是“智能化软件创新推动数字经济与社…

CSS3媒体查询与页面自适应

2017年9月&#xff0c;W3C发布媒体查询(Media Query Level 4)候选推荐标准规范&#xff0c;它扩展了已经发布的媒体查询的功能。该规范用于CSS的media规则&#xff0c;可以为文档设定特定条件的样式&#xff0c;也可以用于HTML、JavaScript等语言。 1、媒体查询基础 媒体查询…

无限上下文,多级内存管理!突破ChatGPT等大语言模型上下文限制

目前&#xff0c;ChatGPT、Llama 2、文心一言等主流大语言模型&#xff0c;因技术架构的问题上下文输入一直受到限制&#xff0c;即便是Claude 最多只支持10万token输入&#xff0c;这对于解读上百页报告、书籍、论文来说非常不方便。 为了解决这一难题&#xff0c;加州伯克利…

【MATLAB源码-第67期】基于麻雀搜索算法(SSA)的无人机三维地图路径规划,输出最短路径和适应度曲线。

操作环境&#xff1a; MATLAB 2022a 1、算法描述 ​麻雀搜索算法&#xff08;Sparrow Search Algorithm, SSA&#xff09;是一种新颖的元启发式优化算法&#xff0c;它受到麻雀社会行为的启发。这种算法通过模拟麻雀的食物搜索行为和逃避天敌的策略来解决优化问题。SSA通过模…

Java基础之类型(内涵面试题)

目录 一、自动类型转换&#xff1a; 二、强制类型转换&#xff1a; 1.强制类型转换可能造成数据丢失&#xff08;溢出&#xff09;。 2.浮点型强转成整型&#xff0c;直接丢掉小数部分&#xff0c;保留整数部分返回。 三、自增、自减&#xff08;、--&#xff09;有关面试题…

在 Python 中创建奇数列表

我们将在本文中介绍在 Python 中创建奇数列表的不同方法。 Python 中的奇数 定义奇数有两种方法&#xff0c;第一种是整数不能被 2 整除时的情况。另一种是整数除以 2 时余数为 1 的情况。 例如&#xff0c;1、5、9、11、45等都是奇数。 从列表中获取奇数的方法有很多&#x…

AI“走深向实”,蚂蚁蚁盾在云栖大会发布实体产业「知识交互建模引擎」

数字化起步晚、数据分散稀疏、专业壁垒高、行业知识依赖「老师傅」&#xff0c;是很多传统产业智能化发展面临的难题。2023年云栖大会上&#xff0c;蚂蚁集团安全科技品牌蚁盾发布“知识交互建模引擎”&#xff0c;将实体产业知识与AI模型有机结合&#xff0c;助力企业最快10分…

IP路由配置

一、路由协议分类 路由协议是路由器之间维护路由表的规则,用于发现路由并生成路由表以指导报文转发。可分为: 通过链路层协议发现的直连路由通过网络管理员手动配置的静态路由通过动态路由协议发现的动态路由其中,动态路由根据作用范围分为: 内部网关协议(IGP):包括rip…

Java——接口类和抽象类的方法声明不需要加{}

在Java中&#xff0c;接口类和抽象类的方法声明是不需要加{}的。具体来说&#xff1a; 1. 接口类&#xff08;Interface&#xff09;&#xff1a;接口类定义了一组方法的规范&#xff0c;没有具体的方法实现。在接口中&#xff0c;方法声明只包含方法名、参数列表和返回类型&a…