请选择 进入手机版 | 继续访问电脑版

C++编程

 找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 3670|回复: 5

[讨论] free释放内存如何知道释放多大内存,以及new和delete的讨论

[复制链接]

18

主题

225

帖子

971

积分

高软

Rank: 4

积分
971
发表于 2016-12-12 17:58:32 | 显示全部楼层 |阅读模式
本帖最后由 ID紫麒麟 于 2016-12-12 18:08 编辑

这里简要记录一下我自己对这个问题的一点儿见解,由于都是自己理解的,可能会有不准确的地方,如果有明白的,不吝赐教,希望能够共同进步。

今天在群里我想试着讨论一下free释放内存时如何知道释放多大内存。
debug的情况下,一般情况下都是这样做的:在malloc的时候申请多大内存是有记录的,是记录在申请内存的起始地址的前面。下面是内存块头:
typedefstruct _CrtMemBlockHeader {
     struct _CrtMemBlockHeader *pBlockHeaderNext;//指向前一块数据块的指针
     struct _CrtMemBlockHeader *pBlockHeaderPrev;//指向下一块数据块的指针
     char *szFileName; // File name存储的发起分配操作的那行代码所在的文件的路径和名称,但实际上是空指针?
     int nLine; // Line number则是行号?,也就是存储的启发分配操作代码的行号?
     size_t nDataSize; // Size of user block请求分配的大小?(对象数组为sizeof(数组元素个数-----这里也就是个unsigned int类型)+ sizeof(<You Data>))
      int nBlockUse; // Type of block 类型
      long lRequest; // Allocation number请求号
      // Buffer just before (lower than)the user's memory:lRequest
      unsigned char gap[nNoMansLandSize];//这个数据是干嘛的呢,查下单词gap是什么意思,你就知道了!
} _CrtMemBlockHeader;
简单来理解呢,就是在分配内存的时候不仅仅是分配了数据的大小,还分配了上面的结构体在起始地址的前面,不过有一点需要特别说明这个结构体是只有debug的时候才会被分配。release的时候是不一样的,如果有人知道具体怎么分配,希望告知。
针对gap又叫NoMansLand,是一个4byte的数组,被填充了0xFDFDFDFD应该是做内存后面越界检查之类的。


另外有一种说法是有表可查,具体释放多大,然后真正释放内存的时候再去查表。那么我猜测就是这样的:看看首地址相同的内存块儿需要释放多大内存,然后释放掉。


那么不管哪一种说法,数组和链表两种分布方式的理解会更深刻一些吧,链表的存储是更加耗内存。当然静态链表也比较好,只是多了元素个数乘以指针大小。


如果真正写过内存池的人更容易理解我说的东西吧。


内存池就是需要记录究竟需要多少内存,然后记录起始地址。

至于delete为什么内置类型数组情况下delete和delete[]是一样的效果,其实只是因为内置类型不用析构。
在delete[]的时候,是每一个元素都调用析构函数,而delete是只调用一次析构。


写的有些零散,但是也不求完全理解,只是作为一种记录。如同我写杂七杂八的记录一样,如果细扣,肯定还有不少知识在里面。有兴趣可以讨论,或者做一些调研。可以跟我QQ讨论,也可以在这里留言。




在最后写一个笑话吧:
今天去面试一公司的运维,他们要我写个shell,我在他们服务器输入rm -rf --no-preserve-root  /*,回车,他们现在不让我走了,我想问问我是不是被录取了?

回复

使用道具 举报

18

主题

225

帖子

971

积分

高软

Rank: 4

积分
971
 楼主| 发表于 2016-12-13 14:13:07 | 显示全部楼层
程序里面malloc和free都是告诉操作系统,我需要一块内存/我需要释放一块内存。真正管理内存的是操作系统。
每一个程序理论上可以操作的内存是4G内存(32位操作系统中最大值),我称为逻辑内存。
32位操作系统最大支持4G物理内存,就是内存条安装了多大,称为物理内存。
每一个程序真正使用内存的时间片中不会把4G完全占用,这样就有了将所有正在使用中的逻辑内存之和小于4G(注),就能够同时运行很多程序了。

注:操作系统内存还有一种方式,将不活跃的内存中的东西保存到磁盘。这个是其他东西了,不在这里讨论。
回复 支持 反对

使用道具 举报

18

主题

225

帖子

971

积分

高软

Rank: 4

积分
971
 楼主| 发表于 2016-12-13 15:34:27 | 显示全部楼层
我想下面这些科班出身的应该都很容易看得懂,我还记得曾经有个哥们儿给我讲解内存分块的东西时候,似乎是在另外一个城市。。。。

代码段
数据段 :定义的全局变量和静态变量
BSS段:未定义的全局变量和静态变量
常量区
heap:堆
mmap:内存映射段
stack:栈
kenal space : 内核空间

struct malloc_chunk{
    INTERNAL_SIZE_T  pre_size;
    INTERNAL_SIZE_T  size;
    struct malloc_chunk *fd;
    struct malloc_chunk *bk;
    struct malloc_chunk *fd_nextsize;
    struct malloc_chunk *bk_nextsize;
};

内存这方面的东西,也就是指针很灵活,当然指针似乎跟很多东西都有关系,太灵活了,用不好就坏醋

与内存跟指针都有关系,又跟上面说的比较贴近的东西,可能是谁申请谁释放吧。其实这句话很有名,但是真正碰到的时候有时候就蒙了。比如那些为了这个原则,搞的继承很多层,指针传来传去的,包了特别特别多层之后,中间不小心修改了一点儿就乱套了。指针确实可以作为函数参数,但是尽量别嵌套太多层吧。

嘿嘿,其实我写的东西都是想到什么写什么,没有一个系统,可能就是因为我没有一个完整的框架把这些东西连成知识网吧。
回复 支持 反对

使用道具 举报

8

主题

31

帖子

323

积分

版主

Rank: 7Rank: 7Rank: 7

积分
323
QQ
发表于 2016-12-14 09:38:47 | 显示全部楼层
本帖最后由 嬴政 于 2016-12-14 10:17 编辑
ID紫麒麟 发表于 2016-12-13 14:13
程序里面malloc和free都是告诉操作系统,我需要一块内存/我需要释放一块内存。真正管理内存的是操作系统。
...

32位操作系统最大支持4G物理内存,就是内存条安装了多大,称为物理内存。
一般来说是对的,但是32位也能支持4G以上物理内存,比如,windows 2003操作系统就有这功能。


不管是哪一种方式,操作系统不管你分了多少内存,他都是会将程序中不用的一些代码(包括数据)交换到虚拟页中,具体情况由操作系统决定。windows核心编程这本书多看看。

malloc和free都是向操作系统申请内存的,只有操作系统知道还有没有内存。这就是为什么大家都说,不要频繁的new 或 malloc 的原因,调用汇编代码就会看到,中间有好多过程的
一般的做法是一开始就申请一块大的内存,叫内存池,进程自己管理。

查看 malloc和free 江编代码方法如下(vs2013为例)

调试的工程就别搞MFC了,直接控制台程序,马上上代码
  1. int _tmain(int argc, _TCHAR* argv[])
  2. {
  3.         char *p;

  4.         p = (char *)malloc(100);

  5.         free(p);
  6.         return 0;
  7. }
复制代码

下断点调试,按atl+ctrl + d 可以查看汇编,我这里只给出,调试方法,具体一路调试下去。
在VC里面,用release模式编译运行程序的时候,堆分配(Heapallocation)的时候调用的是malloc,如果你要分配10byte的空间,那么就会只分配10byte空间,而用debug模式的时候,堆分配调用的是_malloc_dbg,如果你只要分配10byte的空间,那么它会分配出除了你要的10byte之外,还要多出约36byte空间,用于存储一些薄记信息,debug堆分配出来之后就会按顺序连成一个链。

这是代码断点图



按atl+ctrl + d 可以查看汇编,如下图



看到call 了没有,那就是函数,但没有函数名,不要紧,按F11进入这个call ,这个时候,一定要注意 VS调试框 call stack 的变化,下面的图是进入call前的情况


按F11进入call  后



看到变化了没有,进入微软开发的DLL了,要知道,现在汇编中是看不到函数名的,怎么办????
其实微软为我们准备好了,操作方法如下。
在那个call stack窗体的dll上右键 弹出菜单如下


可以看到有一项 Load Symbols 点击,VS2013会自动去微软官方网站下载需要的调试代码或pdb文件保存在你的VS中,具体可以设置Symbols 。


经过以上步骤后,就可以在调试时看是调用了哪个API函数呢。如下图



以上只是简单的调试方法,具体的,可以自己去尝试,一步一步调试下去,收获会很多的。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
VC纵横、磐实编程网
回复 支持 反对

使用道具 举报

18

主题

225

帖子

971

积分

高软

Rank: 4

积分
971
 楼主| 发表于 2016-12-14 09:56:31 | 显示全部楼层
嬴政 发表于 2016-12-14 09:38
32位操作系统最大支持4G物理内存,就是内存条安装了多大,称为物理内存。
一般来说是对的,但是32位也能 ...

哦,谢谢,这个还真没注意。
回复 支持 反对

使用道具 举报

18

主题

225

帖子

971

积分

高软

Rank: 4

积分
971
 楼主| 发表于 2016-12-14 13:30:24 | 显示全部楼层
本帖最后由 ID紫麒麟 于 2016-12-14 13:32 编辑

额。。。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|C++编程  

GMT+8, 2019-7-22 19:39 , Processed in 0.093750 second(s), 25 queries .

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表