【Java ee初阶】jvm(3)

一、双亲委派机制(类加载机制中,最经常考到的问题)

类加载的第一个环节中,根据类的全限定类名(包名+类名)找到对应的.class文件的过程。

JVM中进行类加载的操作,需要以来内部的模块“类加载器”(class loader)

JVM自带了三种类加载器

Bootstrap ClassLoader 负责在Java的标准库中进行查找

ExtensionClassLoader  负责在Java的扩展库中进行查找

ApplicationClassLoader 负责在Java的第三方库/当前项目中进行查找

其中,Java的扩展库是JDK自带的,但是不是标准约定的库,是JDK的厂商自行扩展的功能。现在很少涉及到,一般都是使用第三方库。

Java的官方(Oracle)推出Java的标准文档,其他的厂商就会依据官方的文档,开发对应的JDK(官方确实也开发了JDK,还有一些第三方的,比如知名的OpenJDK,比如知名大厂也会有自己版本的JDK)

不同厂商,都能保证,标准约定的功能都是包含的,并且表现一致。但是这些厂商也会根据需要,扩展出一些功能出来。

这三类加载器之间,存在“父子关系”(不是父类子类,继承关系),每个类加载器中有一个parent这样的属性,保存了自己的父亲是谁。这是在JVM的源码中已经写死的。

双亲委派模型的目的,是为了确保三个类加载的优先级:标准库优先加载,第三方库/当前项目类最后加载。比如自己写一个类,和标准库恰好重复了。java.lang.String。此时JVM保证加载的仍然是标准库的String,而不是你自己写的。

双亲委派模型也是可以打破的。程序员在特定场景下,也可以实现自己的类加载器(实现库/框架 可能涉及到),自己实现的类加载器可以让他遵守,也可以不遵守。

二、JVM的垃圾回收机制 GC

C/C++ 这样的编程语言中,申请内存的时候,是需要用完了手动进行释放的

C 申请内存
1) 局部变量
2) 全局变量 不需要手动释放
3) 动态申请 malloc 通过 free 进行释放的

C++ 申请内存
1) 局部变量
2) 全局变量 / 静态变量
3) 动态申请 new 通过 delete 进行释放

这样的释放操作,容易遗忘(执行不到)就会导致 “内存泄露”

malloc
free

逻辑代码
1) 条件判定 触发 return
2) 抛出异常

很多编程语言,引入了 垃圾回收 机制
不需要程序员写代码手动释放内存,会有专门的逻辑,帮助自动进行释放
垃圾回收,大大的解放了程序员,提高了开发效率

Java, Python, Go, PHP, JS.... 大多数主流语言都包含 GC 功能

为啥 C/C++ 没有引入 GC 呢?
C++ 的设计的核心理念,有两个(C++ 的红线)
1) 和 C 兼容 (C 语言写的代码,用 C++ 编译器可以正常编译运行的)
2) 把性能发挥到极致
隔壁会有很多的技巧 提高 "性能"
++i 代替 i++
通过返回 右值引用 代替返回值对象
通过引用传参代替值传参
通过 constexpr 增加编译期做的工作,减少运行时开销
...................

引入 GC 会影响性能, 引入了额外的运行时开销。
很早之前,C++ 的标准委员会讨论这个事情。
C++ 引入了 "智能指针",可以一定程度的解决内存泄露的问题。(虽然可用性,远不如 GC,总比 C 语言啥都不做,直接摆烂强

在对性能有要求的开发场景中 C++ 是无可替代的
AI
游戏引擎
搜索引擎 (现在 java 性能也赶上来不少,也有 java 实现的版本了...)
交易系统 (股票,基金,外汇,期货...)
操作系统级的开发
...................

挑战者,Rust,尝试挑战 C++ 的生态位
走高性能的路线

主打优势,能够很好的应对内存错误问题
(内存泄露,内存访问越界...)
Rust 通过特殊的语法,在编译期做检查的。
假设代码写出内存泄露,编译通过不了
目前,Rust 发展下来,也变的语法非常复杂了

为什么GC 会影响执行效率?因为触发 GC 的时候,可能会涉及到 STW 问题

stop the world 世界都停止

1. GC 回收的内存区域是哪个部分呢?
IVM
程序计数器
元数据区

堆 => GC 主要回收这个区域

2. GC 的目的是为了释放内存,是以字节为单位“释放”嘛?
不是的,而是以“对象为单位”

正在使用的内存 不回收
不再使用(尚未回收) 不回收
没有使用的区域 回收

按照对象为维度进行回收,更简单方便。
如果是按照“字节维度”,就可能针对每个对象都得描述出哪部分需要回收,哪部分不需要。比较麻烦了。

堆上的内存 => new 的对象

3. 如何回收?
1) 找出垃圾,区分出哪些对象是垃圾(后续代码不再使用)
2) 释放这些垃圾对象的内存

如何“找出垃圾” ?

由于在 Java 中使用对象,都是通过 “引用” 来进行的,使用对象,无非是使用对象的属性/方法,都要通过对象的引用进行。.前面的部分就是指向对象的引用。

如果一个对象已经没有任何引用指向它了,此时这个对象就注定无法被使用了。

判断一个对象是否是垃圾这个问题比较抽象,因此我们将其转换成判断是否有引用指向这个对象,这样子问题就比较具体了。

JVM 内部是有一些办法可以做到的以上这种解决方案的,周大佬 《深入理解 Java 虚拟机》 这本书介绍了两种方案:

*面试的时候,区分好,看面试官是咋问的:
1) 让你介绍下 垃圾回收 中如何判定对象是垃圾的 两个方案都可以介绍
2) 让你介绍 JVM 中如何判定对象是垃圾的 别说引用计数

1) 引用计数(Java 没有使用,Python,PHP... 采用的方案)
简单粗暴的方案。
给每个对象都分配了一个 “计数器”

1. 引用计数 [Java 没有使用,Python,PHP... 采用的方案]
简单粗暴的方案。
给每个对象都分配了一个 “计数器”

Test a = new Test();
Test b = a;
a = null;
b = null;
当引用计数为 0,此时对象就没有任何引用指向了。
对象就是 垃圾了

Python / PHP 等语言
会搭配其他垃圾回收机制,识别当前的引用是否构成循环引用

两个弊端
1) 消耗额外的内存空间较大。
如果对象本身很小(就 4 个字节)
计数器占了俩字节,相当于额外的内存空间多了 50%
2) 循环引用问题 (类似于死锁)

class Test {
    Test t;
}
Test a = new Test();
Test b = new Test();
a.t = b
b.t = a

此时,这俩对象的引用计数是 1,不能释放。
但是,这俩对象却无法通过任何引用来访问到。

AB 相互证明对方不是垃圾
实际上 AB 都是垃圾

以下是图中的文字内容:

```
2. 可达性分析 [Java 使用的方案]
在 Java 代码中,每个 “可访问的对象” 一定是可以通过一系列的引用操作,访问到的。

Node build() {
    Node a = new Node();
    Node b = new Node();
    Node c = new Node();
    Node d = new Node();
    Node e = new Node();
    Node f = new Node();
    Node g = new Node();
    a.left = b;
    a.right = c;
    b.left = d;
    b.right = e;
    e.left = g;
    c.right = f;
    return a;
}

Node root = build();  构建二叉树
此时通过 root 这个引用,是可以访问到这个树上的任何一个对象的


此时通过 root 这个引用,是可以访问到这个树上的任何一个对象的

root => a
root.left => b
root.left.left => d
root.left.right.left => g
...........

假设,写 root.right.right = null
这样的代码会使 f 无法被访问到(f 已经没有引用指向了)

假设,写 root.right = null
这样的代码使 c 不可达。由于 f 必须依赖 c,f 也一起不可达

JVM 安排专门的线程,负责上述的 “扫描” 的过程
会从一些特殊的引用开始扫描 (GC roots)
1. 栈上的局部变量 (引用类型)
2. 常量池里指向的对象 (final 修饰的,引用类型)
3. 元数据区 (静态成员,引用类型)

这三组里可能有很多变量
以这些变量为起点,尽可能的往里访问所有可能被访问到的对象
但凡被访问到的对象,都 “标记为可达”
JVM 又能够知道所有的对象列表,去掉 “标记为可达的”,剩下的就是垃圾了

不引入额外的内存空间
但是需要消耗较多的时间,进行上述扫描过程,这些过程中也是容易触发STW的

(时间换了空间)
另外这里也不会涉及到 “循环引用”

如何释放垃圾 (回收内存)
关于内存回收,涉及到几种算法.

1. 标记 - 清除

标记,就是可达性分析,找到垃圾的过程.
清除,直接释放这部分的内存(相当于直接调用 free / delete 释放这个内存给操作系统)

存在内存碎片问题

总的空闲内存空间,是比较多的 (一共 4MB)
但是这些空闲空间,不连续.

在申请内存的时候,都是在申请连续的内存空间
当尝试申请 2MB 的内存时候,就会申请失败

2. 复制算法解 决内存碎片

把不是垃圾的对象,复制到另外一侧
把整个空间都释放掉

很好的解决了内存碎片问题。
弊端:
1. 内存浪费比较多
2. 如果存活的对象比较多/比较大,复制开销非常明显的

3. 标记 - 整理 类似于顺序表删除元素 - 搬运元素

4. 分代回收 (综合方案), 把上述几个方案,结合起来,扬长避短

整个堆空间,分成 "新生代" "老年代"
年轻对象 年老对象
年龄:一个对象经过垃圾回收扫描线程的轮次

对于年轻对象来说,是容易成为垃圾的。
年老对象,则不容易成为垃圾

可达性分析中,JVM 会不停使用线程扫描这些对象是否是垃圾。每隔一定时间,扫描一次。如果一个对象扫描一次,不是垃圾,年龄就 + 1。一般来说年龄超过 15 (可以配) 的就进入老年代。

"要死早死了"
比如 C 语言,已经存在了 50 年了,可以遇见到这个 C 语言还有很大的希望再活 50 年
> 和 C 语言同时期的 C++ 语言,都死的差不多...

刚创建的新鲜对象放到伊甸区。如果对象活过一轮 GC,进入幸存区。

新对象,大多数是生命周期非常短的 “朝生夕死”,经验规律。这俩幸存区,同一时刻使用一个(相当于复制算法,分出两个部分)。每次经过一轮 GC,都会淘汰掉幸存区中的一大部分对象,把存活的对象和伊甸区中新存活下来的对象,复制算法拷贝到另一个幸存区。新生代非常适合复制算法的。

如果这个对象在新生代中存活多轮之后,就会进入老年代。老年代的对象由于生命周期大概率很长,没有必要频繁扫描。如果这个对象非常大,不适合使用复制算法了。直接进入老年代
老年代回收内存采取的是 标记-清除 / 标记-整理(取决于垃圾回收器的具体实现了)

主流垃圾收集器详解

1.G1垃圾收集器(GarbageFirst)

定位:自Java11起成为默认收集器,是目前最主流的垃圾收集器。

核心特点:

采用分区堆(Region)设计,将堆内存划分为多个大小相等的块(通常为1MB~32MB)。

通过优先回收垃圾最多的Region("GarbageFirst"策略)实现高效回收。

支持大内存(几十GB级别),同时保持可控的STW(StopTheWorld)停顿时间。

2.ZGC垃圾收集器(ZGarbageCollector)

定位:新一代低延迟收集器,未来可能逐步取代G1。

核心特点:

突破性低延迟,STW时间可控制在1ms以内。

同样采用分区堆设计,但通过染色指针(ColoredPointers)和读屏障(LoadBarriers)技术实现并发标记与整理。

支持超大堆内存(TB级别),适合现代高性能应用。

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

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

相关文章

wps excel将表格输出pdf时所有列在一张纸上

记录:wps excel将表格输出pdf时所有列在一张纸上 1,调整缩放比例,或选择将所有列打印在一页 2,将表格的所有铺满到这套虚线

分布式微服务系统架构第134集:笔记1运维服务器经验,高并发,大数据量系统

加群联系作者vx:xiaoda0423 仓库地址:https://webvueblog.github.io/JavaPlusDoc/ https://1024bat.cn/ https://github.com/webVueBlog/fastapi_plus https://webvueblog.github.io/JavaPlusDoc/ ✅ 一、查看端口是否被占用的常用命令 1️⃣ lsof 命令&…

IS-IS 中间系统到中间系统

前言: 中间系统到中间系统IS-IS(Intermediate System to Intermediate System)属于内部网关协议IGP(Interior Gateway Protocol),用于自治系统内部 IS-IS也是一种链路状态协议,使用最短路径优先…

前端安全:XSS、CSRF 防御与最佳实践

引言 随着互联网应用的普及,前端安全问题日益凸显。作为开发者,了解并防范常见的安全威胁至关重要。本文将深入探讨两种最常见的前端安全威胁:跨站脚本攻击(XSS)和跨站请求伪造(CSRF)&#xff…

uniapp 弹窗封装(上、下、左、右、中五个方位)

无脑复制即可&#xff01;&#xff01;&#xff01; <template><view><viewv-if"mask"class"tui-drawer-mask":class"{ tui-drawer-mask_show: visible }":style"{ zIndex: maskZIndex }"tap"handleMaskClick&qu…

Axure制作可视化大屏动态滚动列表教程

在可视化大屏设计中&#xff0c;动态滚动列表是一种常见且实用的展示方式&#xff0c;能够有效地展示大量信息。本文将详细介绍如何使用Axure制作一个动态滚动的列表展示模块。 一、准备工作 打开Axure软件&#xff1a;确保你已经安装并打开了Axure RP软件。创建新项目&#x…

零基础玩转Apache Superset可视化部署

根据官方Quick Start Guide&#xff0c;你可以按照以下步骤进行部署&#xff1a; 1. 确认环境2. 获取代码3. 获取官方最新代码4. 启动服务5. 访问Superset Web界面6. 接入数据源 前提条件&#xff1a; dockerdocker compose 1. 确认环境 安装Docker和Docker Compose 确保你…

服务器数据恢复—XFS文件系统分区消失的数据恢复案例

服务器数据恢复环境&故障&#xff1a; 服务器上有一组由raid卡组建的raid5磁盘阵列。上层安装linux才做系统&#xff0c;采用XFS文件系统&#xff0c;划分了3个分区。 管理员将服务器的操作系统重装后&#xff0c;发现服务器上的分区发生了改变&#xff1a;一个分区消失&am…

2025/5/18

继续研究一下大佬的RAG项目。开始我的碎碎念。 RAG可以分成两部分&#xff1a;一个是问答&#xff0c;一个是数据处理。 问答是人提问&#xff0c;然后查数据库&#xff0c;把查的东西用大模型组织成人话&#xff0c;回答人的提问。 数据处理是把当下知识库里的东西&#xf…

在 Vue 中插入 B 站视频

前言 在 Vue 项目中&#xff0c;有时我们需要嵌入 B 站视频来丰富页面内容&#xff0c;为用户提供更直观的信息展示。本文将详细介绍在 Vue 中插入 B 站视频的多种方法。 使用<iframe>标签直接嵌入,<iframe>标签是一种简单直接的方式&#xff0c;可将 B 站视频嵌…

OpenCv高阶(八)——摄像头调用、摄像头OCR

文章目录 前言一、摄像头调用通用方法1、导入必要的库2、创建摄像头接口 二、摄像头OCR1.引入库2、定义函数&#xff08;1&#xff09;定义显示opencv显示函数&#xff08;2&#xff09;保持宽高比的缩放函数&#xff08;3&#xff09;坐标点排序函数&#xff08;4&#xff09;…

特斯拉虚拟电厂:能源互联网时代的分布式革命

在双碳目标与能源转型的双重驱动下&#xff0c;特斯拉虚拟电厂&#xff08;Virtual Power Plant, VPP&#xff09;通过数字孪生技术与能源系统的深度融合&#xff0c;重构了传统电力系统的运行范式。本文从系统架构、工程实践、技术挑战三个维度&#xff0c;深度解析这一颠覆性…

【漫话机器学习系列】258.拐点(Inflection Point)

拐点&#xff08;Inflection Point&#xff09;详解&#xff1a;定义、原理与应用 在数学分析与数据建模中&#xff0c;“拐点&#xff08;Inflection Point&#xff09;”是一个非常重要的概念。今天这篇文章&#xff0c;我们将结合图示&#xff0c;深入理解拐点的定义、数学…

语音识别——声纹识别

通过将说话人的声音与数据库中的记录声音进行比对&#xff0c;判断说话人是否为数据库白名单中的同一人&#xff0c;从而完成语音验证。目前&#xff0c;3D-Speaker 声纹验证的效果较为出色。 3D-Speaker 是一个开源工具包&#xff0c;可用于单模态和多模态的说话人验证、说话…

DeepSeek 赋能军事:重塑现代战争形态的科技密码

目录 一、引言&#xff1a;AI 浪潮下的军事变革与 DeepSeek 崛起二、DeepSeek 技术原理与特性剖析2.1 核心技术架构2.2 独特优势 三、DeepSeek 在军事侦察中的应用3.1 海量数据快速处理3.2 精准目标识别追踪3.3 预测潜在威胁 四、DeepSeek 在军事指挥决策中的应用4.1 战场态势实…

uWSGI是什么?

uWSGI 是一个功能强大的应用服务器&#xff0c;专为部署高性能 Web 应用设计&#xff0c;尤其适合 Python 生态系统。以下是对其核心介绍及适用场景的总结&#xff1a; uWSGI 是什么&#xff1f; uWSGI 是一个实现了 WSGI&#xff08;Web Server Gateway Interface&#xff09…

Digi XBee XR 系列介绍

Digi 延续了 20 多年来亚 GHz 射频模块的传统&#xff0c;推出了 Digi XBee XR 系列远距离模块&#xff0c;包括 Digi XBee XR 900 - 已通过多个地区的预先认证 - 以及 Digi XBee XR 868 - 已通过欧洲地区应用的预先认证。 这些先进的射频模块专为远距离抗干扰无线通信而设计。…

RabbitMq C++客户端的使用

介绍 RabbitMQ 是一个开源的消息代理和队列服务器&#xff0c;用于在分布式系统之间传递消息。它实现了高级消息队列协议(AMQP)&#xff0c;同时也支持其他协议如 STOMP、MQTT 等。 核心概念 Producer(生产者): 发送消息的应用程序 Consumer(消费者): 接收消息的应用程序 Q…

HTML 中的 input 标签详解

HTML 中的 input 标签详解 一、基础概念 1. 定义与作用 HTML 中的 <input> 标签是表单元素的核心组件&#xff0c;用于创建各种用户输入字段。作为一个空标签&#xff08;没有闭合标签&#xff09;&#xff0c;它通过 type 属性来决定呈现何种输入控件&#xff0c;是实…

基于Piecewise Jerk Speed Optimizer的速度规划算法(附ROS C++/Python仿真)

目录 1 时空解耦运动规划2 PJSO速度规划原理2.1 优化变量2.2 代价函数2.3 约束条件2.4 二次规划形式 3 算法仿真3.1 ROS C仿真3.2 Python仿真 1 时空解耦运动规划 在自主移动系统的运动规划体系中&#xff0c;时空解耦的递进式架构因其高效性与工程可实现性被广泛采用。这一架…