重新学习 c 语言(4)- 库和宿主实现(四) 动态内存分配

(四)动态内存分配

内存的使用问题和并发问题似乎占据了程序员一半以上的困扰!所以在现代一些语言中,尽量避开程序员自己直接控制内存的使用(主要是动态内存分配的使用).在若干年前的dos时代储存器基本上是公开访问的,现在操作系统给程序员加入各种屏障! 但在嵌入式领域,为了追求性能,操作系统去掉了虚拟存储器的概念,变量指向了实际的内存地址!

虚拟存储器是一个抽象的概念,当你的程序引用一个”内存”变量,你不知道他的实际存贮在什么地方,是物理内存,是磁盘虚拟内存,还是高速缓存中等等!

下面是一个存储器的层次金字塔图形


cache.jpg

对于计算机上的存储是一个层次结构,如图越向上,容量越小,单位容量价格越高,但存储速度越快.对于操作系统而言,速度慢的存储器扮演着速度快存储器的缓存(一般来说最快的是CPU),所以一级catch可以说是寄存器的缓存,二级catch是一级catch的缓存,主内存是二级catch的缓存,本地磁盘是主内存的缓存…

如果对程序性能要求很高,就用充分理解这种缓存机制!这方面的文章也很多.

下面说说动态内存分配的问题!

从前面讨论可以知道,在c语言中由#define定义的常量是在预编译做了宏替换,而在程序中的这些字面量有的被编译到代码中(ELF文件的.text段),有的在只读数据段(比如字符串,另外双精度double占用8个字节,运算时需要FPU 的ST寄存器或者叫浅栈,不容易编译到代码中),剩下的有初值的外部变量(包括static限定的变量)在数据段(.data),为赋初值的外部变量在.bss段中(ELF文件没有给分配空间,加载到内存映像时分配),还有一种是局部变量(自动变量)在用户栈中(参看前面的内存映像图).前面我们讲过变量生存期的问题,局部变量决定于调用栈,生存周期在函数调用过程中,外部变量(包括static限定的变量)在整个进程运行期中,还有一种情况就是通过malloc函数调用动态生存期的变量,他们的生存期是程序员自己控制的(其实,还要取决于malloc等函数的实现,不过先这么认为吧)!理解动态内存分配先要有下面的前提概念:

首先,内存分配(包括内核加载,用户进程加载,动态库加载等等)都是建立在操作系统的虚拟内存分配之上的!关于虚拟内存分配可以参考任何OS的书籍(Unix,Win32都可以,大同小异).其基本含义是: (1)进程使用的内存地址是虚拟的(每个进程感觉自己拥有所有的内存资源),需要进过各种地址翻译最终指向实际的物理地址(2)主内存和磁盘采用页交换的方式加载进程和相关数据,也就是说数据何时加载到主内存,何时缓存到磁盘是OS调度的,对应用程序是透明的!(3)虚拟存储器给用户程序提供了一个基于字节的内存序列(可以认为是一个大的字节数组),在32位系统中,用户可以得到假象的4G(内核要使用1G或2G等内存地址)字节的内存序列.

其次,不同的OS提供的内存访问方式是有区别的.进程映像也是有区别的!(虽然虚拟存储器的概念是一致的)!

最后,不是所有计算机系统都有虚拟存储器的概念,比如嵌入式领域大多数是没有虚拟存储器的,程序使用的都是实际物理地址!

既然我们可以得到很大的内存字节序列,而我们程序使用的数据类型是不同种类的(比如记录类型),还有,OS提供的获得内存的API(系统调用)是有一定的约束的,比如有最小内存块,还有内存对齐等等,另外申请内存和释放内存都涉及到内核调用,频繁的使用会影响性能!直接使用OS提供的API会有什么问题呢?首先如果我们需要比最小内存块还小的数据怎么办?

频繁的内核调用牺牲性能怎么办? 基于这样的各种问题,产生了一个新的概念 内存管理器.

C标准库的Malloc/Free函数就是内存管理器.Win32下OS本身也提供了内存管理器.

在Win32下实现动态内存分配有三种方法(1)调用VirtualAlloc的虚拟存储器分配! (2)调用HeapAlloc系列函数实现的一种堆内存的内存管理器!(3)内存映射文件.

在Linux下提供了两种系统调用实现动态内存分配(1)调用brk实现进程内堆内存分配(参看前面的进程内存映像图)(2)使用mmap的内存映像.

关于操作系统的内存分配可以参考各自的资料(Linux方面的专著较少,但网上资料丰富,Win32方面微软出版的windows的书籍不少).

需要知道的是,c库中的malloc/free函数是通过操作系统的API(系统调用)实现的内存管理器.也就是说,你可以使用第三方的内存管理器,或者自己实现malloc/free.在K&R的著作里有简单的malloc/free实现!怎样使用虚拟内存提供的字节序列实现复杂数据格式属于一种技术,当然还要考虑内存分配回收的效率,使用的简单等许多因素!在c/c++等编译语言中,由内存引发的编程问题非常之多(另外一个就是并发了,以及并发和内存访问的”综合症”).如果你具备了各种基本的知识(包括前面的变量的概念,进程内存映像等等),遇到的问题就会少很多,或者可以很快找到问题之所在!但不是所有程序员对底层的实现都有很好的理解,所以现代许多语言比如Java就不允许程序员管理内存,设置不允许程序员对变量进行内存引用!

手动管理内存有其优缺点!尤其在复杂的程序结构中比如有异常流的执行中,还有比如Windows的消息机制下,手动分配和释放内存有很大的复杂度.

有没有更好的自动内存管理的方式呢?

介绍两种方法:

(1) 使用引用技术实现生存期自管理类型.

Borland Delphi的内存管理器提供了类似c语言的malloc/free/realloc 函数(分别是getmem/freemem/reallocmem),也提供了类似c++的 new/delete(分别是new/dispose).在这个内存管理器上面使用引用计数实现了ansistring(字符串),interface(接口),array(动态数组),variant(变体类型)几种类型的身存期自管理.

比如我前面提到的ansistring在这种类型数据的负偏移保留了内存变量对他的引用计数,如果引用计数为0则调用内存管理器函数释放内存,为了完成这种机制,编译器嵌入了不少用于实现这种机制的隐含代码(一般程序员是看不到的).如果是基于应用的快速开发,大可放心的使用这种机制减少手动内存分配.但如果涉及比较底层的开发,就需要了解这种机制,否则会付出很大的代价.

(Borland提供delphi实现的绝大多少源代码,大家可以自己看)

(2)垃圾回收机制

Java采用的是垃圾回收的完全自动的内存管理模式(垃圾回收有很多中算法,有兴趣可以阅读这方面的文章).需要知道的是,使用垃圾回收,程序员无法知道何时释放内存(更无法干涉内存分配释放的行为),还有就是垃圾回收相对来说管理内存的性能比较差!

采用什么样的内存管理方式,取决于程序的应用!c语言中完全可以各种方式的库实现内存管理,当然你也可以自己编写内存管理器用于自己的数据管理(不同操作系统上方法不同).

动态内存分配(包括各种内存管理器)会遇到什么问题呢?(c语言关于内存方面常见的编程问题可以在百度google中查找,多看几遍会有好处).其实也就两方面的问题,空间和时间

空间问题有一个比较典型的问题就是,如果一个程序中使用多个内存管理器实例,它们会有冲突,解决办法就是使用同一个内存管理器!时间的问题就是并发,动态内存管理天生的并发问题(比如多线程).

前一篇:重新学习 c 语言(4)- 库和宿主实现(三) 程序级异常

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,640评论 6 507
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,254评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,011评论 0 355
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,755评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,774评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,610评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,352评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,257评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,717评论 1 315
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,894评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,021评论 1 350
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,735评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,354评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,936评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,054评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,224评论 3 371
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,974评论 2 355

推荐阅读更多精彩内容