深入浅出:JVM 的架构与运行机制
一、什么是JVM
1、什么是JDK、JRE、JVM
- JDK是 Java语言的软件开发工具包,也是整个java开发的核心,它包含了JRE和开发工具包
- JRE,Java运行环境,包含了JVM和Java的核心类库(Java API)
- JVM,Java虚拟机,它是运行在操作系统之上的,它与硬件没有直接的交互
2、说的通俗点,JVM到底是什么?
JVM 是一个虚拟的计算机,但它并不是真正的物理机器,而是在你的电脑上运行的一个软件。它的主要任务是执行 Java 程序。你可以把它想象成一个翻译官,负责把 Java 程序的代码翻译成你的电脑能够理解并执行的指令。
Java 语言有一个著名的口号:“一次编写,到处运行”。所谓“一次编码,随处运行“正是基于不同系统下的jvm帮你掩盖了系统之间接口的差异:
jdk是开发人员的工具包,它包含了java的运行环境和虚拟机,而一次编写到处运行就是基于jvm
3、总结
JVM就是一套软件,不管在什么平台上都可以安装,安装好之后,就可以运行我们的Java程序,并且是在任意平台上都可以,平台之间的接口差异那都是JVM去做的,无需我们关心。
二、JVM整体架构
1、Java程序如何被运行的
1.源码编译:通过Java源码编译器将Java代码编译成JVM字节码(.class文件)
2.类加载:通过ClassLoader及其子类来完成JVM的类加载
3.类执行:字节码被装入内存,进入JVM虚拟机,被解释器解释执行
2、JVM模型
由上面的图可以看出,JVM虚拟机中主要是由三部分构成,分别是类加载子系统、运行时数据区、执行引擎。
类加载子系统
JVM把描述类的数据从Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被JVM直接使用的Java类型。
运行时数据区
JVM在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域。
这些区域有各自的用途,以及创建和销毁的时间,有的区域随着虚拟机进程的启动而一直存在,有些区域则是依赖用户线程的启动和结束而建立和销毁。
执行引擎
执行引擎用于执行JVM字节码指令,主要有两种方式,分别是解释执行和编译执行,区别在于,解释执行是在执行时翻译成虚拟机指令执行,而编译执行是在执行之前先进行编译再执行。
解释执行启动快,执行效率低。编译执行,启动慢,执行效率高。
垃圾回收器就是自动管理运行数据区的内存,将无用的内存占用进行清除,释放内存资源。
本地方法库、本地库接口
在jdk的底层中,有一些实现是需要调用本地方法完成的(使用c或c++写的方法),就是通过本地库接口调用完成的。比如:System.currentTimeMillis()方法。、
三、class到底长什么样子?
这是我们的测试案例
/*
* 基本类结构
* */
public class ClassStruct {private static String name = "JVM";private static final int age = 18;public static void main(String[] args) {System.out.println("Hello " + name);}}
. java文件编译之后,就会产生一个.class文件
我们将上面这个编译后的.class文件打开,就长下面这样
class文件是一个二进制文件,转化后是16进制展示,实际上class文件就是一张表,它由以下数据项构成,这些数据项从头到尾严格按照以下顺序排列:
下面我们对这些数据项逐一介绍
魔数
固定的"CAFEBABE",巧记"咖啡宝宝"
版本号
34,换成10进制就是52
jdk的版本标记映射关系:
可以看到就是采用的jdk8进行编译的
常量池
常量池记录了jvm内的一堆常量信息,这部分由 【2个字节的常量池计数器】 + 【n个cp_info结构】组成
常量池中主要存放两大类常量:字面量(Literal)和符号引用(Symbolic References)。 字面量比较接近于 Java 语言层面的常量概念,如文本字符串、声明为 final 的常量值等。 而符号引用则属于编译原理方面的概念,包括了下面三类常量: 类和接口的全限定名(Fully Qualified Name)、字段的名称和描述符(Descriptor)、方法的名称和描述符
常量池计数器
标注后面有多少个,对应个数的cp_info
CP_INFO
cp_info有多种类型:
- 直接类型,存的就是当前值,这种像Integer,Long等长度都是确定的
- 引用类型,存的是指向其他位置的指针
我们可以看看真实的CP_INFO条目的内容
javap -v ClassStruct.class
Utf8对应的就是CONSTANT_Utf8_INFO,String对应的就是CONSTANT_String_INFO
其他信息
常量池之后,是紧挨的一系列信息,这些信息大同小异,无非就是值、或者引用
- 访问标记:public abstract 等信息
- 类索引,class类型,最终指向一个utf8,标记当前类的名字
- 父类,同上
- 接口,2字节记录数量,后面记录多个接口类型
- 接下来是字段、方法、属性,都是2字节记录后面多少个,后面紧跟对应的结构体类型
四、运行时数据区深度剖析
字节码只是一个二进制文件存放在那里。要想在jvm里跑起来,先得有个运行的内存环境。
也就是我们所说的jvm运行时数据区。
1、运行时数据区的内存分布
运行时数据区是jvm中最为重要的部分,执行引擎频繁操作的就是它。类的初始化,以及后面我们讲的对象空间的分配、垃圾的回收都是在这块区域发生的。
根据《Java虚拟机规范》中的规定,在运行时数据区将内存细分为几个部分
线程私有的:Java虚拟机栈(Java Virtual Machine Stack)、程序计数器(Program Counter Register)、本地方法栈(Native Method Stacks)
大家共享的:方法区(Method Area)、Java堆区(Java Heap)
接下来我们分块详细来解读,每一块是做什么的,如果溢出了会发生什么事情
2、程序计数器
程序计数器
- 每个线程一个。是一块较小的内存空间,它表示当前线程执行的字节码指令的地址。
- 字节码解释器工作时,通过改变这个计数器的值来选取下一条需要执行的字节码指令,所以整个程序无论是分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。
- 由于线程是多条并行执行的,互相之间执行到哪条指令是不一样的,所以每条线程都需要有一个独立的程序计数器,各条线程之间计数器互不影响,独立存储,我们称这类内存区域为“线程私有”的内存。
- 如果是native方法,这里为空
总结:程序计数器就是记录着当前线程的所有指令,以及当前执行到哪个指令了,下一步该执行哪个指令
在虚拟机规范中,没有对这块区域设定内存溢出规范,也是唯一一个不会溢出的区域
因为它不会溢出,所以我们没有办法给它造一个,但是从class类上可以找到痕迹。
回顾上面javap的反汇编,其中code所对应的编号就可以理解为计数器中所记录的执行编号。
3、虚拟机栈
JVM栈呢,包含许许多多的栈帧,每个栈帧包含四个部分:局部变量、操作数栈、动态链接、方法出口。
- 线程私有:每个线程都有自己的 JVM 栈,线程之间不能共享栈中的数据。
- 生命周期:JVM 栈的生命周期与线程相同,线程启动时创建,线程结束时销毁。
- 栈帧:每个方法调用都会创建一个新的栈帧,方法执行完毕后,对应的栈帧会被弹出栈。
JVM栈呢通常会产生两种异常
-
StackOverflowError:
- 当线程请求的栈深度大于 JVM 所允许的最大深度时,抛出
StackOverflowError
。 - 常见原因包括递归调用过深、线程栈大小设置不当等。
- 当线程请求的栈深度大于 JVM 所允许的最大深度时,抛出
-
OutOfMemoryError:
- 当线程栈所需的内存超过 JVM 堆内存限制时,抛出
OutOfMemoryError
。 - 常见原因包括线程数量过多、单个线程栈大小过大等。
- 当线程栈所需的内存超过 JVM 堆内存限制时,抛出
4、本地方法栈
- 本地方法栈的功能和特点类似于虚拟机栈,均具有线程隔离的特点
- 不同的是,本地方法栈服务的对象是JVM执行的native方法,而虚拟机栈服务的是JVM执行的java方法
- 虚拟机规范里对这块所用的语言、数据结构、没有强制规定,虚拟机可以自由实现它
- 甚至,hotspot把它和虚拟机栈合并成了1个
和虚拟机栈一样,也是两个:
如果是创建的栈的深度大于虚拟机允许的深度,抛出 StackOverFlowError
内存申请不够的时候,抛出 OutOfMemoryError
5、堆区
与上面的3个不同,堆是所有线程共享的!所谓的线程安全不安全也是出自这里。
在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,Java世界里“几乎”所有的对象实例都在这里分配内存。
Java堆是垃圾收集器管理的内存区域,因此它也被称作“GC堆”,这就是我们做JVM调优的重点区域部分。
堆区我们要分两个版本介绍,jdk1.7和1.8版本的略有不同
1.7版本的堆区
-
Young 年轻区(代)
Young区被划分为三部分,Eden区和两个大小严格相同的Survivor区
其中,Survivor 区分为两个部分,通常称为 S0 和 S1。每次GC 后,Eden 区中存活的对象会被移动到其中一个 Survivor 区,而另一个 Survivor 区则被清空。两个 Survivor 区轮流使用。
在Eden区间变满的时候, GC就会将存活的对象移到空闲的Survivor区间中,根据JVM的策略,在经过几次(有一个阈值,默认15)垃圾收集后,仍然存活于Survivor的对象将被移动到下面的Tenured区间。
-
Tenured 年老区
Tenured区主要保存生命周期长的对象,一般是一些老的对象,当一些对象在Young复制转移一定的次数以后,对象就会被转移到Tenured区,一般如果系统中用了application级别的缓存,缓存中的对象往往会被转移到这一区间。
-
Perm 永久区
现在已经成为历史,Perm代主要保存类信息,class,method,filed等对象,这部份的空间一般不会溢出,除非一次性加载了很多的类
1.8版本的堆区
jdk1.8的内存模型是由2部分组成,年轻代 + 年老代。永久代被干掉,换成了Metaspace(元数据空间)
需要特别说明的是:Metaspace所占用的内存空间不是在虚拟机内部,而是在本地内存空间中(使用的是操作系统的内存)
Mataspace和永久区的区别
堆区的内存分配策略
-
对象优先在 Eden 分配:
- 新创建的对象首先分配在 Eden 区。
- 如果 Eden 区没有足够的空间,会触发 Minor GC。
-
大对象直接进入老年代:
- 大对象(如长字符串或大数组)可以直接进入老年代,以减少新生代的碎片化。
-
长期存活的对象进入老年代:
- 经过多次 Minor GC 仍然存活的对象会被晋升到老年代。
-
动态对象年龄判定:
- 如果 Survivor 区中相同年龄的所有对象大小总和大于 Survivor 区的一半,年龄大于或等于该年龄的对象可以直接进入老年代。
6、方法区
用于存储已被虚拟机加载的类信息、常量池、静态变量、即时编译器编译后的代码等数据。在 JDK 8 及之前的版本中,方法区通常被称为永久代(PermGen),而在 JDK 8 及之后的版本中,方法区被移到了元空间(Metaspace)
所以方法区只是一个逻辑概念,存放在哪里由虚拟机自己去决定
五、类加载器
通过字节码,我们了解了class文件的结构
通过运行数据区,我们了解了jvm内部的内存划分及结构
接下来,让我们看看,字节码怎么进入jvm的内存空间,各自进入那个空间,以及怎么跑起来。
1、加载
类的加载就是将class文件中的二进制数据读取到内存中,然后将该字节流所代表的静态数据结构转化为方法区中运行的数据结构,并且在堆内存中生成一个java.lang.Class对象作为访问方法区数据结构的入口。
Java中有哪些类加载器呢?
jvm提供了3个系统加载器,分别是Bootstrp loader、ExtClassLoader 、AppClassLoader
这三个加载器互相成父子继承关系
Bootstrap加载器是用C++语言写的,它在Java虚拟机启动后初始化
它主要负责加载以下路径的文件:
-
%JAVA_HOME%/jre/lib/*.jar
-
%JAVA_HOME%/jre/classes/*
-
-Xbootclasspath参数指定的路径
ExtClassLoader是用Java写的,具体来说就是 sun.misc.Launcher$ExtClassLoader
ExtClassLoader主要加载:
- %JAVA_HOME%/jre/lib/ext/*
- ext下的所有classes目录
- java.ext.dirs系统变量指定的路径中类库
AppClassLoader也是用Java写成的,它的实现类是 sun.misc.Launcher$AppClassLoader,另外我们知道ClassLoader中有个getSystemClassLoader方法,此方法返回的就是它。
- 负责加载 -classpath 所指定的位置的类或者是jar文档
- 也是Java程序默认的类加载器
双亲委派机制
类加载器加载某个类的时候,因为有多个加载器,甚至可以有各种自定义的,他们呈父子继承关系。
这给人一种印象,子类的加载会覆盖父类,其实恰恰相反!
与普通类继承属性不同,类加载器会优先调父类的load方法,如果父类能加载,直接用父类的,否则最后一步才是自己尝试加载,从源代码上可以验证。
我们看一下ClassLoader.loadClass()方法:
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {synchronized (getClassLoadingLock(name)) { // 首先,检测是否已经加载 Class<?> c = findLoadedClass(name);if (c == null) {//如果没有加载,开始按如下规则执行:long t0 = System.nanoTime();try {if (parent != null) { //重点!父加载器不为空则调用父加载器的loadClass c = parent.loadClass(name, false);} else { //父加载器为空则调用Bootstrap Classloader c = findBootstrapClassOrNull(name);}} catch (ClassNotFoundException e) { }if (c == null) {long t1 = System.nanoTime(); //父加载器没有找到,则调用findclass,自己查找并加载c = findClass(name); }}if (resolve) { resolveClass(c);}return c;}}
为什么要有双亲委派机制
-
保证类的唯一性:通过双亲委派机制,确保了类的唯一性。例如,Java 核心类库中的类(如
java.lang.Object
)只会被启动类加载器加载一次,避免了不同类加载器加载同一个类而导致的冲突和不一致问题。 -
防止类的重复加载:每个类加载器都有自己的命名空间,通过双亲委派机制,确保了同一个类在不同的类加载器中只加载一次,提高了类加载的效率,减少了内存开销。
-
增强安全性:双亲委派机制确保了核心类库的类不会被用户自定义的类加载器加载,从而防止了恶意代码替换核心类库中的关键类,增强了系统的安全性。
2、验证
加载完成后,class里定义的类结构就进入了内存的方法区。
而接下来,验证是连接阶段的第一步。实际上,验证和上面的加载是交互进行的(比如class文件格式验证)。
文件格式的验证
这个好理解,就是验证加载的字节码是不是符合规范
- 是不是CAFEBABYE开头
- 主次版本号是否在当前jvm虚拟机可运行的范围内
- 常量池类型对不对
- 有没有其他不可识别的信息
- ……等
元数据验证
到java语法级别了。这个阶段主要验证属性、字段、类关系、方法等是否合规
- 是否有父类?除了Object其他类必须有
- 是否继承了不该被继承的类,比如final
- 是不是抽象类,是的话,方法都完备了没
- 字段有没问题?是不是覆盖了父类里的final
- ……等
字节码验证
最复杂的一个阶段。
等等,字节码前面不是验证过了吗?咋还要验证?
上面的验证是基本字节表格式验证。而这里主要验证class里定义的方法,看方法内部的code是否合法。
- 类型转换是不是有问题?
- 指令是否跳到了方法外的字节码上?
- ……
符号引用验证
最后一个阶段。
这个阶段也好理解,我们上面的字节码解读时,知道字节码里有的是直接引用,有的是指向了其他的字节码地址。
而符号引用验证的就是,这些引用的对应的内容是否合法。
- utf8里记了某个类的名字,这个类存在不?
- 方法或字段引用,这些方法在对应的类里存在不存在?
- 类、字段、方法等上面的可见性是否合法
- ……
3、准备
这个阶段为class中定义的各种类变量分配内存,并赋初始值。
所做的事情好理解,但是要注意几点:
- 类变量 = 静态变量
- 实例变量 = 实例化new出来的那些
理论上这些值都在方法区里,但是注意,方法区本身就是一个逻辑概念。
1.6里,在永久代
1.8以后,静态类变量如果是一个对象,其实它在堆里。这个上面我们讲方法区的时候验证过。
//普通类变量:在准备阶段为它开了内存空间,但是它的value是int的初始值,也就是 0!
//而真正的123赋值,是在类构造器,也就是下面的初始化阶段
public static int a = 123;//final修饰的类变量,编译成字节码后,是一个ConstantValue类型
//这种类型,在准备阶段,直接给定值123,后期也没有二次初始化一说
public static final int b = 123;
4、解析
解析阶段开始解析类之间的关系,需要关联的类也要被加载。
这涉及到:
- 类或接口的解析:类相关的父子继承,实现的接口都有哪些类型?
- 字段的解析:字段对应的类型?
- 方法的解析:方法的参数、返回值、关联了哪些类型
- 接口方法的解析:接口上的类型?
5、初始化
最后一个步骤,经过这个步骤后,类信息完全进入了jvm内存,直到它被垃圾回收器回收。
前面几个阶段都是虚拟机来搞定的。我们也干涉不了,从代码上只能遵从它的语法要求。
而这个阶段,是赋值,才是我们应用程序中编写的有主导权的地方
在准备阶段,jvm已经初始化了对应的内存空间,final也有了自己的值。但是其他类变量,是在这里赋值完成的。
也就是我们说的:
public static int a = 123;
注意:
1)类变量与实例变量的区分
注意一件事情!
这里所说的初始化是一个class类加载到内存的过程,所谓的初始化值得是类里定义的类变量。也就是静态变量。
这个初始化要和new一个类区分开来。new的是实例变量,是在执行阶段才创建的。
2)实例变量创建的过程
当我们在方法里写了一段代码,执行过程中,要new一个类的时候,会发生以下事情:
- 在方法区中找到对应类型的类信息
- 在当前方法栈帧的本地变量表中放置一个reference指针
- 在堆中开辟一块空间,放这个对象的实例
- 将指针指向堆里对象的地址,完工!
相关文章:

深入浅出:JVM 的架构与运行机制
一、什么是JVM 1、什么是JDK、JRE、JVM JDK是 Java语言的软件开发工具包,也是整个java开发的核心,它包含了JRE和开发工具包JRE,Java运行环境,包含了JVM和Java的核心类库(Java API)JVM,Java虚拟…...

如何在 Eclipse 中调试ABAP程序
原文链接:Debugging an ABAP Program ADT 中的调试器是一个重要的诊断工具,可用于分析 ABAP 应用程序。 使用调试器,您可以通过在运行时 Debug 单步执行(F5)程序来确定程序无法正常工作的原因。这使您可以看到正在执…...

websocket是什么?
一、定义 Websocket是一种在单个TCP连接上进行全双工通信的协议,它允许服务器主动向客户端推送数据,而不需要客户端不断的轮询服务器来获取数据 与http协议不同,http是一种无状态的,请求,响应模式的协议(单向通信)&a…...

Java项目实战II基于微信小程序的图书馆自习室座位预约平台(开发文档+数据库+源码)
目录 一、前言 二、技术介绍 三、系统实现 四、文档参考 五、核心代码 六、源码获取 全栈码农以及毕业设计实战开发,CSDN平台Java领域新星创作者,专注于大学生项目实战开发、讲解和毕业答疑辅导。 一、前言 在知识爆炸的时代,图书馆和…...

5.算法移植第六篇YOLOV5 /onnx模型转换成rknn
上两篇文章讲述了pytorch模型下best.pt转换成onnx模型,以及将onnx进行简化成为best-sim.onnx, 接下来这篇文章讲述如何将onnx模型转换成rknn模型,转换成该模型是为了在rk3568上运行 1.创建share文件夹 文件夹包含以下文件best-sim.onnx,rknn-tookit2-…...

微知-DOCA SDK中如何编译一个sample?如何运行?(meson /tmp/xxx; meson compile -C /tmp/xxx)
文章目录 快速回忆背景前期准备DOCA SDK中的例子情况编译编译request编译responser 执行测试启动响应端启动请求端查看响应端 综述参考 快速回忆 # 生成编译目录和相关文件 cd /opt/mellanox/doca/samples/doca_rdma/rdma_write_requester meson /tmp/req #将编译目录指定到/t…...
【Leetcode 每日一题】146. LRU 缓存(c++)
146. LRU 缓存 请你设计并实现一个满足 LRU (最近最少使用) 缓存 约束的数据结构。 实现 LRUCache 类: LRUCache(int capacity) 以 正整数 作为容量 capacity 初始化 LRU 缓存int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值&#x…...
【机器学习】近似分布的熵到底是p(x)lnq(x)还是q(x)lnq(x)?
【1】通信的定义 信息量(Information Content)是信息论中的一个核心概念,用于定量描述一个事件发生时所提供的“信息”的多少。它通常用随机变量 𝑥的概率分布来定义。事件 𝑥发生所携带的信息量由公式给出࿱…...

网络安全,文明上网(6)网安相关法律
列举 1. 《中华人民共和国网络安全法》: - 这是中国网络安全的基本法律,于2017年6月1日开始实施。该法律明确了网络运营者的安全保护义务,包括采取数据分类、重要数据备份和加密等措施。 2. 《中华人民共和国数据安全法》: …...
网络安全学习74天(记录)
11.21日,今天学习了 app抓包(需要的工具charles(激活),夜神模拟器,postern,) 思路:首先charles需要抓取的app的包,需要的是装证书,将charles的证…...

Spring Boot 实战:基于 Validation 注解实现分层数据校验与校验异常拦截器统一返回处理
1. 概述 本文介绍了在spring boot框架下,使用validation数据校验注解,针对不同请求链接的前端传参数据,进行分层视图对象的校验,并通过配置全局异常处理器捕获传参校验失败异常,自动返回校验出错的异常数据。 2. 依赖…...

20241125复盘日记
昨日最票: 南京化纤 滨海能源 广博股份 日播时尚 众源新材 返利科技 六国化工 丰华股份 威领股份 凯撒旅业 华扬联众 泰坦股份 高乐股份高均线选股: 理邦仪器高乐股份日播时尚领湃科技威领股份资金最多的票: 资金攻击最多的票: …...
【Excel】拆分多个sheet,为单一表格
Private Sub 分拆工作表() Application.ScreenUpdating True 让屏幕显示操作过程, Dim sht As Worksheet Dim MyBook As Workbook Set MyBook ActiveWorkbook For Each sht In MyBook.Sheets If sht.Visible True Then 隐藏的sheet跳过,否则会报1004无…...

类和对象plus版
一.类的定义 1.1类定义的格式 图中class为关键字,Stack为类的名字,用{}框住类的主体,类定义完后;不能省略。 为了区分成员变量,一般习惯在成员变量前面或后面加一个特殊标识,_或者m_ 1.2访问限定符 c采用…...

shell练习
开篇小贴士:为创建的sh(当然可以是任何一个文件)文件添加开头的注释 1、进入到家目录,然后通过 ls -a 查看全部文件 2、找到并编辑一个名为 .vimrc (Vim编辑器的核心配置文件)的配置文件,下图…...

ApiChain 从迭代到项目 接口调试到文档生成单元测试一体化工具
项目地址:ApiChain 项目主页 ApiChain 简介 ApiChain 是一款类似 PostMan 的接口网络请求与文档生成软件,与 PostMan 不同的是,它基于 项目和迭代两个视角管理我们的接口文档,前端和测试更关注版本迭代中发生变更的接口编写代码…...

Vercel 设置自动部署 GitHub 项目
Vercel 设置自动部署 GitHub 项目 问题背景 最近 Vercel 调整了其部署政策,免费版用户无法继续使用自动部署功能,除非升级到 Pro 计划。但是,我们可以通过配置 Deploy Hooks 来实现同样的自动部署效果。 解决方案 通过设置 Vercel 的 Dep…...
SQL进阶:如何跳过多个NULL值取第一个非NULL值?
NULL 一、问题描述二、ORACLE<一>、last_value () over ()<二>、lag () over()<三>、相关子查询 三、MYSQL<一>、全局变量<二>、coalesce() lag() over()<三>、相关子查询<四>、 recursive<五>、lag() over() min() over() …...
laravel 5.5 增加宏指令 joinSub, 省去->toSql() 和 addBinding($bindings);
laravel 5.5 增加宏指令 joinSub, 省去->toSql() 和 addBinding($bindings); 1. 在laravel5使用join 子查询时 $sub_query DB::table(table1)->select([table1.id, cate_id])->join(table2, table1.id, , table2.id)->where(table1.cate_id, 2)->orderBy(tabl…...

远程控制软件:探究云计算和人工智能的融合
在数字化时代,远程控制工具已成为我们工作与生活的重要部分。用户能够通过网络远程操作和管理另一台计算机,极大地提升了工作效率和便捷性。随着人工智能(AI)和云计算技术的飞速发展,远程控制工具也迎来了新的发展机遇…...

C++_核心编程_多态案例二-制作饮品
#include <iostream> #include <string> using namespace std;/*制作饮品的大致流程为:煮水 - 冲泡 - 倒入杯中 - 加入辅料 利用多态技术实现本案例,提供抽象制作饮品基类,提供子类制作咖啡和茶叶*//*基类*/ class AbstractDr…...
【Linux】shell脚本忽略错误继续执行
在 shell 脚本中,可以使用 set -e 命令来设置脚本在遇到错误时退出执行。如果你希望脚本忽略错误并继续执行,可以在脚本开头添加 set e 命令来取消该设置。 举例1 #!/bin/bash# 取消 set -e 的设置 set e# 执行命令,并忽略错误 rm somefile…...
Leetcode 3576. Transform Array to All Equal Elements
Leetcode 3576. Transform Array to All Equal Elements 1. 解题思路2. 代码实现 题目链接:3576. Transform Array to All Equal Elements 1. 解题思路 这一题思路上就是分别考察一下是否能将其转化为全1或者全-1数组即可。 至于每一种情况是否可以达到…...

理解 MCP 工作流:使用 Ollama 和 LangChain 构建本地 MCP 客户端
🌟 什么是 MCP? 模型控制协议 (MCP) 是一种创新的协议,旨在无缝连接 AI 模型与应用程序。 MCP 是一个开源协议,它标准化了我们的 LLM 应用程序连接所需工具和数据源并与之协作的方式。 可以把它想象成你的 AI 模型 和想要使用它…...
AtCoder 第409场初级竞赛 A~E题解
A Conflict 【题目链接】 原题链接:A - Conflict 【考点】 枚举 【题目大意】 找到是否有两人都想要的物品。 【解析】 遍历两端字符串,只有在同时为 o 时输出 Yes 并结束程序,否则输出 No。 【难度】 GESP三级 【代码参考】 #i…...

学习STC51单片机31(芯片为STC89C52RCRC)OLED显示屏1
每日一言 生活的美好,总是藏在那些你咬牙坚持的日子里。 硬件:OLED 以后要用到OLED的时候找到这个文件 OLED的设备地址 SSD1306"SSD" 是品牌缩写,"1306" 是产品编号。 驱动 OLED 屏幕的 IIC 总线数据传输格式 示意图 …...

BCS 2025|百度副总裁陈洋:智能体在安全领域的应用实践
6月5日,2025全球数字经济大会数字安全主论坛暨北京网络安全大会在国家会议中心隆重开幕。百度副总裁陈洋受邀出席,并作《智能体在安全领域的应用实践》主题演讲,分享了在智能体在安全领域的突破性实践。他指出,百度通过将安全能力…...

全志A40i android7.1 调试信息打印串口由uart0改为uart3
一,概述 1. 目的 将调试信息打印串口由uart0改为uart3。 2. 版本信息 Uboot版本:2014.07; Kernel版本:Linux-3.10; 二,Uboot 1. sys_config.fex改动 使能uart3(TX:PH00 RX:PH01),并让boo…...

分布式增量爬虫实现方案
之前我们在讨论的是分布式爬虫如何实现增量爬取。增量爬虫的目标是只爬取新产生或发生变化的页面,避免重复抓取,以节省资源和时间。 在分布式环境下,增量爬虫的实现需要考虑多个爬虫节点之间的协调和去重。 另一种思路:将增量判…...

10-Oracle 23 ai Vector Search 概述和参数
一、Oracle AI Vector Search 概述 企业和个人都在尝试各种AI,使用客户端或是内部自己搭建集成大模型的终端,加速与大型语言模型(LLM)的结合,同时使用检索增强生成(Retrieval Augmented Generation &#…...