【jvm系列-05】精通运行时数据区共享区域---方法区

news2025/1/18 8:53:48

JVM系列整体栏目


内容链接地址
【一】初识虚拟机与java虚拟机https://blog.csdn.net/zhenghuishengq/article/details/129544460
【二】jvm的类加载子系统以及jclasslib的基本使用https://blog.csdn.net/zhenghuishengq/article/details/129610963
【三】运行时私有区域之虚拟机栈、程序计数器、本地方法栈https://blog.csdn.net/zhenghuishengq/article/details/129684076
【四】运行时数据区共享区域之堆、逃逸分析https://blog.csdn.net/zhenghuishengq/article/details/129796509
【五】运行时数据区共享区域之方法区、常量池https://blog.csdn.net/zhenghuishengq/article/details/129958466

运行时数据区共享区域---方法区

  • 一,运行时数据区共享区域---方法区
    • 1,方法区的基本概述
    • 2,方法区的演进过程
    • 3,方法区大小设置与OOM
      • 3.1,方法区内存大小的分配
      • 3.2,OOM的解决方案
    • 4,方法区的内部结构
      • 4.1,方法区存储数据概述
      • 4.2,static final
      • 4.3,运行时常量池
        • 4.3.1,什么是常量池
        • 4.3.2,什么是运行时常量池
    • 5,方法区的使用
    • 6,方法区的演进细节
      • 6.1,方法区的具体实现以及内部组成的演进
      • 6.2,字符串常量池为何要存储到堆中
    • 7,方法区的垃圾回收机制

一,运行时数据区共享区域—方法区

1,方法区的基本概述

方法区和堆一样,也是属于运行时数据区中的共享区域,并且也属于重要的一个内存空间,该空间主要是配合堆栈一起工作。

在这里插入图片描述

如下面这行代码,new User就是存在Java堆中,第一个User就是存在方法区中,第二个user就是作为局部变量表存储在栈中。

User user = new User();
#方法区:User
#栈:user
#堆:new User();

《Java虚拟机规范》中明确说明:“尽管所有的方法区在逻辑上是属于堆的一部分,但一些简单的实现可能不会选择区进行垃圾回收或者进行压缩”。但是对于HotSpot虚拟机而言,方法区还有一个别名就叫做Non-Heap(非堆),目的就是要和堆分开。因此,方法区可以看做是一块独立于Java堆的内存空间

🎄 方法区和java堆一样,属于是各个线程共享的区域

🎄 方法区在Jvm启动的时候被创建,他的物理内存和堆一样可以是不连续的

🎄 方法区的大小和堆空间一样,可以选择固定大小或者可扩展

🎄 方法区的大小决定了系统可以保存多少个类,如果系统定义了太多的类,导致方法区溢出,虚拟机同样会抛出内存溢出错误,如加载大量的第三方jar包,Tomcat部署的工程过多,大量的动态反射类等

🎄 关闭JVM之后,就会释放这个区域的内存

2,方法区的演进过程

这里主要是针对HotSpot虚拟机,在JDK8以前,习惯将方法区称为永久代;从JDK8开始,使用了这个元空间取代了永久代。就相当于把这个方法区当成是一个接口,而永久代和元空间就是该接口的具体实现。

In JDK8,classes metadata is now stored in the navite heap and this space is called MetaSpace

方法区和这个永久代并不等价,《Java虚拟机规范》对如何实现方法区,不做统一的要求。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Da8vWxpe-1680600889285)(img/1679972148964.png)]

到了这个 JDK8 之后,终于完全废弃了永久代的概念,改用JRockit、J9一样在本地内存中实现的元空间来代替。

元空间的本质和永久代类似,都是JVM规范中方法区实现的,不过元空间与永久代最大的区别在于:元空间不在虚拟机设置的内存中,而是使用的是本地内存

永久代和元空间二者不只是名字变了,内部结构也调整了,根据《java虚拟机规范》的规定,如果方法区无法满足新的内存分配需求时,将抛出OOM异常。

3,方法区大小设置与OOM

方法区的大小不必是固定的,jvm可以根据应用的需要动态调整。

3.1,方法区内存大小的分配

在jdk1.8之前

🎄 可以通过 -XX:PermSize 来设置永久代初始的分配空间,默认值为20.75M

🎄 通过 -XX:MaxPermSize 来设置永久代最大的分配空间,32位机器默认是64M,64位机器为82M

🎄 当JVM加载的类信息容量超过了这个值,会报异常OutOfMemoryError:PermGen

在jdk1.8及以后

🎄 元数据区大小可以使用参数 -XX:MetaspaceSize-XX:MaxMetaspaceSize 设置原始值和最大值

🎄 默认值依赖平台,windows下 -XX:MetaspaceSize 为21M,-XX:MaxMetaspaceSize 为-1,即没有限制

🎄 默认情况下,虚拟机会耗尽所有内存,如果元数据区溢出,虚拟机会抛异常OutofMemoryError:Metaspace

🎄 当内存高于设置的21M时,就会触发Full GC,Full GC就会卸载掉没用的类

🎄 因此建议将这个初始内存设置一个相对较高的值,以免频繁触发Full GC

3.2,OOM的解决方案

  • 要解决这些OOM异常或者heap space异常,一段手段是通过内存印象分析工具堆dump出来的堆转存储快照进行分析,重点是确认内存中的对象是否是必要的,也就是要分清楚是出现了内存泄漏还是内存溢出
  • 如果是内存泄漏,可进一步通过工具查看泄漏对象到GC Roots的引用链,于是就能找到泄漏对象是通过怎样的路径与GC Roots相关联。在掌握了泄漏对象的信息,以及GC Roots引用链之后,就可以准确的定位到泄漏代码的位置了。
  • 如果不存在内存泄漏,换句话就是说内存中的对象确实是还活着,那么就应该检查虚拟机的参数,与机器内存相比看是否还可以调大,从代码上检查是否存在某些对象的生命周期过长,持有时间过长等情况,从而减少运行期间的内存消耗。

4,方法区的内部结构

4.1,方法区存储数据概述

在将 .java 文件编译成 .class字节码文件之后,这个字节码文件是需要存储的,而类本身的一些信息,则需要存储在这个方法区里面,除了类信息之外,这个运行时常量池也是存储在这个方法区里面的。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tR6mct0T-1680600889286)(img/1680486141969.png)]

在《深入理解Java虚拟机》这本书中,对方法区存储内容的概述如下:它用于存储已被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等。 但是随着JDK的不断迭代,其内存存储的东西也会有着稍小的变化。

类型信息:对加载的类型,包括类class、接口interface、枚举enum、注解annotation等,JVM必须在方法区中存储以下类型

♟ 这个类型的完整有效名称,全名就是 包名.类名

♟ 直接父类的完整有效名,interface和Object都是没有父类的

♟ 这个类型的修饰符,如public、static、final、abstract

♟ 直接接口的一个有序列表,如这个类可能实现多个接口

属性信息:域的相关信息主要包括以下:域名称、域类型、域修饰符等

方法信息:方法信息主要包括一些方法名称、返回类型、参数的数量和类型、方法的修饰符、方法的字节码、局部变量表以及其大小、异常表等

4.2,static final

在静态变量中,一般是随着累的加载而加载,他们成为类数据在逻辑上的一部分,并且类变量被类所有的实例共享,即使类实例不存在也可以进行访问。

而被声明的final的类变量的处理方法则不同,该变量在编译阶段就会被分配了;而没有被声明final的类变量,在准备阶段进行初步的赋值,在初始化阶段进行一个最终的赋值。

如写一个Java测试类,定义一个被final修饰的类变量和不被final修饰的类变量,并且这个类型为基础数据类型

public class Test {
    public static final int i = 10;
    public static  int k = 20;

    public static void main(String[] args) {
        System.out.println(i+k);
    }
}

然后在编译好的文件中,输入反编译命令,并将最终输出的文件加载到zhs.txt文件中

javap -v -p Test.class > zhs.txt

接下来重点分析这两个变量,可以发现这个加了final修饰的i,在编译阶段就进行了分配,赋值为10;而没有加final修饰的k,在编译阶段没有赋予默认值,而是在准备阶段赋值的默认值,在初始化阶段赋予的最终值

public static final int i;
  descriptor: I
  flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL
  ConstantValue: int 10

public static int k;
  descriptor: I
  flags: ACC_PUBLIC, ACC_STATIC

4.3,运行时常量池

4.3.1,什么是常量池

一个有效的字节码文件除了包含类的版本信息、字段、方法以及接口等描述信息之外,还包含一项重要的信息,那就是常量池表(Constant Pool Table),其中包括各种字面量和对类型,字段和方法的符号引用,如下

Constant pool:
   #1 = Methodref          #6.#27         // java/lang/Object."<init>":()V
   #2 = Fieldref           #28.#29        // java/lang/System.out:Ljava/io/PrintStream;
   #3 = Class              #30            // com/fc/v2/util/Test
   #4 = Fieldref           #3.#31         // com/fc/v2/util/Test.k:I

一个Java源文件在编译之后会产生一个字节码文件,而字节码文件需要数据支持,通常这种数据量很大不能直接存储到字节码文件里面,因此就通过常量池的方式,提前将数据存储在常量池中,然后根据引用去获取对应的数据,如在栈帧的 动态链接 就是通过这种方式来获取数据的。

在常量池中存储的数据类型主要有:数量值,字符串值,类引用,字段引用,方法引用

常量池就可以看做成是一张表,虚拟机指令根据这张常量表找到执行的类名、方法名、参数类型和字面量等类型。

4.3.2,什么是运行时常量池

上面提到了常量池,常量池是字节码文件的一部分,用于存放编译期生成的各个字面量和符号引用,这部分内容在类加载之后存放到方法区的运行时常量池中;而运行时常量池是属于方法区的一部分,接下来详细的描述一下上面是运行时常量池

⚽ 在将类和接口加载到虚拟机之后,就会创建对应的运行时常量池

⚽ JVM会为每个已加载的类型都维护一个常量池,池中的数据可以通过索引访问

⚽ 运行时常量池包含多种不同的常量,包括编译期就已经明确的数值,如栈帧的大小,以及运行期间才能获取到的方法或者字段引用,此时不再是常量池中的符号地址#,而是具体的真实地址。

⚽ 运行时常量池具备动态性,如实际大小可能比计算的大小大

⚽ 当创建接口或者类的运行时常量池时,如果构造的运行时常量池所需要的内存空间超过了方法区所能提供的最大值,则JVM就会抛出OutOfMemoryError异常

5,方法区的使用

接下来查看一段简单的代码,如下

/**
 * @author zhenghuisheng
 * @date : 2023/4/4
 */
public class Test {
    public static void main(String[] args) {
        int x = 500;
        int y = 100;
        int a = x/y;
        int b = 50;
        System.out.println(a+b);
    }
}

然后编译之后,通过jclasslib插件查看对应的字节码,在前面的章节又讲如何安装使用

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uBRWlWCU-1680600889287)(img/1680577628874.png)]

其对应的字节码文件如下

 0 sipush 500
 3 istore_1
 4 bipush 100
 6 istore_2
 7 iload_1
 8 iload_2
 9 idiv
10 istore_3
11 bipush 50
13 istore 4
15 getstatic #2 <java/lang/System.out>
18 iload_3
19 iload 4
21 iadd
22 invokevirtual #3 <java/io/PrintStream.println>
25 return

这个具体的操作流程如下,由于没有new对象,因此在下图中暂时先不展示堆空间。

首先程序计数器的记录的地址为0,并将这个500这个数值压入到操作数栈中。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rtwA3FrZ-1680600889288)(img/1680577383605.png)]

然后就是这个istore_1,将操作数栈中存储的值存储到本地变量表中。而这个本地变量表,如果是实例方法或者是构造方法,第一个数据存储的应该是this,而这里的是static的静态方法,因此第一个slot存储的不是this。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lRt7QCtH-1680600889288)(img/1680578174090.png)]

后续的操作和上面的一样,这个iload_1就是将数据从本地变量本中取出来放到操作数栈顶,iload_2原理一样,然后结果这个idiv除法运算,将结果5存放在本地变量表3的位置

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4JeLuyz6-1680600889289)(img/1680578487922.png)]

随后就是将50入栈,也存储到本地变量表中4的位置,然后通过这个getstatic #2,就是获取常量池中的#2的位置,然后最终可以获取到这个System类,out类和对应的type属性,然后加载到方法区中。如果这些类或者属性在方法区中存在就不会进行加载,如果不存在就会将这些加载到方法区中,然后将这些#等符号的间接引用变成地址的直接引用。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pgTqNNY1-1680600889289)(img/1680578804544.png)]

然后通过这个iload3和iload4将本地变量表的数据加载到操作数栈中

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bqWqPPVX-1680600889290)(img/1680579228326.png)]

然后再经过iadd计算,再调用这个invokevirtual #3的符号引用,最终可以定位到一个打印操作,最终return结束

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6cq8gc5s-1680600889290)(img/1680579346070.png)]

通过上图可以发现,无论是在哪一个操作,程序计数器都会指向对应的执行位置。

6,方法区的演进细节

在虚拟机中,只有HotSpot虚拟机才有永久代,JRockit和J9是不存在永久代的概念的。在HotSpot虚拟机的方法区变化如下(方法区是一种概念,永久代和元空间属于具体实现):

  • 在jdk6及以前:有永久代,静态变量存放在永久代上面
  • 在jdk7中:有永久代,但逐步去除,字符串常量池、静态变量保存在堆中
  • 在jdk8及以后:无永久代,类型信息、字段、方法、常量保存在本地内存的元空间中,字符串常量池和静态变量在堆中

6.1,方法区的具体实现以及内部组成的演进

jdk6的方法区的组成如下,也称为永久代,其静态变量,运行时常量池,字符串常量池等都是保存在这个永久代的里面,并且字符串常量池是属于运行时常量池的一部分

在这里插入图片描述

而在jdk7开始,就将字符串常量池和静态变量存放在堆里面

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YV3PdXgK-1680600889291)(img/1680591492386.png)]

而从jdk8开始,永久代已经不存在了,取而代之的是本地内存的 元空间,会将运行时常量池,类信息等全部存储在本地内存中,并且静态变量和字符串常量池都是存储在堆中

在这里插入图片描述

在官方文档中https://openjdk.org/jeps/122,也提到过这个删除这个永久代的动机,如下,主要就是说参考了这个 JRockit 内部的实现,这样用户可以不必自己去配置这个永久代。这样这块空间可以不用jvm本身去管理,从而交给本地内存区实现。

This is part of the JRockit and Hotspot convergence effort. JRockit customers do not need to configure the permanent generation (since JRockit does not have a permanent generation) and are accustomed to not configuring the permanent generation.

其实这项改动还是很有必要的,接下来从两个方面来说明为啥要替换

  • 永久代的空间大小设置是很难确定的,在某些场景下,如果动态的加载类过多,就很容易出现OOM,比如一些Web项目。而元空间和永久代之间最大的区别在于:元空间不在虚拟机中,而是使用的是本地内存,因此元空间的大小只受本地内存限制
  • 永久代的调优比较困难

6.2,字符串常量池为何要存储到堆中

在jdk7的时候,将静态变量和字符串常量池都存储到了堆中,其主要原因是在永久代中,其触发的回收效率很低,在full gc的时候才会触发。而full gc的老年代空间不足,或者永久代空间不足时才会触发,因此这就导致了这个字符串常量池的回收率不高。

而在如今的开发中,可能会创建大量的字符串,如果还是存储在方法区内部,那么其回收率会比较低,很容易导致永久代的内存不足,因此选择将这个字符串常量池存放到堆中,这样就可以快速的实现内存回收

7,方法区的垃圾回收机制

在运行时数据区中,方法区又被称为non-heap,就是非堆的意思。并且在《java虚拟机规范》中描述,对方法区中的约束是非常宽松的,提到过不要求虚拟机在方法区中实现垃圾回收

一般来说这个区域的回收效果比较难令人满意,但是有时又确实是有必要的,因此在HotSpot虚拟机中,方法区的垃圾回收主要是回收两部分内容:常量池中废弃的常量和不再使用的类型

常量回收的策略就是只要该常量没有被任何地方引用,就可以被回收,回收废弃常量和回收Java堆的对象非常类似

类型回收的条件相对比较苛刻,需要同时的满足以下三点条件:

  • 该类的实例被回收,该类以及对应的子类在堆中不存在
  • 该类的类加载器已被回收
  • 该类的Class对象没有任何地方被引用,无法在任何地方通过反射访问到该类

满足这三点条件也不是一定会进行回收,而是可能被允许回收。在大量的使用反射、动态代理、CGLib这些字节码框架,动态生成Jsp等这些场景中,通常需要java虚拟机具备类型卸载的能力,以保证不会对方法区造成过大的压力

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

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

相关文章

Redis 6.0的多线程是如何工作的

来了解下 6.0 版本中新出的多线程特性。 1、多线程处理网络IO&#xff0c;单线程执行命令 Redis 一直被大家熟知的就是它的单线程架构&#xff0c;虽然有些命令操作可以用后台线程或子进程执行&#xff08;比如数据删除、快照生成、AOF 重写&#xff09;&#xff0c;但是&…

freeswitch带媒体压力测试方案

概述 原本的计划是使用sipp完成带媒体压力测试&#xff0c;但是实际测试过程中发现sipp的媒体处理功能有问题&#xff08;也有可能是我使用的姿势不对&#xff09;。 sipp在带媒体的情况下&#xff08;600路并发开始&#xff09;&#xff0c;出现大量的不响应和响应延迟&…

请求响应-响应

前面已经说了我们重点关注的就是XXXcontroller类 进行请求接收 和响应 接收参数那些我们在请求部分讲过了 现在我们来处理响应部分 响应 设置响应数据 可以发现其实我们之前都是设置过的 比如那个Hello World 浏览器都接收到了且在浏览器上进行了输出 这里的是返回值作为这个…

Direct3D 12——纹理——纹理

纹理不同于缓冲区资源&#xff0c;因为缓冲区资源仅存储数据数组&#xff0c;而纹理却可以具有多个mipmap层级(后 文有介绍)&#xff0c;GPU会基于这个层级进行相应的特殊操作&#xff0c;例如运用过滤器以及多重采样。支持这些特殊 的操作纹理资源都被限定为一些特定的数据格式…

7 个最好的 Word 转 PDF 转换器

如果您使用 Word 文件&#xff0c;您可能在某个时候遇到过将 Word 文件转换为 PDF 的紧迫问题。PDF 文件有很多优点。它们通常更紧凑&#xff0c;无论您在哪里打开它们看起来都一样。PDF 还允许您共享文档&#xff0c;而不必冒有人更改内容的风险。那么如何将 Word 文档转换为 …

小白学网络安全要学些什么?

一.网络安全学些什么呢&#xff1f; 虽然网上已经有非常多的学习路线了&#xff0c;但是仍然有很多零基础的小白还是不懂网络安全到底应该要怎么去学习&#xff0c;我也经常会在后台收到这样的问题“我想学网络安全&#xff0c;需要先学编程语言吗&#xff1f;”、“学渗透就业…

【python零碎】

1. 拼接字符中&#xff0c;插入变量 >>> shepherd "Mary" >>> age 32 >>> stuff_in_string "Shepherd {} is {} years old.".format(shepherd, age) >>> print(stuff_in_string) Shepherd Mary is 32 years old. &…

HIT-CSAPP 第五章 面向程序的优化方法(1)

考纲: 1.面向程序性能的优化 面向编译器的程序优化方法:减少过程调用、减少内存引用、指令并行等方法等方法。面向流水线、超标量、向量CPU的程序优化方法。 2.存储器的层次结构 优化编译器的能力和局限性 内存别名使用妨碍函数优化 void twiddle1(long *xp, long *yp){ //…

ERTEC200P-2 PROFINET设备完全开发手册(4-1)

ERTEC200P-2作为应用处理器&#xff0c;既可以单独使用&#xff0c;通过GPIO扩展实现基本的IO功能。也可以配合外部主机&#xff08;例如单片机&#xff09;实现更复杂的应用。ERTEC200P-2与外部主机接口的示意图如下。常用的接口包括UART&#xff0c;SPI&#xff0c;XHIF接口。…

MSI: 基于多元同步索引的SSVEP频率识别算法

MSI: 基于多元同步索引的SSVEP频率识别算法1.算法背景2.算法原理3.Python代码实现1.算法背景 脑机接口&#xff08;Brain-Computer Interface, BCI&#xff09;因其在神经工程与神经科学中的广泛应用价值而备受研究者们的关注。BCI系统可以在人类或动物被试与外部设备之间提供…

“深元AI”赋能传统加油站智能化转型,全力打造新一代智慧加油站

历届的全国两会和党代会上&#xff0c;“安全生产”始终是核心议题。党的二十大报告提出&#xff1a;推动公共安全治理模式向事前预防转型&#xff0c;并强调要加强重点行业、重点领域安全监管&#xff0c;提高防灾减灾救灾和重大突发公共事件处置保障能力。同时&#xff0c;国…

Linux_vim编辑器

Vi编辑器是所有Unix及Linux系统下标准的编辑器&#xff0c;类似于windows系统下的notepad&#xff08;记事本&#xff09;编辑器&#xff0c;由于在Unix及Linux系统的任何版本&#xff0c;Vi编辑器是完全相同的&#xff0c;因此可以在其他任何介绍vi的地方都能进一步了解它&…

Java的CPU 飙升700%优化的真实案例

最近负责的一个项目上线&#xff0c;运行一段时间后发现对应的进程竟然占用了700%的CPU&#xff0c;导致公司的物理服务器都不堪重负&#xff0c;频繁宕机。 那么,针对这类java进程CPU飙升的问题&#xff0c;我们一般要怎么去定位解决呢&#xff1f; 采用top命令定位进程 登…

spring初始项目创建

首先进入http://spring.p2hp.com/projects/spring-framework.html&#xff0c;点击git按钮 点击Access to Binaries中的链接 找到里程碑版本&#xff0c;要引入仓库地址 这里的spring-context依赖只是基础的spring框架的依赖 在resources目录下创建spring的xml文件&#xff0c…

中国31个主要城市绿地数据(空间分辨率为1m)

近年来&#xff0c;为了满足生态文明和可持续发展的理念&#xff0c;科学的城市绿地规划和管理在中国越来越受到重视。因此&#xff0c;提高UGS分类体系和布局布局的合理性&#xff0c;建设绿色宜居城市&#xff0c;是近年来政府和学者关注的重点。为此&#xff0c;本文选取中国…

ArcGIS、ENVI、InVEST、FRAGSTATS等多技术融合提升环境、生态、水文、土地、土壤、农业、大气等领域

专题一、空间数据获取与制图 1.1 软件安装与应用讲解 1.2 空间数据介绍 1.3海量空间数据下载 1.4 ArcGIS软件快速入门 1.5 Geodatabase地理数据库 专题二、ArcGIS专题地图制作 2.1专题地图制作规范 2.2 空间数据的准备与处理 2.3 空间数据可视化&#xff1a;地图符号与…

Terraform 系列-Terraform Cloud 比 Terraform OSS 有哪些增强?

系列文章 &#x1f449; Terraform 系列文章 前言 最近在使用 Terraform Cloud 来置备 OCI 的 Always Free Tier, 发现它非常好用&#xff0c;相比 Terraform OSS, 用起来省心多了。 也借此总结学习下&#xff1a;Terraform Cloud 比 Terraform OSS 有哪些增强&#xff0c;…

【从零开始学Skynet】实战篇《球球大作战》(一):功能设计

为了能把之前在基础篇中学习到的Skynet的各种知识结合起来&#xff0c;所以在实战篇中&#xff0c;我们准备开发一个完整的游戏案例《球球大作战》&#xff0c;介绍分布式游戏服务端的实现方法。 1、功能需求 《球球大作战》是一款多人对战游戏&#xff0c;下图是它的战斗场景…

C语言库函数(memcpy,memmove)的模拟实现

模拟实现memcpy函数 下面是memcpy的函数声明 void *memcpy(void *str1, const void *str2, size_t n) 参数 str1 -- 指向用于存储复制内容的目标数组&#xff0c;类型强制转换为 void* 指针。str2 -- 指向要复制的数据源&#xff0c;类型强制转换为 void* 指针。n -- 要被复…

stm32当中的EXTI外部中断系统

一. 中断系统 中断 &#xff1a; 在主程序运行过程中&#xff0c;出现特定的中断触发条件&#xff0c;使得CPU暂停当前正在运行的程序&#xff0c;而去处理中断程序&#xff0c;完成后&#xff0c;又返回原来被暂停的位置继续工作 中断优先 &#xff1a; 当有多个中断开始时&…