注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

一路

To find the final symmetry & beauty

 
 
 

日志

 
 
 
 

SGI STL源码阅读笔记9,常规内存分配与内存处理[原创]  

2011-02-18 01:40:46|  分类: c++学习笔记 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

这里先简单介绍一下c/c++常规的内存分配方式和相应的策略,为下一篇介绍allocator做准备。

一、堆和栈与静态存储区

常规程序在运行时的内存需求可能是多样的,比如一般程序变量的分配都分配在栈区,先不说栈的后进先出特性,先来看一下英文的栈stack是什么意思,这意为一堆东西整齐的堆在一起,如下图。

需与此同时中文的栈是什么意思呢,看下面的栈道。

这两个的共性就是它们都要求整齐规范,如果稍有差错就可能出现灾难的后果。再看英文的heap和中文的堆。这两个就有明显的对应关系,都表示东西堆在一起,并没有提出规整的要求,如下图

这两个计算机词汇翻译的相当精确,是我认为翻的最好的两个词。那么系统为什么为分为堆和栈呢,其实这个在最古老的程序设计中是没有堆这个东西的,可以认为一次只能运行一个程序或是分别运行。就算是分时的程序也规定一哪个程序只能占用哪一块内存,什么变量放在什么 地方,这在程序运行前就规定好了的,是付费的,不能多用也不能越界。但是后来内大变大的,多道程序与发时程序变得十分流行,这就对内存的有效管理提高了要求(当然也对CPU的管理提高了要求,这时的虚拟内存空间地址、进程调度、进程同步并发等经典问题也都是在这个时候提出的,这些与本文关系不大,故略去)。因此就有了把内存分为多个层次,可以让程序在运行期动态的申请和释放内存,这极大的提高了程序的灵活性。这多个层次中比较重要的就是堆、栈和静态存储区。其中栈是用来管理程序主体的结构的,直接声明的变量与函数传值等都在栈中申请空间,这种空间申请空间动作是在编译期就可以确定的,空间也是在编译器预留好的,这种可以预留的方式当然其整齐性有极高的要求,如果栈被破坏可能的结果是灾难性的。既然栈如些规范也确定了它的灵活性不高,不能在运行时根据程序的需要运态申请内存,但可确可以自己释放内存,这种特性也来自于它良好的结构。由于栈要运行在连续的内存空间中,这对系统的要求还很高的,这也就规定了栈不能开的很大,一般系统为程序设定的调用栈大小也就是在Mb级别。再来说说堆的管理,堆内存不是由编译器直接维护的,它是由操作系统的内核的内存管理服务直接管理的,这种管理一般会使用复杂的数据结构,向它申请内存要经过一定的系统调用,当然这是有开销的。这种堆式管理为程带来了极大的灵活性,但是如果程序在运行中会不不断申请内存而不释放,准确的说是把指向这部分内存的指针丢掉而造成无法释放它们,这就会造成内存泄露。一般如果有堆内存有在程序中释放,在程序退出后系统会把它们释放掉,必竟现代的操作系统已不是原始时代的产品,但也不能保证一些嵌入式系统的这项功能总是有效,内存泄露是十分危险的,因为它可能会把系统的内存消耗光而其实没有程序在真正占用如此大的内存,这对影响系统的稳定性。尤其是服务器更是如此,因为长时间无故障运行是服务器的基本要求。程序的静态存储区用来存放一些程序中用到的静态变量,因为这些静态变量可能随时在程序中访问且大小不 会发生变化,因此它们一般是在程序载入时就放在内存中的,在程序退出时由系统释放,这些静态变量的管理相对简单。

二、C/C++语言的常见内存管理模式

    C语言的内存管理主要有三个函数,malloc realloc和free,关于这三个函数的用法请参见这个网址http://www.cplusplus.com/reference/clibrary/cstdlib/malloc/ 这想其用法大家已经很熟悉了,C语言的处理方式有一个特别的就是这个remalloc,这个函数的思想是这样的,如果我们调用malloc申请内存失败,这并不说明系统没有内存了,只是说明它没有我们需要的那么多了,但是我们手上又恰好有一块内存,它只比我们需要的小了,我们可以把它释放之后再重新申请,那这个重新申请就可能成功,因不两块小的内存组在一起就可能达到我们的需要了,这个简单的思想在SGI STL中也有体现。这些也是C语言内存管理的一般模式,如果内存申请不成功就先放弃掉我们手头不用的内存,如果再申请还是不成功就要调用一个事先设定好的函数,一般是提醒用户内存不足让其选择关掉一些程序或是直接中止此程序的运行。

    C++的内存管理更加直接operator new和operator delete是两大基本内存处理函数,operator new[]和operator delete[]是其数组版本。这想这些大家已经很熟悉了,不再多做介绍。

三、关于内存池

    C与C++的内存管理方式是无论是delete还是free,它们的参数都是一个指针,但是他们怎么知道到底要释放多少存呢,这就说明系统一定是放什么东西在我们看不见的地方。是的,大多数系统为我们分配内存的时候实际分配的要比我们想要的多一点,这一部分大小对分配任意大小的内存都是一样的。如下图:

SGI STL源码阅读笔记9,常规内存分配与内存处理 - saturnman - 一路

 

 

每一个申请的内存的前面都要有一个小块,一般记录小块系信息的空间都叫cookie,这种对于space很小的情况下相当于使用率很低,尤其是对于小内存的系统来说更是如些,再加上系统调用中内核保护模式的切换这个开销将更是巨大。对于类似这种情况就有一种就做内存池的技术,它先向系统申请一块大的内存放入池中,程序申请内存时直接向池内申请而释放内存时也是把内存直接放入池中,如果池不够用了也可以再向系统申请大块内存加入池中。但是这种技术有些被过分地夸大,成事都是有两面性。内存池在程序不用内存时也占有内存,这相当于降低系统的可用内存,使用内存池的程序的性能是提高了,但是这是以降低系统内其它程序的性能为代价的,当池比较大时尤其如此。典型的程序如chrome,它使用内存池和线程池等多项技术,但是打开几个页面后可看出其内存占用相当可观,在不做任何操作时cpu也有较高的平均占用,浏览网页的高速来的并不是没有代价,Google的广告中你当然是看不到这一代价的。

Ref:

[1] <<The C programming language>> By Brian W. Kernighan ,Dennis M. Ritchie

[2] http://www.cplusplus.com/reference/clibrary/cstdlib/malloc/

  评论这张
 
阅读(1070)| 评论(0)
推荐 转载

历史上的今天

在LOFTER的更多文章

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017