Java虚拟机(JVM)、垃圾回收器

news2024/11/22 19:39:44

一、Java简介

1、Java开发及运行版本

JRE(Java Runtime Environment,运行环境)
所有的程序都要在JRE下才能够运行。包括JVM和Java核心类库和支持文件。

JDK(Java Development Kit,开发工具包)
用来编译、调试Java程序的开发工具包。包括Java工具(javac/java/jdb等)和Java基础的类库(java API )。

JVM(Java Virtual Machine,虚拟机)
JRE的一部分,是Java的核心和基础,用来加载字节码(.class)文件、管理并分配内存、执行垃圾收集。解释自己的指令集(即字节码)并映射到本地的CPU指令集和OS的系统调用。不同的操作系统会有不同的JVM映射规则,完成跨平台性。

2、Java程序的应用版本

Java SE
标准版(桌面程序、控制台开发、嵌入式环境...),Java的基础与核心,也是JavaEE和JavaME技术的基础。
javaSE包含:面向对象、多线程、IO流、javaSwing

Java EE
在javaSE的基础上,创建了规范和框架,提供Web服务、组件模型、管理和通信API、服务器开发,如开发B/S架构软件
javaEE包含:serclet、jstl、jsp、spring、mybatis

Java ME
机顶盒、移动电话和PDA之类嵌入式消费电子设备提供的Java语言平台,包括虚拟机和一系列标准化的Java API。

Java Card
支持一些java小程序(Applets),运行在小内存设备上的平台。

二、Java的类加载

1、类加载器种类

启动类加载器(Bootstrap ClassLoader)
负责加载%JAVA_HOME%/lib目录下的 jar 包和类。它只能加载自己能够识别的类。
扩展类加载器(Extendtion ClassLoader)
它负责加载<JAVA_HOME>\lib\ext目录中的或者被java.ext.dirs系统变量所指定的路径中的所有类库,开发者可以使用扩展类加载器。
应用程序类加载器(Application ClassLoader)
负责加载ClassPath上所指定的类库,如果应用程序没有自定义过自己的类加载器,一般情况下这就是程序的默认类加载器。
自定义类加载器(CustomerClassLoader)
自己定义的类加载器,如tomcat、jboss都会根据j2ee规范自行实现ClassLoader。

2、类加载步骤

1.加载(类加载器完成)

1.通过类的全限定名来获取定义此类的二进制字节流
2.将这个类字节流代表的静态存储结构转为方法区的运行时数据结构
3.在堆中生成一个代表此类的java.lang.Class对象,作为访问方法区这些数据结构的入口。

2.校验(连接阶段)

确保Class文件的字节流中包含的信息符合当前虚拟机的要求,不危害虚拟机的自身安全。
1.文件格式验证:基于字节流验证。
2.元数据验证:基于方法区的存储结构验证。
3.字节码验证:基于方法区的存储结构验证。
4.符号引用验证:基于方法区的存储结构验证。

3.准备(连接阶段)

为类变量分配内存,并将其初始化为默认值。在方法区中分配这些变量所使用的内存空间。
例1:
public static int value = 123;
此时在准备阶段过后的初始值为0而不是123;
例2:
public static final int value = 123;
此时value的值在准备阶段过后就是123。

4.解析(连接阶段)

把类型中的符号引用转换为直接引用,主要四种:
1.类或接口的解析
2.字段解析
3.类方法解析
4.接口方法解析

5.初始化

为类的静态变量赋予正确的初始值、执行类的静态代码块。
如果有父类,则先运行父类中的变量赋值语句和静态语句。

6.注意

1.类在使用时才会加载:反射触发、 new 对象。
2.加载类的子类会先加载父类。
3.static final修饰的常量属性会存到常量池中,类不会加载。

三、JVM的内存模型

运行时数据区域

1、程序计数器

存放下条指令所在单元的地址。

2、线程栈(虚拟机栈)

1.局部变量表:存储基本数据类型(int、float、byte等),如果是引用数据类型,则存储的是其在堆中的内存地址,也就是指向对象的一个指针。
2.操作数栈:操作数运算时一块临时的空间来存放操作数。
3.动态链接:将代码的符号引用转换为在方法区(运行时常量池)中的直接引用。
4.方法出口:存储了栈帧中的方法执完之后回到上一层方法的位置。

3、本地方法栈

与虚拟机栈结构一致,本地方法栈执行的是Java底层由C++编写的native方法。

4、元空间(方法区)

主要包括:常量、静态变量、类信息(对象头)、运行时常量池,操作的是直接内存。
默认情况下是off heap的(堆的一个逻辑分区)内存,大小不受jvm大小的限制,属于操作系统内存。
运行时常量池:虚拟机启动,将各个Class文件中的常量池载入到运行时常量池中(加载到内存中)。Class常量池只是一个媒介场所。

5、堆

虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。

5.1 对象结构

四、垃圾回收

1、回收策略

1.四种引用类型

强引用
使用最普遍的引用。垃圾回收器绝对不会回收它,内存不足时宁愿抛出 OOM 导致程序异常,平常的 new 对象就是。
软引用
垃圾回收器在内存充足时不会回收软引用(SoftReference)对象,不足时会回收它,特别适合用于创建缓存。
弱引用
在扫描到该对象时无论内存是否充足都会回收该对象。ThreadLocal 的 Key 就是弱引用。
虚引用
如果一个对象只具有虚引用(PhantomReference)那么跟没有任何引用一样,任何适合都可以被回收。主要用跟踪对象跟垃圾回收器回收的活动。

2.判定堆中对象是否为垃圾

引用计数法:在对象中添加一个引用计数器,当有地方引用这个对象时,引用计数器值+1,当引用失效时,计数器值-1。两个对象循环引用的时,各自的计数器始终不会变成0,导致无法回收,引起内存泄露。(一般不采用)。
可达性分析法:从GCRoots的对象虚拟机栈、方法区的类属性引用的对象、方法区中常量引用的对象、本地方法栈中引用的对象)的引用连来判断是否为垃圾。

3.回收算法

1.标记–清除
首先通过根节点,标记所有从根节点开始的可达对象。清除阶段,清除所有未被标记的对象。
2.标记–整理(压缩)
标记-压缩算法从根节点开始,对所有可达对象做一次标记。将所有的存活对象压缩到内存的一端,清理边界外所有的空间。
3.标记-复制
将内存区域均分为了两块(记为S0和S1),创建对象的时只用其中的一块(如S0),当S0使用完之后,将S0上面存活的对象全复制到S1上去,然后将S0全部清理掉。
4.分代收集
目前大部分JVM的采用的算法。根据对象存活周期将内存划分为几块。根据每块内存区间的特点,使用不同的回收算法,以提高垃圾回收的效率。
将Java堆划分为新生代 (Young Generation)和老年代(Old Generation)两个区域。新生代又分为Eden区、From Survivor和To Survivor。

4.三色标记法

1.三色

黑色:表示根对象,或者该对象与它引用的对象都已经被扫描过了。
灰色:该对象本身已经被标记,但是它引用的对象还没有扫描完。
白色:未被扫描的对象,如果扫描完所有对象之后,最终为白色的为不可达对象,也就是垃圾对象。

2.三色标记过程

1.初始时,全部对象都是白色的
2.GC Roots直接引用的对象变为灰色
3.从灰色集合中获取元素;将本对象直接引用的对象标记为灰色;然后将当前的对象标记为黑色。
4.重复步骤3,直到灰色的对象集合全部变为空
5.结束后,仍然被标记为白色的对象就是不可达对象,就视为垃圾对象。

2、各类回收器

垃圾收集器工作方式作用空间     算法  特点适用场景
Serial
串行
新生代
复制算法
响应速度优先
适用于单CPU环境下的client模式
ParNew
并行
新生代
复制算法
响应速度优先
多CPU环境Server模式下与CMS配合使用
Parallel
并行
新生代
复制算法
吞吐量优先
适用于后台运算而不需要太多交互的场景
Serial Old
串行
老年代
标记-整理(压缩)算法
响应速度优先
适用于单CPU环境下的Client模式
Paraller Old
并行
老年代
标记-整理(压缩)算法
吞吐量优先
适用于后台运算而不需要太多交互的场景
CMS
并发
老年代
标记-清除算法
响应速度优先
适用于互联网或B/S业务
G1
并发、并行
新生代、老年代
标记-整理(压缩)算法
响应速度优先
响应速度优先

1、对象分配

1.优先分配到Eden。
2.如果对象在Eden出生并经过第一次 Minor GC后仍然存活,且能被 Survivor容纳的话,将被移动到 Survivor空间中。对象在Survivor区中每熬过一次 Minor GC,年龄就增加1岁,当到达阈值(默认为15岁,其实每个JVM、每个GC都有所不同)时,就会被晋升到老年代中。
3.大对象直接分配到老年代,尽量避免程序中出现过多的大对象。
4.长期存活的对象分配到老年代。
5.动态对象年龄判断,如果 Survivor区中相同年龄的所有对象大小的总和大于 Survivor空间的一半,年龄大于或等于该年龄的对象可以直接进入老年代,无须等到MaxtenuringThreshold中要求的年龄。

2.GC收集

新生代收集(Minor GC/ Young GC):新生代的垃圾收集。
老年代收集(Major GC/ old GC):老年代的垃圾收集。目前,只有 CMS GC会有单独收集老年代的行为。注意,很多时候 Major GC会和Full GC混淆使用,需要具体分辨是老年代回收还是整堆回收。
混合收集(Mixed GC):收集整个新生代以及部分老年代的垃圾收集,目前,只有G1会有这种行为。
整堆收集(Full GC):收集整个java堆和方法区的垃圾收集。

1.年轻代GC(Minor GC)触发机制

(1)当年轻代空间不足时,就会触发 Minor GC,这里的年轻代满指的是Eden代满,,Survivor满不会引发GC。(每次 Minor Gc会清理年轻代的内存。)
(2)因为Java对象大多都具备朝生夕灭的特性,所以 Minor gc非常频繁,一般回收速度也比较快。这一定义既清晰又易于理解。
(3)Minor gc会引发STW,暂停其它用户的线程,等垃圾回收结束,用户线程才恢复运行。

2.老年代GC(Major GC/FullGC)触发机制

(1)指发生在老年代的GC,对象从老年代消失时, Major GC”或“Fu11GC发生了。
(2)出现了 Major GC,经常会伴随至少一次的 Minor GC(但非绝对的,在Parallel Scavenge收集器的收集策略里就有直接进行 Major GC的策略选择过程)。
(3)也就是在老年代空间不足时,会先尝试触发 Minor GC。如果之后空间还不足,则触发 Major GC。
(4)Major GC的速度一般会比 Minor gc慢10倍以上,STW的时间更长。

3.触发FullGc执行的情况

(1)调用 System. gc()时,系统建议执行Fu11GC,但是不必然执行。
(2)老年代空间不足。
(3)方法区空间不足。
(4)通过 Minor GC后进入老年代的平均大小大于老年代的可用内存。
(5)由Eden区、 from区向 from区复制时,对象大小大于 To Space可用内存,则把该对象转存到老年代,且老年代的可用内存小于该对象大小。

3、G1回收器

G1 作为 JDK9 之后的服务端默认收集器,不再区分年轻代和老年代进行垃圾回收。
G1 默认把堆内存分为 N 个分区,每个 1~32M。提供了四种不同区域标签 Eden、Survivor 、Old、 Humongous。H(Humongous)区可以认为是 Old 区中一种特列专门用来存储大数据的。

1.G1的运行过程

1.初始标记:标记下GC Roots能直接关联到的对象。这个阶段需要短暂停顿线程。
2.并发标记:从GC Root开始对堆中对象进行可达性分析,递归扫描整个堆里的对象图,找出要回收的对象,这阶段耗时较长,但可与用户程序并发执行。
3.最终标记:对用户线程做一个短暂的暂停,用于处理并发标记阶段仍遗留下来的最后那少量的SATB记录(漏标对象)。
4.筛选回收:负责更新Region的统计数据,对各个Region的回收价值和成本进行排序,根据用户所期望的停顿时间来制定回收计划,采用复制清除算法,把有用的复制到新Region,再清理掉整个旧Region的全部空间。须暂停用户线程,由多个收集器线程并行完成。

2.G1的特点

优点:
1、支持较大的内存。
2、暂停时间可控。
3、压缩空间,避免产生内存碎片。
4、简单配置就能达到很好的性能。
5、内存模型方面:G1采用物理分区,逻辑分代,不连续的内存区域Region组成。而CMS中Eden,Survivor,Old区是连续的一整块内存。
6、G1既可以收集年轻代,也可以收集老年代,而CMS只能在老年代使用。
缺点:
1、记忆集RSet会占用比较大的内存,因此不建议在小内存下使用G1,推荐至少6G。
2、对CPU的负载可能会更大一点。
3、由于采用复制算法,GC垃圾回收过程对象复制转移会占用较多的内存,更容易出现回收失败(Allocation (Evacuation) Failure)的问题。
4、可能会降低吞吐量。
虽然 G1收集器的垃圾收集暂停时间通常要短得多,但应用程序吞吐量也往往略低一些。
相当于把一次垃圾回收的工作,分开多次进行执行(主要指老年代),单次暂停的时间虽然更加可控,但是由于每次垃圾回收的空间会更少,
总体来说垃圾回收的效率会更低,暂停的总时间会更长,所以吞吐量往往会略低一些。

3.对比CMS

CMS的老年代回收采用的是标记-清除算法
初始标记:标记GC root能直接关联的对象(短暂STW)。
并发标记:GCRootsTracing,从并发标记中的root遍历,对不可达的对象进行标记。
重新标记:修正并发标记期间因为用户操作导致标记发生表更的对象,采用的incremental update算法,会出现比较多的STW。
并发清除:由于是直接清理,不涉及对象的复制转移,所以阶段可以并发执行。

五、JVM的工具及调优

1、工具

jps:输出 JVM 中运行的进程状态信息
jstack:生成虚拟机当前时刻的线程快照
jstat:虚拟机统计信息监控工具
jinfo:实时地查看和调整虚拟机各项参数
jmap:生成虚拟机的内存转储快照,heapdump 文件
JConsole:可视化管理工具,常用

2、调优

在没有全面监控、收集性能数据之前,调优是盲目的(一般项目加个 xms 和 xmx 参数就够)。
日常分析 GC 情况优化代码比优化 GC 参数要多得多。一般如下情况不用调优的:
minor GC 单次耗时 < 50ms,频率 10 秒以上。说明年轻代 OK。
Full GC 单次耗时 < 1 秒,频率 10 分钟以上,说明年老代 OK。
GC 调优目的:GC 时间够少,GC 次数够少。

3、调优建议

-Xms5m 设置 JVM 初始堆为 5M,-Xmx5m 设置 JVM 最大堆为 5M。-Xms 跟-Xmx 值一样时可以避免每次垃圾回收完成后 JVM 重新分配内存。
-Xmn2g:设置年轻代大小为 2G,一般默认为整个堆区的 1/3 ~ 1/4。- Xss 每个线程栈空间设置。
-XX:SurvivorRatio,设置年轻代中 Eden 区与 Survivor 区的比值,默认=8,比值为 8:1:1。
-XX:+HeapDumpOnOutOfMemoryError 当 JVM 发生 OOM 时,自动生成 DUMP 文件。
-XX:PretenureSizeThreshold 当创建的对象超过指定大小时,直接把对象分配在老年代。
-XX:MaxTenuringThreshold 设定对象在 Survivor 区最大年龄阈值,超过阈值转移到老年代,默认 15。
开启 GC 日志对性能影响很小且能帮助我们定位问题,-XX:+PrintGCTimeStamps -XX:+PrintGCDetails -Xloggc:gc.log

4、JVM 参数 

参数        说明                实例
-Xms    初始堆大小,默认物理内存的1/64    -Xms512M
-Xmx    最大堆大小,默认物理内存的1/4    -Xms2G
-Xmn    新生代内存大小,官方推荐为整个堆的3/8    -Xmn512M
-Xss    线程堆栈大小,jdk1.5及之后默认1M,之前默认256k    -Xss512k
-XX:NewRatio=n    设置新生代和年老代的比值。如:为3,表示年轻代与年老代比值为1:3,年轻代占整个年轻代年老代和的1/4    -XX:NewRatio=3
-XX:SurvivorRatio=n    年轻代中Eden区与两个Survivor区的比值。注意Survivor区有两个。如:8,表示Eden:Survivor=8:1:1,一个Survivor区占整个年轻代的1/8    -XX:SurvivorRatio=8
-XX:PermSize=n    永久代初始值,默认为物理内存的1/64    -XX:PermSize=128M
-XX:MaxPermSize=n    永久代最大值,默认为物理内存的1/4    -XX:MaxPermSize=256M
-verbose:class    在控制台打印类加载信息
-verbose:gc    在控制台打印垃圾回收日志
-XX:+PrintGC    打印GC日志,内容简单
-XX:+PrintGCDetails    打印GC日志,内容详细
-XX:+PrintGCDateStamps    在GC日志中添加时间戳
-Xloggc:filename    指定gc日志路径    -Xloggc:/data/jvm/gc.log
-XX:+UseSerialGC    年轻代设置串行收集器Serial
-XX:+UseParallelGC    年轻代设置并行收集器Parallel Scavenge
-XX:ParallelGCThreads=n    设置Parallel Scavenge收集时使用的CPU数。并行收集线程数。    -XX:ParallelGCThreads=4
-XX:MaxGCPauseMillis=n    设置Parallel Scavenge回收的最大时间(毫秒)    -XX:MaxGCPauseMillis=100
-XX:GCTimeRatio=n    设置Parallel Scavenge垃圾回收时间占程序运行时间的百分比。公式为1/(1+n)    -XX:GCTimeRatio=19
-XX:+UseParallelOldGC    设置老年代为并行收集器ParallelOld收集器
-XX:+UseConcMarkSweepGC    设置老年代并发收集器CMS
-XX:+CMSIncrementalMode    设置CMS收集器为增量模式,适用于单CPU情况。

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

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

相关文章

【LNMP】架构及应用部署 搭建电影网站

准备环境 一台虚拟机192.168.108.67 关闭防火墙 systemctl stop firewalld iptables -F setenforce 0 检查光盘 查看yum仓库 安装nginx依赖 [rootlocalhost ~]# yum -y install pcre-devel zlib-devel 创建管理nginx用户&#xff08;用来运行nginx&#xff09; [rootlocalh…

picard安装时报错“Exception in thread “main“ java.lang.UnsupportedClassVersionError”

最近在通过GATK所介绍的best practice流程来call SNP流程 1.流程 1.1 BWA比对&#xff0c;获得sam文件 1.2 准备用picard来压缩排序sam文件为bam文件&#xff0c;并对bam文件进行去重复&#xff08;duplicates marking&#xff09; 这是就需要用到picard软件 按照教程网页上…

go-zero的rpc服务案例解析

go-zero的远程调用服务是基于gRpc的gRPC教程与应用。 zero使用使用gRpc需要安装protoc插件&#xff0c;因为gRpc基于protoc插件使用protocol buffers文件生成rpc服务器和api的代码的。 gRPC 的代码生成还依赖 protoc-gen-go&#xff0c;protoc-gen-go-grpc 插件来配合生成 Go…

机器学习笔记 - 局部敏感哈希简介

一、算法简述 局部敏感散列 (LSH) 技术,可显著加快对数据的邻居搜索或近似重复检测。例如,这些技术可用于以惊人的速度过滤掉抓取网页的重复项,或者从地理空间数据集中对附近点执行近恒定时间查找。 让我们快速回顾一下其他类型的哈希函数,哈希函数的传统用途是…

青岛大学_王卓老师【数据结构与算法】Week04_13_案例分析与实现3_学习笔记

本文是个人学习笔记&#xff0c;素材来自青岛大学王卓老师的教学视频。 一方面用于学习记录与分享&#xff0c;另一方面是想让更多的人看到这么好的《数据结构与算法》的学习视频。 如有侵权&#xff0c;请留言作删文处理。 课程视频链接&#xff1a; 数据结构与算法基础–…

漏洞检测01:DNS域传送漏洞

DNS域传送漏洞 文章目录 DNS域传送漏洞漏洞描述检测方法修复建议 DNS域传送漏洞 该漏洞当下已经非常少了 DNS服务器分为&#xff1a;主服务器、备份服务器和缓存服务器。在主备服务器之间同步数据库&#xff0c;需要使用“DNS域传送”。域传送是指备份服务器从主服务器拷贝数…

多目标下改进NSGA-II算法并使用ZDT测试函数验证matlab

目标优化机械臂轨迹&#xff0c;不知道NSGA-II算法改进后到底有没有效果&#xff0c;需要用测试函数进行验证&#xff0c;附matlab代码 参考多目标优化NSGA-II的实现&#xff08;MATLAB完整代码&#xff09;_nsga2 matlab_羽丶千落的博客-CSDN博客 目录 1.复制所有matlab代码…

STM32速成笔记—低功耗模式

文章目录 一、STM32低功耗模式介绍二、睡眠模式2.1 进入睡眠模式2.2 退出睡眠模式 三、停止模式3.1 进入停止模式3.2 退出停止模式 四、待机模式五、程序设计 一、STM32低功耗模式介绍 STM32提供了一些低功耗模式。默认情况下&#xff0c;系统复位或上电复位后&#xff0c;微控…

html中表格

一、table标签 参数说明 实例 <body><table border"1" align"center" width"300" height"200" cellspacing"10"><tr><td>1.1</td><td>1.2</td><td>1.3</td></t…

把labelme得到的json文件转换成yolov8需要的格式,划分数据集

使用labelme打标&#xff0c;得到json文件把所有json文件放到一个单独的文件夹&#xff0c;里面只有json文件使用脚本&#xff0c;把json里面的label,标注框的中心坐标、宽、高提取出来&#xff0c;注意这里的4个值都按照图像大小压缩了。 脚本如下&#xff1a; import json…

【网页设计】基于HTML的湖南渔鼓文化宣传网页的设计

1.引言 随着文化交流的不断深入&#xff0c;湖南渔鼓文化作为湖南省的非物质文化遗产&#xff0c;逐渐引起了人们的关注和研究。为了更好地推广和宣传湖南渔鼓文化&#xff0c;我们设计了一款基于HTML的湖南渔鼓文化宣传网页。 该网页旨在向广大用户介绍湖南渔鼓文化的深厚底…

第四章:SSD网络详解

(目标检测篇&#xff09;系列文章目录 第一章:R-CNN网络详解 第二章:Fast R-CNN网络详解 第三章:Faster R-CNN网络详解 第四章:SSD网络详解 第五章:YOLO v1网络详解 第六章:YOLO v2网络详解 第七章:YOLO v3网络详解 文章目录 系列文章目录技术干货集锦前言一、摘要二、正…

FME之发布全局变量和接收全局变量

1.发布变量用VariableSetter转换器&#xff0c;其中DH是全局变量名称&#xff0c;它的值是从上一步产生的数据中某个字段提取。发布之前我们一般都会用Sampler抽样保留一条记录即可。 2.接收全局变量用VariableRetriever转换器&#xff0c;其中变量名称用之前发布的全局变量名称…

Redis下载和安装(Windows系统)

本套教程中采用 Windows 系统对 Redis 数据库进行讲解。 虽然 Redis 官方网站没有提供 Windows 版的安装包&#xff0c;但可以通过 GitHub 来下载 Windows 版 Redis 安装包&#xff0c;下载地址&#xff1a;点击前往。 注意&#xff1a;Windows 安装包是某位民间“大神”根据 …

小白到运维工程师自学之路 第四十七集 (LNMP部署电影网站)

一、概述 Linux&#xff1a;作为操作系统&#xff0c;提供服务器的基本功能和稳定性。通常使用常见的Linux发行版&#xff0c;如Ubuntu、CentOS等。 Nginx&#xff1a;作为Web服务器和反向代理服务器&#xff0c;处理HTTP请求和传递数据。Nginx具有高性能、稳定性和并发处理能…

Java链式编程与Builder(建造者)设计模式

一、链式编程 1.1.释义 链式编程&#xff0c;也叫级联式编程&#xff0c;调用对象的函数时返回一个this对象指向对象本身&#xff0c;达到链式效果&#xff0c;可以级联调用。 1.2.特点 可以通过一个方法调用多个方法&#xff0c;将多个方法调用链接起来&#xff0c;形成一…

Mysql数据NULL避坑指南

NULL空值是mysql中一种特殊的数据值&#xff08;即"缺少的未知值"&#xff09;,NULL和字符串空值不是一回事&#xff0c;处理NULL与其他值不同&#xff0c;下面具体阐述相关差异&#xff1a; 一、NULL运算符 1、普通数据使用 > 、 < 、即可做常用的逻辑运算如…

Linux随机生成数

简介 在某些情况下&#xff0c;我们需要随机产生一个数来在一些场景中使用&#xff0c;例如验证码、ssh反向代理随机数的产生&#xff0c;又或者在一些shell脚本设计中需要用到随机数&#xff0c;下面以随机产生一个30000-40000之间的随机数为例。 使用shuf shuf 命令在一些 Li…

【FFMPEG】AVFilter使用流程

流程图 核心类 AVFilterGraph ⽤于统合这整个滤波过程的结构体 AVFilter 滤波器&#xff0c;滤波器的实现是通过AVFilter以及位于其下的结构体/函数来维护的 AVFilterContext ⼀个滤波器实例&#xff0c;即使是同⼀个滤波器&#xff0c;但是在进⾏实际的滤波时&#xff0c;也…

基于单片机水质检测系统的设计与实现

功能介绍 以STM32单片机作为主控系统&#xff1b;液晶显示当前参数&#xff1b;PH模块采集当前水质PH酸碱度&#xff1b;DS18B20温度传感器采集当前水体温度&#xff1b;TDS传感器采集当前水体TDS值&#xff1b;浊度传感器采集当前水体浑浊度&#xff1b;按键设置PH、温度、TDS…