浅识JVM

news2025/6/30 4:58:13

在这里插入图片描述

请添加图片描述

⭐️前言⭐️

本篇文章,博主分享的是在面试中JVM常考的考点,希望这篇文章能够对你有用。

🍉博客主页: 🍁【如风暖阳】🍁
🍉精品Java专栏【JavaSE】、【备战蓝桥】、【JavaEE初阶】、【MySQL】、【数据结构】
🍉欢迎点赞 👍 收藏留言评论 📝私信必回哟😁

🍉本文由 【如风暖阳】 原创,首发于 CSDN🙉

🍉博主将持续更新学习记录收获,友友们有任何问题可以在评论区留言

🍉博客中涉及源码及博主日常练习代码均已上传码云(gitee)、GitHub


请添加图片描述

📍内容导读📍

  • 🍅1.JVM执行流程
  • 🍅2.JVM内存区域划分
    • 2.1 程序计数器
    • 2.2 栈
    • 2.3 堆
    • 2.4 方法区
  • 🍅3.JVM类加载机制
    • 3.1 类加载过程
      • 3.1.1 Loading
      • 3.1.2 Linking
      • 3.1.3 Initializing
    • 3.2 典型面试题
      • 3.2.1 代码块的执行先后
      • 3.2.2 双亲委派模型
  • 🍅4.JVM的垃圾回收(GC)
    • 4.1 什么垃圾需要回收?
    • 4.2 如何找垃圾(判定垃圾)?
      • 4.2.1 基于引用计数
      • 4.2.2 基于可达性分析
    • 4.3 垃圾如何回收(释放内存)?
      • 4.3.1 标记-清除算法
      • 4.3.2 复制算法
      • 4.3.3 标记-整理算法
      • 4.3.4 分代回收算法
    • 4.4 垃圾收集器
      • 4.4.1 串行收集器
      • 4.4.2 并行收集器
      • 4.4.3 CMS收集器
      • 4.4.4 G1收集器

请添加图片描述

🍅1.JVM执行流程

程序在执行之前先要把Java代码转换成字节码(class文件),JVM首先需要把字节码通过一定的方式如类加载器(ClassLoader),把文件加载到内存中的运行时数据区(Runtime Data Area),而字节码文件是JVM的一套指令集规范,并不能直接交给底层操作系统去执行,因此需要特定的命令解析器执行引擎(Execution Engine),将字节码翻译成底层系统指令再交给CPU去执行,而这个过程中需要调用其他语言的接口本地库接口(Native Interface),来实现整个程序的功能,这就是这4个主要组成部分的职责与功能。
在这里插入图片描述

🍅2.JVM内存区域划分

JVM的内存区域的划分就是指JVM的 运行时数据区(Runtime Data Area) 的划分,按照规范,其可以划分为以下几个区域:
在这里插入图片描述
JVM内存分为线程私有区和线程共享区,其中方法区是线程共享区,程序计数器是线程隔离的私有数据区。

2.1 程序计数器

该区域是内存中最小的区域,保存了下一条要执行的指令(指令就是字节码)的地址在哪里。

程序要想运行,JVM就需要把字节码加载起来放到内存中,然后程序把一条一条的指令从内存中取出来,放到CPU上执行,由于CPU是并发式执行程序的,它同时为多个进程服务,线程则更多,所以每个线程都得记录自己的执行位置,即每个线程都有一个程序计数器。

2.2 栈

在该区域存储的有局部变量方法调用信息

在方法调用的时候,每次调用一个新的方法,就都涉及到入栈操作,每次执行完了一个方法,就都涉及到出栈,每个线程都有这样一份栈。

2.3 堆

实例对象(new 出来的实例)在该区域存储

因为实例对象在堆区域存储,所以对象的成员变量也在该区域存储。

一个进程只有一份堆,多个线程共用一个堆

2.4 方法区

类对象存储在方法区中

程序的.java文件转为.class字节码文件后,通过类加载器加载到内存中,就被JVM构造成了类对象,此处的类对象就在方法区中;
类对象描述了这个类的细节,类的名字、里面有哪些成员/方法,每个成员/方法叫啥名字是啥类型等类信息。
类对象中还有很重要的静态成员,也就是被static修饰的成员,其也是类属性;而普通的成员,叫做实例属性

🍅3.JVM类加载机制

3.1 类加载过程

JVM的类加载,就是为了把.class文件加载到内存中,构建成类对象;具体加载过程见下图:
在这里插入图片描述
类加载包括三个步骤:Loading,Linking,Initializing
可参考官方文档:https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-5.html
在这里插入图片描述

3.1.1 Loading

先找到对应的.class文件,然后打开并读取.class文件,把读取并解析到的信息,初步的填写到生成的类对象中。

.class文件的格式如下图,JVM就按照这个格式来加载生成类对象
在这里插入图片描述

3.1.2 Linking

1)Verification
校验阶段

主要就是验证读到的内容是不是和规范中的规定格式完全匹配,如果发现这里读到的数据格式不符合规范,就会类加载失败,并且抛出异常。

2)Preparation
准备阶段

该阶段主要完成的工作是正式为类中定义的变量(即被staitic修饰的静态变量)分配内存,并设置类变量的初始值。

3)Resolution
解析阶段

该阶段主要完成的工作是Java虚拟机将常量池内的符号引用替换为直接引用的过程,也就是初始化常量的过程。

3.1.3 Initializing

真正地对类对象进行初始化,特别是静态成员,Java虚拟机真正开始执行类中编写的Java程序代码,将主导权移交给应用程序。

3.2 典型面试题

3.2.1 代码块的执行先后

执行以下代码块:

class A {
    public A() {
        System.out.println("A的构造方法");
    }
    {
        System.out.println("A的构造代码块");
    }
    static {
        System.out.println("A的静态代码块");
    }
}

class B extends A {
    public B() {
        System.out.println("B的构造方法");
    }
    {
        System.out.println("B的构造代码块");
    }
    static  {
        System.out.println("B的静态代码块");
    }
}
public class Test {
    public static void main(String[] args) {
        new B();
        System.out.println("----------");
        new B();
    }
}

//
A的静态代码块
B的静态代码块
A的构造代码块
A的构造方法
B的构造代码块
B的构造方法
----------
A的构造代码块
A的构造方法
B的构造代码块
B的构造方法

由以上代码的执行结果可得:

对象创建时代码的加载顺序为:静态代码–>非静态代码–>构造方法

若继承了父类,则加载顺序为:父类的静态代码块–>子类的静态代码块–>父类内部非静态代码–>父类的构造方法–>子类的非静态代码块–>子类的构造方法

静态代码块在类加载的时候只会执行一次,再实例化对象时并不会再次执行静态代码块。

3.2.2 双亲委派模型

双亲委派模型,描述的是JVM中的类加载器,如何根据类的全限定名(java.lang.String)来找到.class文件的过程。

类加载器是专门负责进行类加载的对象,默认的类加载器主要有三个,每个类加载器负责一个片区:

1.启动类加载器 (Bootstrap Class Loader)
负责加载标准库中的类(String,ArrayList,Random,Scanner…)

2.扩展类加载器 (Extension Class Loader)
负责加载JDK中扩展的类(较少用到)

3.应用程序类加载器 (Application Class Loader)
负责加载当前项目目录中的类

程序猿还可以自定义类加载器,来加载其他目录中的类

例如Tomcat就自定义了类加载器,用来专门加载webapps里面的.class

双亲委派模型就描述了上述类加载器如何通过配合,来找目录的过程:
在这里插入图片描述

1)加载java.lang.String

a. 程序启动,先进入ApplicationClassLoader类加载器。

b. ApplicationClassLoader会检查其父加载器是否已经加载过了;如果没有就调用父 类加载器 ExtensionClassLoader

c. ExtensionClassLoader也会检查其父加载器是否加载过了;如果没有就调用父 类加载器 BootStrapClassLoader

d. BootStrapClassLoader也会检查其父加载器是否加载过了;但因为其没有父加载器,于是就扫描自己负责的目录

e. java.lang.String这个类在标准库中能找到,则直接由BootStrapClassLoader负责后续的加载过程

在这里插入图片描述
2)加载自己写的Test
a. 程序启动,先进入ApplicationClassLoader类加载器。

b. ApplicationClassLoader会检查其父加载器是否已经加载过了;如果没有就调用父 类加载器 ExtensionClassLoader

c. ExtensionClassLoader也会检查其父加载器是否加载过了;如果没有就调用父 类加载器 BootStrapClassLoader

d. BootStrapClassLoader也会检查其父加载器是否加载过了;但因为其没有父加载器,于是就扫描自己负责的目录;没有扫描到,回到子加载器继续扫描

e. ExtensionClassLoader也扫描自己的目录,也没扫描到,回到子加载器继续扫描

f. ApplicationClassLoader也扫描自己负责的目录,能找到Test类,于是进行后续加载,查找目录环节结束

如果最终ApplicationClassLoader也找不到,就会抛出ClassNotFoundException的异常

在这里插入图片描述
这样的查找规则就叫做双亲委派模型

🍅4.JVM的垃圾回收(GC)

在我们写代码的时候,经常会通过创建变量,new 对象或者加载类等方式来申请内存,但我们不能只申请内存而不释放。

申请内存的时机我们一般都是明确的(需要保存某些数据时,就需要申请内存),但是释放内存的时机,则不是那么清楚;

内存释放的早或者晚都会影响开发效率,所以我们需要让内存释放的时机恰到好处;

Java,Go,Python等较为主流的编程语言就采取了垃圾回收机制(GC),靠运行时环境额外多做一些工作,来完成释放内存的操作,让程序猿的工作量减少。

同样,垃圾回收机制也带来了一些劣势:

  • 消耗额外的开销(消耗的资源更多了)
  • 可能会影响程序的流畅运行(会引入STW问题)

STW<=>Stop The World 时间静止,就是卡了。

4.1 什么垃圾需要回收?

在JVM的内存中,划分有很多区域,我们在2中也有介绍,但是不是这些所有的区域都需要进行垃圾回收;

程序计数器占据固定大小的内存,不涉及释放,所以也就用不到GC;在函数执行完毕后,对应的栈帧就自动释放了,也不需要GC;是最需要GC的,因为代码中大量的内存都是在堆上;方法区中存储类对象,进行类卸载操作需要释放内存,但类卸载操作是一个非常低频的操作,所以也不需要过多考虑,只需考虑堆区域内存的垃圾回收即可。

在堆区域中,空间被分为三部分,一部分是正在使用的内存,一部分是不再使用,但尚未回收的内存,另一部分是未分配的内存;同时也有三类对象在这三部分区域中存在,一种是“积极派”,一种是“消极派”,还有另一种是“中间摇摆派”。
在这里插入图片描述
在上述的三个对象中,JVM只会回收“消极派”对象,GC回收是以对象为单位的,并不会出现半个对象的情况。

我们知道了什么垃圾需要回收,下边就需要我们来进行具体的垃圾回收操作,第一阶段需要我们找垃圾(判别垃圾),第二阶段就是释放垃圾,下边具体介绍:

4.2 如何找垃圾(判定垃圾)?

4.2.1 基于引用计数

该方案的具体操作是,针对每个对象,都会额外引入一小块内存,保存这个对象有多少个引用,当这个引用计数为0时,也就说明这个内存不再使用了,就释放了。

void func() {
	Test t=new Test();
	Test t2=t;//t和t2都是指向new对象的引用
}

在这里插入图片描述
如上边的func()方法的调用过程中,创建了分配对象(分配了内存),在方法的执行过程中,引用计数是2,当方法执行结束后,由于t和t2都是局部变量,就跟随栈帧一起释放了,如此而来引用计数就变为了0,此时就认为这个对象是垃圾。

通过引用计数的方法来找垃圾,确实简单可靠高效,但也存在两个问题:

1.空间利用率较低
每个new的对象都需要额外开辟一块空间来存储计数器,如果对象本身就很小,而且还需要计数器,就会导致空间浪费很大。

2.会有循环引用的问题
通过以下代码来引入这个问题:

class Test {
	Test t=null;
}
Test t1=new Test();
Test t2=new Test();

t1.t=t2;
t2.t=t1;
---------------------
t1=null;
t2=null;
//代码执行到这里就会出现循环引用的问题

在这里插入图片描述
在代码执行完最后一行后,两个对象的引用计数不为0,但是引用在彼此身上,外界的代码无法访问到这两个对象,此时此刻,这两个对象既不能使用,也不能释放,就出现了“内存泄漏”的问题。

4.2.2 基于可达性分析

该方案是Java采取的方案。

可达性分析就是通过额外的线程,对整个内存空间的对象进行扫描,首先会找到一些起始位置GCRoots,从这些位置开始,以类似深度优先搜索的方式去遍历对象,它会对所有可以到达的对象进行标记,无法到达的对象自然也就没有标记,就被视为垃圾。
在这里插入图片描述
可以作为GCRoots的对象有以下几种:

  • 栈上的局部变量
  • 常量池中的引用指向的对象
  • 方法区中的静态成员指向的对象

可达性分析的方法克服了引用计数中空间利用率低,循环引用两个缺点,但其也有自身的缺点:系统开销较大,遍历一次可能比较满。

结合以上两种方法,找垃圾的核心就是确定这个对象未来是否还会使用了,如果没有引用指向这个对象这个对象就是垃圾,不再使用了

4.3 垃圾如何回收(释放内存)?

垃圾回收算法共有以下四种:

4.3.1 标记-清除算法

标记就是可达性分析的过程,清除就是直接释放内存,如果直接释放内存,虽然内存是还给系统了,但是被释放的内存还是离散的(不连续)
在这里插入图片描述

在需要申请内存时,并不能提供连续的内存,这将非常影响程序的执行。

4.3.2 复制算法

为了解决内存碎片,引入了复制算法

复制算法就是把内存分为两部分,直接把不是垃圾的,拷贝到另一半,然后把原来的空间整体释放掉。
在这里插入图片描述

但该算法存在的问题有:

  • 内存空间利用率低
  • 如果要保留的对象多,要释放的对象少,此时复制开销就很大

4.3.3 标记-整理算法

该算法又针对复制算法做出了改进,该算法的实现类似于顺序表中删除中间元素的操作。
在这里插入图片描述
这个方法的空间利用率虽然提高了,但是仍然没有解决复制/搬运元素开销大的问题。

4.3.4 分代回收算法

上述的三种算法,虽然都能解决问题,但都有缺陷,在实际的JVM中的实现,会把多种方法结合起来使用,就形成了分代回收

针对对象进行分类(根据对象的“年龄”分类),一个对象经历过一轮GC扫描,就认为是“长了一岁”,针对不同年龄的对象,采取不同的方案,这就是分代回收

在内存中划分出如下图的区域划分:
在这里插入图片描述
1.刚创建出来的对象,就放在伊甸区

2.如果伊甸区的对象熬过一轮GC扫描,就会被拷贝到幸存区(应用了复制算法

3.在后续的几轮GC中,幸存区的对象就在两个幸存区之间来回拷贝(复制算法),每一轮拷贝都会淘汰掉一波幸存者

4.在持续若干轮之后,对象进入老年代,老年代中的对象都是比较老的,也就是继续存活的可能性是越大的,因此在老年代中的GC扫描频率大大低于新生代,所以在老年代中使用标记-清除标记-整理的方式来进行回收。

4.4 垃圾收集器

在JVM里,上述垃圾回收算法具体实现的模块就是垃圾收集器,下边我们来了解一些常见的垃圾收集器

4.4.1 串行收集器

串行收集就是指,在进行垃圾的扫描和释放的时候,业务线程要停止工作,这种方式扫描的慢,释放的也慢,会产生严重的STW(Stop The World,时间静止/卡顿)

基于这一方式的收集器有:

  • Serial收集器(新生代收集器,串行GC)
  • Serial Old收集器(老年代收集器,串行GC)

4.4.2 并行收集器

并行收集,就是通过引入多线程,来进行垃圾的扫描和释放任务,效率比串行更高

基于这一方式的收集器有:

  • ParNew收集器(新生代收集器,并行GC)
  • Parallel Scavenge收集器(新生代收集器,并行GC)
  • Parallel Old收集器(老年代收集器,并行GC)

4.4.3 CMS收集器

该收集器设计的初衷就是尽可能的让STW时间短,其具体流程如下:

1)初始标记
速度很快,只会引起短暂的STW(这一步只是找到GCRoots

2)并发标记
虽然速度慢,但是可以和业务线程并发执行,不会产生STW

3)重新标记
2)的业务代码可能会影响并发标记的结果,3)就是针对2)的结果进行微调,虽然会引起STW,但是很快。

4)回收内存
该操作也与业务线程并发

前三步就是可达性分析分开来完成了,最后一步就是通过标记-整理来处理老年代对象。

4.4.4 G1收集器

G1收集器是唯一一款全区域的垃圾回收器,该收集器把整个内存,分成了很多小的区域-Region,有的Region放新生代对象,有的放老年代对象,然后在扫描的时候,一次扫描若干个Region(不追求一轮GC就扫描完,分多次来扫描),这样对于业务代码的影响最小。


⭐️最后的话⭐️
总结不易,希望uu们不要吝啬你们的👍哟(^U^)ノ~YO!!如有问题,欢迎评论区批评指正😁

请添加图片描述

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

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

相关文章

多维度比对三种软件开发方式后,无代码开发到底赢在哪?

近年来受信息化、数字化和 5G、云计算发展的影响&#xff0c;以及企业管理的不确定性和复杂性增加&#xff0c;国内管理软件的需求及行业市场整体规模也连年高速增长&#xff0c;进而带动了管理软件开发方式的变革。 但是由于传统自主及外包开发方式存在的周期长、成本高、不能…

DataX二次开发——(9)新增s3reader和s3writer模块

1 背景 DataX3.0支持阿里的OSS的读写&#xff0c;但没支持S3的读写&#xff0c;虽然OSS的也是基于S3协议去做二开的&#xff0c;但是一些参数有点区别&#xff0c;所以按照阿里的OSSReader和OSSWriter开发了S3Reader和S3Writer。 2 代码开发 2.1 s3reader 2.1.1 项目结构 2…

pytorch快速上手(8)-----pytorch优化器简介

文章目录一、简介二、optimizer属性方法1. zero_grad()2. step()3. add_param_group()4. state_dict()5. load_state_dict()学习率动量三、常见优化器介绍1. BGD&#xff08;Batch Gradient Descent&#xff09;2. Stochastic Gradient Descent&#xff08;SGD&#xff09;3. M…

记录一次因执行时间过长锁已经释放导致finally块再次unlock引发的异常

一、前言 因为我的一个需求需要请求一个耗时比较长的接口&#xff08;耗时长其实是对接方的锅&#xff09;&#xff0c;该接口交给了Spring事务管理&#xff0c;并且使用了分布式锁&#xff0c;但是在请求的时候&#xff0c;出现error,看日志发现是unlock的时候没有锁可以去解…

牛客网语法篇练习复合类型(二)

1.输入NxM矩阵&#xff0c;矩阵元素均为整数&#xff0c;计算其中大于零的元素之和。 a,b map(int,input().split()) list1 [] sum 0 for i in range(a):list1.extend(list(map(int,input().split()))) for i in list1:if i>0:sumi print(sum) 2.给你一个整数n&#xff…

辨别代码能否引发线程安全问题--避免在平时写代码时引发线程安全问题

前景提要&#xff1a; 本篇文章只是入门&#xff0c;目的在于在脑海中构建一个Java运行的模型&#xff0c;然后可以在平时写代码时对是否引发线程安全问题有感知。 文章目录引入了解辨别线程安全问题之前先来构建一个计算机运行模型了解线程安全问题怎么能不知道线程和进程了解…

搭建repo服务器管理多个git工程

参考自&#xff1a;搭建repo服务器管理多个git工程     repo系列讲解 —— Android系统源码(AOSP)下载 1、repo介绍 Android使用git作为代码管理工具&#xff0c;开发了gerrit进行代码审核&#xff0c;以便更好的对代码进行集中式管理。还开发了repo命令行工具&#xff0…

MySQL8.0 binlog进阶

MySQL8.0经过这几年的操揉磨治&#xff0c;已经上升到海平面了。其中binlog也悄然无声带来了不一样的变化。高可用核心复制基础binlog变化更应该进一步了解。从参数入手&#xff0c;了解带来的变化。 slave回放算法 slave_rows_search_algorithms 当使用基于 row-based复制格…

牛客网语法篇练习循环控制(二)

1.今天牛牛学到了回文串&#xff0c;他想在数字里面找回文&#xff0c;即回文数&#xff0c;回文数是正着读与倒着读都一样的数&#xff0c;比如1221&#xff0c;343是回文数&#xff0c;433不是回文数。请输出不超过n的回文数。 a int(input()) for i in range(1,a1):n str…

CSS-counter 计数器详细教程+使用场景示例

counter一. counter计数器二. 属性和方法1. 计数器命名/重置2. 计数器-值递增规则3. 计数器显示 counter() / counters() 函数三 代码示例1. 重新开始计数2. counters嵌套使用3. 借助CSS计数器呈现CSS var变量值一. counter计数器 计数器是一种特殊的数字跟踪器&#xff0c;通常…

systemd的unit配置文件详解

Systemd 是 Linux 的系统和服务的管理器&#xff0c;兼容 SysV 和 LSB初始化脚本&#xff0c;Systemd有以下特性&#xff1a; 积极的并行化能力使用套接字和 D-Bus 激活来启动服务提供按需启动守护进程&#xff0c;使用 Linux cgroups 跟踪进程支持系统状态的快照和恢复维护挂…

艾美捷Cas9核酸酶应用说明及实例展示

Product Description:Recombinant Streptococcus pyogenes Cas9 (wt) protein expressed in an E. coli . Form:Liquid Preparation Method:E. coli expression system Purity:≥ 95% by SDS-PAGE Activity:20 nM CRISPR/Cas9-C-NLS nuclease incubated for 1 hour at 37℃…

【新知实验室 TRTCIM】实时互动课堂最佳实践

【新知实验室 TRTC&IM】实时互动课堂最佳实践一、新知实验室-TRTC腾讯云音视频产品体验官计划活动简介二、产品简介TRTCIM三、最佳实践3.1 官方快速上手TRTC(快速跑通)3.1.1 注册腾讯云账号3.1.2 使用实时音视频(需先开通)3.1.3 创建应用3.1.4 查看项目(查看密钥和快速上手…

java基础—String

我们都知道 创建一个字符串最简单的方式是 String meaasge "java资讯";当然还可以用构造来创建 &#xff08;不推荐&#xff0c;开发中不要用&#xff09; String str2new String("java资讯");这两种创建最主要的区别在于&#xff0c;一个在公共池中&…

gitlab CI/CD 自动化部署vue项目到阿里云服务器步骤

目录1&#xff0c;gitlab托管vue项目2&#xff0c;本地项目连接到远程仓库3&#xff0c;设置gitlab-runner4&#xff0c;编写yml文件5&#xff0c;部署到阿里云服务器&#xff08;本地设置&#xff09;5.1 安装相关依赖5.2 vue项目中添加deploy.js文件5.3 注册deploy命令5.4 验…

文本生成图像工作简述2--常用数据集分析与汇总

文本到图像的 AI 模型仅根据简单的文字输入就可以生成图像。用户可以输入他们喜欢的任何文字提示——比如&#xff0c;“一只可爱的柯基犬住在一个用寿司做的房子里”——然后&#xff0c;人工智能就像施了魔法一样&#xff0c;会产生相应的图像。 文本生成图像&#xff08;te…

实验2:Arduino的nRF24L01双向收发实验

实验结果: 00节点向01发送:00ReqMesFor01 01节点向00发送:CodeNewNiceBoy 并且在串口打印出相应信息 硬件电路: 01 软件 00节点代码: /*00 */#include <SPI.h> #include <nRF24L01.h> #include <RF24.h> RF24 radio(9, 10);// CE, CSNconst char te…

dolphinscheduler 2.0.5 性能手动测试

目录&#x1f42c;官方配置文件说明&#x1f42c;测试并发量&#x1f420;线程数量设置100&#x1f420;线程数量设置200&#x1f420;线程数量设置500&#x1f42c;测试结论&#x1f42c;官方配置文件说明 官方说明 master.exec.threads&#xff1a; master工作线程数量,用于…

智能晾衣架(二)--功能实现

本文素材来源于红河学院 工学院 作者&#xff1a;赵德森 张艺锦 潘志慧 曹紫康 指导老师&#xff1a;江洁 张龙超 1. 自动升降功能 我们设计时采用了热释电传感器&#xff08;人体红外传感器&#xff09;&#xff0c;在热释电传感器感应到有人靠近时&#xff0c;晾衣架通…

C++:内存管理:C++内存管理详解(二):带你攻破内存管理

前言&#xff1a; 任何程序运行起来都需要分配内存空间存放该进程的资源信息&#xff0c;C程序也不例外。C程序中的变量、常量、函数、代码等等信息所存放的区域都有所不同&#xff0c;不同的区域又有不同的特性。 欺骗C进程 每一个C语言的程序被执行起来的时候系统为了方便开…