微灯手握寸笔,细谈内存管理

简介: 正片开始👀地址空间👏

image.png

首先我们回味一下之前的老图,这个图由于是我手残加 ppt 即时创作,又因为是C语言入门时讲的,内容非常粗糙磕碜。要仔细研究这张图我们应该将它翻转90度会更加容易理解更贴近原理:

image.png

我们所熟知的,栈区数据存储的地址是从高地址到低地址,堆区数据存储的地址则是由低到高,而堆区下面可细分为未初始化和已初始化的全局数据区,字符常量区和代码区。而细心的你可能注意到了我代码区下面留了一撮空间代表下面还有,但这一撮属于灰色地带,目前看作为 “内存” ,但本质上不是内存,涉及到计算机操作系统原理不赘述。


从内存中0x000……0到堆区的地方其实基本上伴随整个程序的运行一直都存在,我们相对熟悉的就是栈区和堆区,栈区的我们函数调用后临时变量在栈帧中形成,随着申请与释放来进行空间管理。


那伴随整个程序的运行一直都存在的这部分数据,像 static 这类函数修饰的变量,为什么又会被改变生命周期呢?其实在编译的时候就被编译进了全局数据区。


指针与内存关系👏

void function(char *a)
{
  return 1;
}

我们在写函数时如果内容传的是指针,如果有好的习惯一般会先去对指针做一下合法性判定,这个判定什么意思,比如我们传了一个野指针,它会指向内存中任何一个位置,我是没有办法确认这个随机位置有无访问权限,所以要做合法性判定。


但是指针如果有具体的指向,对应的合法性我们是没办法验证的,包括野指针,是不是很疑惑,野指针不是随机指向,说白了就是乱指,那还没办法验证吗?是的,没办法。很简单,确认指针具体值的合法性,这不是咱作为用户可以做到的,这属于操作系统职责。

这种尴尬的情况我们所谓的合法性判定怎么搞呢?我们所谓的“合法”是落足于应用层面。其实所有的指针在没有被使用时,我们都应该设置成 NULL,这是一个规范问题。


在函数内部要验证指针合法性时,本质上就是在验证指针( !=NULL)。可以直接 if 判断,还有就是很多书中用的一个检查指针的宏——assert ,一般是在调试阶段使用,assert(name)如果内部条件不满足非空,就会直接咔嚓掉,中道崩殂没有后续。但是不好意思,assert只能检验是否 NULL,不能检验是否为野指针。


内存分配与初始化细节👏

之前就想专门提一下几个和内存空间有关联的函数,现在就放在这里一起总结了吧。


我们为指针分配了内存,但是内存大小多少会影响实际结果,不够就会造成越界。

char *p = "hello";
char *q = (char*)malloc(sizeof(char)*strlen(p)+1*sizeof(char));
strcpy(q,p);

image.png

p是字符串变量,长度为 5 个字符,但实际内存占用 6 个字符,不要忘了" \0 ",所以我们做 +1 处理,分配完了记得要初始化,初始化为非必须操作,但建议初始化,这是为了能让咱编码盘的明明白白。我们初始化变量时直接 0 或者 NULL,数组可以 = {0},也可以使用 memset 函数:

memset(a,0,sizeof(a));

它的 3 个参数分别代表起始地址,初始化设置的值以及设置的内存大小,单位为字节。

内存泄漏👏

int main()
{
while(1)
{
int *p = malloc(1024);
}
}

这里不做测试了,这会让电脑越来越卡,死循环加申请空间,程序级别的老赖,空间只借不还,以上代码就生动诠释了何为内存泄漏。


给个C语言之外的问题:程序挂了,已经退出了,那内存泄露问题还在吗?我自己的想法是在的,因为内存已经被申请了,退出程序只是终止了空间继续申请,不影响已产生的空间。但是我错了,其实在程序退出时,操作系统会强制拿回这部分空间,内存泄漏也就不在了。


所以诸位警惕windows的操作系统和杀毒软件这类常驻进程,几乎从来不会退出,最怕的就是内存泄漏,蓝屏安排,卡顿安排;但后端的服务器也是如此,无时无刻提供服务,一但内存泄露就会嘿嘿。


Cookie👏

malloc 之后空间要给 free 掉,我们 free(p)目前只知道堆空间的起始地址,并不知道要释放多少空间,如果 p 是 5 个字节,那么 free 一定会释放的比5个字节多,那么辩证思维,其实申请的空间就一定会比 5 个字节多。


编译器是怎么做到正确释放呢?其实实际 malloc 申请空间的时候,系统就会给的更多,多出来的部分,记录的就是申请的详细信息:空间大小,申请时间等等,free 会确认信息然后精准 free掉。


这部分多申请的空间叫 cookie,内存级的 cookie,就是用来保存这些信息的。再延伸就是C语言的边界操作系统了,不赘述。


所以我们在 malloc 时肯定是申请大空间会更好,因为 cookie 的比例会更小,想象一下利息相同时你会借多借少就能体会了

相关文章
|
3月前
|
算法 调度 UED
揭秘操作系统背后的暗战:进程调度与优先级反转的惊心动魄!
【8月更文挑战第21天】操作系统核心管理计算机资源,进程调度为其关键功能,决定CPU使用权,影响系统性能与用户体验。优先级反转是多任务环境下常见挑战:高优先级进程因等待低优先级进程占用的资源而被阻塞,导致系统效率下降。通过优先级继承或提升机制可解决此问题,确保系统资源高效利用与响应时间优化。
41 1
|
1月前
|
存储 C语言 C++
【C/C++内存管理】——我与C++的不解之缘(六)
【C/C++内存管理】——我与C++的不解之缘(六)
|
3月前
|
Java UED 开发者
揭开Java性能提升之谜:异步编程如何让你的应用响应速度飞升?
【8月更文挑战第30天】随着互联网技术的发展,系统性能和用户体验成为关注焦点,异步编程因其能提高应用响应速度和吞吐量而在Java领域广泛应用。本文将详细阐述Java异步编程的概念与优势,并通过实战示例展示其在实际项目中的应用,如使用`Future`、`Callable`及`CompletableFuture`等接口和类实现异步操作,帮助开发者更好地理解和运用这一技术,以提升程序性能和用户体验。
28 0
|
3月前
|
前端开发 JavaScript Java
揭开 JavaScript 垃圾回收的秘密——一场与内存泄漏的生死较量,让你的代码从此焕然一新!
【8月更文挑战第23天】本文通过多个实例深入探讨了JavaScript中的垃圾回收机制及其对应用性能的影响。首先介绍了基本的内存管理方式,随后分析了变量不再使用时的回收过程。接着,通过事件监听器未被移除及全局变量管理不当等场景展示了常见的内存泄漏问题。最后,文章介绍了使用`WeakRef`和`FinalizationRegistry`等现代API来有效避免内存泄漏的方法。理解并运用这些技术能显著提升Web应用的稳定性和效率。
91 0
|
存储 算法 程序员
拟内存管理技术
拟内存管理技术
95 0
|
存储 Java
java内存机制详解,老年人也看得懂
java内存机制详解,老年人也看得懂
76 0
【自考】第一遍思维导图(经济学+运筹+操作系统)
【自考】第一遍思维导图(经济学+运筹+操作系统)
61 0
|
自然语言处理 搜索推荐 安全
想知道企业需不需要大热的Sitecore CMS,弄清楚这十点就够了!
毫无疑问对于企业来说,数字化转型是长期霸榜的热门话题。而这其中Sitecore又凭借着个性化数字体验、全渠道数据洞察、自动化数字营销成为了这一话题的中心。
171 0
并发程序设计,你真的懂吗?
并发程序设计,你真的懂吗?
96 0
并发程序设计,你真的懂吗?
|
存储 缓存 Linux
真棒! 20 张图揭开内存管理的迷雾,瞬间豁然开朗
本篇跟大家说说内存管理,内存管理还是比较重要的一个环节,理解了它,至少对整个操作系统的工作会有一个初步的轮廓,这也难怪面试的时候常问内存管理。
真棒! 20 张图揭开内存管理的迷雾,瞬间豁然开朗

相关实验场景

更多