深入理解 JVM 之——Java 内存区域与溢出异常

更好的阅读体验 \huge{\color{red}{更好的阅读体验}} 更好的阅读体验

本篇为深入理解 Java 虚拟机第二章内容,推荐在学习前先掌握基础的 Linux 操作、编译原理、计算机组成原理等计算机基础以及扎实的 C/C++ 功底。

该系列的 GitHub 仓库:https://github.com/Doge2077/learn-jvm


运行时数据区域


Java 虚拟机在执行 Java 程序的过程中会把它所管理的内存划分为以下数据区域。

image-20230831150214981


程序计数器


程序计数器(Program Counter Register)是一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器。

Java 中,方法的执行可以分为两种情况:Java 方法和本地方法。Java 方法是用 Java 语言编写的方法,而本地方法是使用其他语言(如 CC++)编写的方法,通过 Java Native Interface(JNI)在 Java 程序中调用。

由于 Java 虚拟机的多线程是通过线程轮流切换、分配处理器执行时间的方式来实现,因此:

  • 每条线程都有一个独立的程序计数器,各条线程之间计数器互不影响,独立存储。
  • 如果线程正在执行的是一个 Java 方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址;如果正在执行的是本地(Native)方法,这个计数器值则应为空(Undefined)。

值得注意的是,在 《Java 虚拟机规范中》,计数器所在的内存区域是唯一一个没有规定任何 OutOfMemoryError 情况的区域。这意味着计数器不会因为内存不足而导致 OutOfMemoryError 异常。


Java 虚拟机栈


Java 虚拟机中的虚拟机栈(Java Virtual Machine Stack),它是线程私有的,其生命周期与线程相同。

虚拟机栈用于描述 Java 方法的执行过程,每个方法在执行时都会创建一个栈帧,用于存储局部变量表、操作数栈、动态连接和方法出口等信息。栈帧在虚拟机栈中入栈和出栈,对应着方法的调用和执行过程

Java 虚拟机中,栈内存通常指的是虚拟机栈,大多数情况下指的是虚拟机栈中的局部变量表部分。

局部变量表用于存放编译期可知的各种 Java 虚拟机基本数据类型、对象引用和 returnAddress 类型。局部变量表的存储空间以局部变量槽(Slot)表示,其中 longdouble 类型的数据占用两个变量槽,其他数据类型占用一个变量槽。

HotSpot 虚拟机的栈容量是不可以动态扩展的,以前的 Classic 虚拟机倒是可以。所以在 HotSpot 虚拟机上是不会由于虚拟机栈无法扩展而导致 OutOfMemoryError 异常——只要线程申请栈空间成功了就不会有 OOM,但是如果申请时就失败,仍然是会出现 OOM 异常的

一般情况下,局部变量表所需的内存空间在编译期间确定,并在方法运行期间不会改变大小(变量槽的数量)。


本地方法栈


本地方法栈(Native Method Stacks)与虚拟机栈所发挥的作用相似,其区别只是虚拟机栈为虚拟机执行 Java 方法(也就是字节
码)服务,而本地方法栈则是为虚拟机使用到的本地(Native)方法服务。

《Java虚拟机规范》对本地方法栈中方法使用的语言、使用方式与数据结构并没有任何强制规定,因此具体的虚拟机可以根据需要自由实
现它。

对于HotSpot虚拟机,它将本地方法栈(Native Method Stack)和虚拟机栈(Java Virtual Machine Stack)合二为一,这意味着它可以在同一个栈中管理 Java 方法和本地方法的执行。


Java 堆


Java 堆(Java Heap)是虚拟机所管理的内存中最大的一块,被所有线程共享的一块内存区域,在虚拟机启动时创建。

  • 此内存区域的唯一目的就是存放对象实例,所有的对象实例都在这里分配内存。
  • 该内存区域受到垃圾收集器管理,也被称为“GC”堆(Garbage Collected Heap)。
  • Java 堆可以处于物理上不连续的内存空间中,但在逻辑上它应该被视为连续的。
  • Java 堆既可以被实现成固定大小的,也可以是可扩展的(通过参数 -Xmx-Xms 设定)。

如果在 Java 堆中没有内存完成实例分配,并且堆也无法再扩展时,Java 虚拟机将会抛出 OutOfMemoryError 异常。


方法区


方法区(Method Area)与Java堆一样,是各个线程共享的内存区域:

  • 用于存储已被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等数据。
  • 方法区和 Java 堆一样不需要连续的内存和可以选择固定大小或者可扩展外,甚至还可以选择不实现垃圾收集。

早期 HotSpot 虚拟机设计团队把收集器的分代设计扩展至方法区,因此方法区也被成为“永久代”,但只是使用永久代来实现方法区而已,这种设计导致了 Java 应用更容易遇到内存溢出的问题,在JDK 6的时候HotSpot开发团队就有放弃永久代,逐步改为采用本地内存(Native Memory)来实现方法区的计划了,到了JDK 7的HotSpot,已经把原本放在永久代的字符串常量池、静
态变量等移出,而到了JDK8,终于完全废弃了永久代的概念,改用与 JRockitJ9 一样在本地内存中实现的元空间(Meta-space)来代替。

如果方法区无法满足新的内存分配需求时,将抛出 OutOfMemoryError 异常。


运行时常量池


运行时常量池(Runtime Constant Pool)是方法区的一部分:

  • Class 文件中包含的常量池表(Constant Pool Table),用于存放编译期生成的各种字面量与符号引用,这部分内容将在类加载后存放到方法区的运行时常量池中。
  • 并非预置入 Class 文件中常量池的内容才能进入方法区运行时常量池,运行期间也可以将新的常量放入池,如 String 类的 intern() 方法。

除了String类的intern()方法之外,还有其他方法可以将常量放入运行时常量池。以下是一些常见的方法:

  1. Class.forName(String className):在使用反射加载类时,如果指定的类名在运行时常量池中不存在,会将该类名添加到运行时常量池中。
  2. String类的valueOf(Object obj)方法:当调用String.valueOf(Object obj)方法时,如果运行时常量池中不存在对应的字符串,会将该字符串添加到运行时常量池中。
  3. StringBuilderStringBuffer类的toString()方法:当调用StringBuilderStringBuffer对象的toString()方法时,如果返回的字符串在运行时常量池中不存在,会将该字符串添加到运行时常量池中。

运行时常量池是方法区的一部分,自然受到方法区内存的限制,当常量池无法再申请到内存时会抛出 OutOfMemoryError 异常。


直接内存


直接内存(Direct Memory)并不是虚拟机运行时数据区的一部分,也不是《Java虚拟机规范》中定义的内存区域。

本机直接内存的分配不会受到 Java 堆大小的限制,但受到本机总内存(包括物理内存、SWAP分区或者分页文件)大小以及处理器寻址空间的限制。

配置虚拟机参数时,要根据实际内存去设置 -Xmx 等参数信息,忽略掉直接内存,会使得各个内存区域总和大于物理内存限制(包括物理的和操作系统级的限制),从而导致动态扩展时出现 OutOfMemoryError 异常。


HotSpot 虚拟机对象


对象的创建


Java 虚拟机遇到一条字节码 new 指令时:

  • 首先将去检查这个指令的参数是否能在常量池中定位到一个类的符号引用,并且检查这个符号引用代表的类是否已被加载、解析和初始化过。若没有则执行相应的类加载过程。
  • 在类加载检查通过后,虚拟机为对象进行内存划分:
    • 指针碰撞(Bump The Pointer)式划分:对于绝对规整的空间,通过指针划分使用和空闲的空间,划分空间的过程等同于指针向空闲空间方向挪动一段与对象大小相等的距离
    • 空闲列表(Free List)式划分:对于不规整的空间,已使用的内存和空闲内存相互交错,则虚拟机必须维护一个列表用于记录可用的内存区域,划分空间的过程等同于从列表中找到一块足够大的空间划分给对象实例,并更新列表上的记录
  • 空间分配完成后,还要对对象进行必要的设置:
    • 将该对象的抽象类元数据信息、哈希码等存放在对象的对象头(Object Header)之中
    • 根据虚拟机当前运行状态的不同,如是否启用偏向锁等,对象头会有不同的设置方式。

在上面工作都完成之后,从虚拟机的视角来看,一个新的对象已经产生了。之后 Java 程序再调用其中 Class 文件的 <init>() 方法对该对象进行初始化。

注意

  • 如果在执行 new 指令时检查到该类已经被加载,虚拟机会获取到该类的运行时常量池中的直接引用,然后继续执行后续操作,如方法调用等。
  • 实际上的内存划分更为复杂,在并发情况下需要考虑到线程安全,通常采用 CAS 配上失败重试的方式保证更新操作的原子性,或者把内存分配的动作按照线程划分在不同的空间之中进行。
  • Java 编译器会在遇到 new 关键字的地方同时生成这两条字节码指令(但如果直接通过其他方式产生的则不一定如此),new 指令之后会接着执行 <init>() 方法。

对象的内存布局


HotSpot 虚拟机里,对象在堆内存中的存储布局可以划分为三个部分:

  • 对象头(Header):主要存储两类信息,第一类存储对象自身的运行时数据,如哈希码(Hash Code)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等;第二类存储类型指针,指向它的类型元数据,用于确定是哪个类的实例。
  • 实例数据(Instance Data):对象真正存储的有效信息,即程序员定义的各种类型的字段内容,无论是从父类继承下来的,还是在子类中定义的字段都必须记录起来。
  • 对齐填充(Padding):这部分可有可无,起占位符的作用。

对象的访问定位


Java 程序会通过栈上的 reference 数据来操作堆上的具体对象,主流访问方式有以下两种:

  • 句柄访问:Java 堆中将可能会划分出一块内存来作为句柄池,reference 中存储的就是对象的句柄地址,而句柄中包含了对象实例数据与类型数据各自具体的地址信息。
  • 直接指针访问:Java 堆中对象的内存布局就必须考虑如何放置访问类型数据的相关信息,reference中存储的直接就是对象地址,如果只是访问对象本身的话,就不需要多一次间接访问的开销。

句柄访问在对象被移动(垃圾收集时移动对象是非常普遍的行为)时只会改变句柄中的实例数据指针,而 reference 本身不需要被修改。而指针访问最大的好处就是速度更快,节省了一次指针定位的时间开销。


实战 :OOM 异常


Java 堆异常


public class HeapOOM {static class OOMObject {Long num[] = new Long[10000000];}public static void main(String[] args) {List<OOMObject> list = new ArrayList<OOMObject>();while (true) {list.add(new OOMObject());System.out.println(list.size());}}
}

Java 堆用于储存对象实例,我们只要不断地创建对象并保证不被回收,最终数量一定会超出内存限制。

运行后报错:

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space

java.lang.OutOfMemoryError: Java heap space 表明了该异常是 Java 堆空间异常。


虚拟机栈和本地方法栈溢出


由于 HotSpot 虚拟机中并不区分虚拟机栈和本地方法栈,因此对于 HotSpot 来说,-Xoss 参数(设置本地方法栈大小)虽然存在,但实际上是没有任何效果的,栈容量只能由 -Xss 参数来设定。

关于虚拟机栈和本地方法栈,在《Java虚拟机规范》中描述了两种异常:

  • 如果线程请求的栈深度大于虚拟机栈所允许的最大深度,将递归地抛出的 StackOverflowError 异常。
  • 如果虚拟机的栈内存允许动态扩展,当扩展栈容量无法申请到足够的内存时,将抛出 OutOfMemoryError 异常。

对于第一种异常,可以设置 -Xss 参数调整栈容量到出现异常的值,也可以使用如下代码:

public class JavaVMStackSOF {private int stackLength = 1;public void stackLeak() {stackLength ++;stackLeak();}public static void main(String[] args) throws Throwable {JavaVMStackSOF oom = new JavaVMStackSOF();try {oom.stackLeak();} catch (Throwable e) {System.out.println("stack length:" + oom.stackLength);throw e;}}
}

递归调用的方法 stackLeak(),当虚拟机栈空间不足以容纳新的栈帧时报错如下:

stack length:23360
Exception in thread "main" java.lang.StackOverflowError

每次递归调用都会在虚拟机栈中申请空间,直到达到最大深度。

对于第二种异常,我们要尽可能地多占局部变量表空间,唯一能做的就是定义大量的变量来实现:

public class JavaVMStackSOF {private static int stackLength = 0;public static void test() {long unused1, unused2, unused3, unused4, unused5, unused6, unused7, unused8, unused9, unused10,unused11, unused12, unused13, unused14, unused15, unused16, unused17, unused18, unused19, unused20,unused21, unused22, unused23, unused24, unused25, unused26, unused27, unused28, unused29, unused30,unused31, unused32, unused33, unused34, unused35, unused36, unused37, unused38, unused39, unused40,unused41, unused42, unused43, unused44, unused45, unused46, unused47, unused48, unused49, unused50,unused51, unused52, unused53, unused54, unused55, unused56, unused57, unused58, unused59, unused60,unused61, unused62, unused63, unused64, unused65, unused66, unused67, unused68, unused69, unused70,unused71, unused72, unused73, unused74, unused75, unused76, unused77, unused78, unused79, unused80,unused81, unused82, unused83, unused84, unused85, unused86, unused87, unused88, unused89, unused90,unused91, unused92, unused93, unused94, unused95, unused96, unused97, unused98, unused99, unused100,unused101, unused102, unused103, unused104, unused105, unused106, unused107, unused108, unused109, unused110,unused111, unused112, unused113, unused114, unused115, unused116, unused117, unused118, unused119, unused120,unused121, unused122, unused123, unused124, unused125, unused126, unused127, unused128, unused129, unused130,unused131, unused132, unused133, unused134, unused135, unused136, unused137, unused138, unused139, unused140,unused141, unused142, unused143, unused144, unused145, unused146, unused147, unused148, unused149, unused150,unused151, unused152, unused153, unused154, unused155, unused156, unused157, unused158, unused159, unused160,unused161, unused162, unused163, unused164, unused165, unused166, unused167, unused168, unused169, unused170,unused171, unused172, unused173, unused174, unused175, unused176, unused177, unused178, unused179, unused180,unused181, unused182, unused183, unused184, unused185, unused186, unused187, unused188, unused189, unused190,unused191, unused192, unused193, unused194, unused195, unused196, unused197, unused198, unused199, unused200;unused1 = unused2 = unused3 = unused4 = unused5 = unused6 = unused7 = unused8 = unused9 = unused10 =unused11 = unused12 = unused13 = unused14 = unused15 = unused16 = unused17 = unused18 = unused19 = unused20= unused21 = unused22 = unused23 = unused24 = unused25 = unused26 = unused27 = unused28 = unused29 = unused30= unused31 = unused32 = unused33 = unused34 = unused35 = unused36 = unused37 = unused38 = unused39 = unused40= unused41 = unused42 = unused43 = unused44 = unused45 = unused46 = unused47 = unused48 = unused49 = unused50= unused51 = unused52 = unused53 = unused54 = unused55 = unused56 = unused57 = unused58 = unused59 = unused60= unused61 = unused62 = unused63 = unused64 = unused65 = unused66 = unused67 = unused68 = unused69 = unused70= unused71 = unused72 = unused73 = unused74 = unused75 = unused76 = unused77 = unused78 = unused79 = unused80= unused81 = unused82 = unused83 = unused84 = unused85 = unused86 = unused87 = unused88 = unused89 = unused90= unused91 = unused92 = unused93 = unused94 = unused95 = unused96 = unused97 = unused98 = unused99 = unused100= unused101 = unused102 = unused103 = unused104 = unused105 = unused106 = unused107 = unused108 = unused109 = unused110= unused111 = unused112 = unused113 = unused114 = unused115 = unused116 = unused117 = unused118 = unused119 = unused120= unused121 = unused122 = unused123 = unused124 = unused125 = unused126 = unused127 = unused128 = unused129 = unused130= unused131 = unused132 = unused133 = unused134 = unused135 = unused136 = unused137 = unused138 = unused139 = unused140= unused141 = unused142 = unused143 = unused144 = unused145 = unused146 = unused147 = unused148 = unused149 = unused150= unused151 = unused152 = unused153 = unused154 = unused155 = unused156 = unused157 = unused158 = unused159 = unused160= unused161 = unused162 = unused163 = unused164 = unused165 = unused166 = unused167 = unused168 = unused169 = unused170= unused171 = unused172 = unused173 = unused174 = unused175 = unused176 = unused177 = unused178 = unused179 = unused180= unused181 = unused182 = unused183 = unused184 = unused185 = unused186 = unused187 = unused188 = unused189 = unused190= unused191 = unused192 = unused193 = unused194 = unused195 = unused196 = unused197 = unused198 = unused199 = unused200= 1145141919810L;stackLength++;test();}public static void main(String[] args) {try {test();} catch (Error e) {System.out.println("stack length:" + stackLength);throw e;}}
}

运行结果如下:

stack length:306
Exception in thread "main" java.lang.StackOverflowError

可以看到结果并不是我们想要的 OutOfMemoryError 异常。

这是因为 HotSpot 虚拟机的栈是不可动态扩展的,所以在 HotSpot 虚拟机上不会由于虚拟机栈无法扩展而导致 OutOfMemoryError 异常,这点我们在最初章节已经提到过了。


方法区和运行时常量池溢出


运行时常量池是方法区的一部分,所以这两个区域的溢出测试可以放到一起进行。

前面曾经提到 HotSpotJDK7开始逐步“去永久代”的计划,并在 JDK8 中完全使用元空间来代替永久代,我们可以使用如下代码来进行测试:

public class RuntimeConstantPoolOOM {public static void main(String[] args) {Set<String> set = new HashSet<String>();int i = 0;while (true) {set.add(String.valueOf(i ++).intern());}}
}

执行 javac RuntimeConstantPoolOOM.java 进行编译,再通过 -XX:PermSize -XX:MaxPermSize 参数设置永久代大小,接着执行:

java -XX:PermSize=6M -XX:MaxPermSize=6M RuntimeConstantPoolOOM

虚拟机报错如下:

Java HotSpot(TM) 64-Bit Server VM warning: ignoring option PermSize=6M; support was removed in 8.0
Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize=6M; support was removed in 8.0

提示我们在 JDK8 已经将该参数移除了,如果使用 JDK7 及之前的版本运行,应该会出现如下结果:

Exception in thread "main" java.lang.OutOfMemoryError: PermGen space

而我们使用的高版本则需限制 -Xmx 参数来调整(自 JDK7 起,原本存放在永久代的字符串常量池被移至Java堆):

java -Xmx6m RuntimeConstantPoolOOM

运行后出现如下结果:

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space

本机直接内存溢出


直接内存(Direct Memory)的容量大小可通过 -XX:MaxDirectMemorySize 参数来指定,如果不去指定,则默认与 Java 堆最大值(由 -Xmx 指定)一致。

我们使用如下代码进行测试:

public class DirectMemoryOOM {private static final int _1MB = 1024 * 1024;public static void main(String[] args) throws Exception {Field unsafeField = Unsafe.class.getDeclaredFields()[0];unsafeField.setAccessible(true);Unsafe unsafe = (Unsafe) unsafeField.get(null);while (true) {unsafe.allocateMemory(_1MB);}}
}

在编译后,我们设置执行参数如下:

java -Xmx20M -XX:MaxDirectMemorySize=10M DirectMemoryOOM

运行后结果如下:

Exception in thread "main" java.lang.OutOfMemoryError  

这是由于在上述代码的每次循环中,unsafe.allocateMemory(_1MB) 会调用 Unsafe 类的 allocateMemory 方法来分配 1MB 的直接内存空间,最终导致直接内存溢出。


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

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

相关文章

Android jni引用第三方so动态库和.a静态库并且调用(c)方法

最近花了一周时间来入门学习 Android JNI方面的知识,因为后续的工作很多需要用到c c++库,我需要用jni来包装一下c函数,来提供给上次java调用。总之多学点知识对自己有好处。 案例效果: 上文我们讲解了 android studio cmake生成.a文件(静态库)及调用(c c++)静态库.a 本文…

机器人中的数值优化(七)——修正阻尼牛顿法

本系列文章主要是我在学习《数值优化》过程中的一些笔记和相关思考&#xff0c;主要的学习资料是深蓝学院的课程《机器人中的数值优化》和高立编著的《数值最优化方法》等&#xff0c;本系列文章篇数较多&#xff0c;不定期更新&#xff0c;上半部分介绍无约束优化&#xff0c;…

【C语言】异或(^)

一.简介 异或&#xff0c;英文为exclusive OR&#xff0c;缩写成xor 异或&#xff08;xor&#xff09;是一个数学运算符。它应用于逻辑运算。异或的数学符号为“⊕”&#xff0c;计算机符号为“xor”。其运算法则为&#xff1a; a⊕b (a ∧ b) ∨ (a ∧b) 如果a、b两个值不…

腾讯云AI绘画:探究AI创意与技术的新边界

目录 一、2023的“网红词汇”——AI绘画二、智能文生图1、智能文生图的应用场景2、风格和配置的多样性3、输入一段话&#xff0c;腾讯云AI绘画给你生成一张图4、文本描述生成图像&#xff0c;惊艳全场 三、智能图生图&#xff1a;重新定义图像美学1、智能图生图的多元应用场景2…

【动手学深度学习】--语言模型

文章目录 语言模型1.学习语言模型2.马尔可夫模型与N元语法3.自然语言统计4.读取长序列数据4.1随机采样4.2顺序分区 语言模型 学习视频&#xff1a;语言模型【动手学深度学习v2】 官方笔记&#xff1a;语言模型和数据集 在【文本预处理】中了解了如何将文本数据映射为词元&…

大数据HBase学习圣经:一本书实现HBase学习自由

学习目标&#xff1a;三栖合一架构师 本文是《大数据HBase学习圣经》 V1版本&#xff0c;是 《尼恩 大数据 面试宝典》姊妹篇。 这里特别说明一下&#xff1a;《尼恩 大数据 面试宝典》5个专题 PDF 自首次发布以来&#xff0c; 已经汇集了 好几百题&#xff0c;大量的大厂面试…

咖啡店小程序:吸引顾客的创新营销手段

近日&#xff0c;“酱香拿铁”的大火让大家再次把目标聚焦在年轻人都喜欢的咖啡上。现在咖啡已经成为年轻一代的社交硬通货&#xff0c;咖啡店也遍地开花。而随着移动互联网的快速发展&#xff0c;咖啡店小程序已经成为了各大咖啡店主的选择&#xff0c;因为它提供了便捷的方式…

Excel文件生成与下载(SpringBoot项目)(easypoi)

说明 通过接口&#xff0c;导出表格。 使用SpringBoot框架和easypoi表格解析框架&#xff0c;生成Excel表格&#xff0c;并通过接口下载。 表格示例 依赖 版本 <easypoi.version>4.4.0</easypoi.version>依赖 <!-- easypoi --> <dependency><…

计算机网络的故事——了解Web及网络基础

了解Web及网络基础 文章目录 了解Web及网络基础一、使用 HTTP 协议访问 Web二、HTTP 的诞生三、网络基础 TCP/IP四、与 HTTP 关系密切的协议 : IP、TCP 和 DNS 一、使用 HTTP 协议访问 Web 根据Web浏览器指定的URL&#xff0c;从对应的服务器中获取文件资源&#xff0c;从而显…

化繁为简 面板式空调网关亮相上海智能家居展 智哪儿专访青岛中弘赵哲海

面对中央空调协议不开放和智能家居协议不统一的问题&#xff0c;青岛中弘选择中央空调控制器这一细分赛道入局智能家居市场&#xff0c;始终贯彻“所有空调&#xff0c;一个网关”的产品技术理念&#xff0c;逐渐探索出一条中弘的发展路径和商业模式。 在2023年的SSHT上海国际智…

【【萌新的STM32学习-27--USART异步通信配置步骤】】

萌新的STM32学习-27–USART异步通信配置步骤 USART/UART 异步通信配置步骤 1.配置串口工作参数 HAL_UART_Init() 我们会在此处调用MSP中的回调函数 2.串口底层初始化 用户定义HAL_UART_MspInit() 配置GPIO NVIC CLOCK 等 3.开启串口异步接收中断 HAL_UART_Receive_IT() 4.…

蠕虫病毒流量分析案例

背景 某供排水集团的网络管理员对其网络的健康状况持认可态度&#xff0c;表示网络运行正常&#xff0c;没有发现异常行为。然而&#xff0c;由于网络环境变得越来越复杂&#xff0c;仅凭借传统的网络经验已经不能全面了解网络情况。因此&#xff0c;我们为供排水集团安装了Ne…