学习笔记之内存处理

 

堆内存介绍:

一段由程序员手动管理的内存段,特点:空间大,需要手动申请、释放,没有固定的使用顺序哪块合适使用哪块。

堆内存管理:

C语言中没有管理堆内存的语句,C标准库中提供一套管理堆内存的函数,这些函数底层封装了各种系统管理堆内存的接口,所以可以跨平台使用。

#include <stdlib.h>

void *malloc(size_t size);

功能:向malloc申请位于堆内存段的size字节的内存块

size:要申请的内存块的大小,如果使用malloc申请数组形式的内存块,size=sizeof(数组元素类型)*数组长度

返回值:成功返回内存块首地址,失败返回NULL(现在有堆内存无法满足size个字节的要求)

如果size等于0,返回NULL或唯一个地址,并且该地址可以通过free释放。

注意:使用malloc申请到的内存块,里面的内容是不确定的,malloc不会帮我们初始化。

void bzero(void *s, size_t n);

功能:把s内存块的n个字节清理为0

void *memset(void *s, int c, size_t n);

功能:把s内存块的n个字节设置为c,c的范围是:0~255,以字节为单位。

void free(void *ptr);

功能:释放堆内存(释放的是使用权,只破坏内存块的一部分内存,大部分数据还在)。

ptr:要释放的内存块的首地址,它必须是malloc、calloc、realloc函数的返回值,如果ptr==NULL则free不会执行任何操作。

注意:如果一个内存被重复释放,会出现"double free or corruption (fasttop)",程序会异常停止。

malloc的堆内存管理机制:

1、当程序首次向malloc申请内存时,此时malloc手里没有堆内存可分配,malloc会向操作系统申请内存,操作系统会一次性分配33页内存交给malloc管理(一页内存=4096个字节),之后再向malloc申请内存时,malloc会从这33页内存中分配给用户。

2、当我们访问堆内存时,只要不超过33页范围,操作系统就不会判断为段错误。

3、使用malloc分配的每个内存块前面(4~12字节的空隙)

空隙前0~8个的空闲字节,用于内存对齐,这块内存可以使用。

空隙后4个字节,记录着malloc的管理信息(这也是为什么free只需要提供内存块的首地址),如果管理信息被破坏会影响malloc、free、calloc、realloc、printf、scanf函数的后续使用。

为什么访问以135160为下标的字节时出现段错误?

1、操作系统分配33页内存,也就是135168个字节给malloc。

2、malloc会拿出8个字节作为内存块前面的空隙,因此malloc返回的是33页内存的第9个字节的地址。

3、从malloc返回的地址开始还剩135160个字节,下标范围是:0~135159,所以当访问135160为下标的内存时就会被操作系统判定为段错误。

使用堆内存越界时会产生什么后果:

int* p = malloc(4);

1、如果越界使用的是空隙中的空闲字节,一切正常。

p[0] = 123; // 合法的正常访问

p[1] = 123; // 空隙中的空闲字节

p[2] = 123; // 空隙中的空闲字节

2、如果越界使用的是空隙中的malloc管理信息,将会影响malloc、free、calloc、realloc、printf、scanf函数的后续使用。

p[3] = 0; // 空隙中的malloc管理信息

malloc.c:2401: sysmalloc: Assertion `(old_top == initial_top (av) && old_size == 0) || ((unsigned long) (old_size) >= MINSIZE && prev_inuse (old_top) && ((unsigned long) old_end & (pagesize - 1)) == 0)' failed.

*** Error in `./xxxx': free(): invalid pointer: 0x08edxxxx ***

3、如果使用的是malloc还没有分配出去的内存,此时不会出现段错误、也不会影响malloc、free的后续使用,但malloc这块内存分配出去后,越界存储的数据就会被覆盖的风险,这种情况叫脏数据。

p[4] = 6666;

int* p1 = malloc(4);

p1[0] = 1234;

printf("%d\n",p[4]); // 输出的结果是1234

4、如果超出33页范围,会被操作系统判定为段错误。

p[33790] = 1234; // 超出了33页范围,会产生段错误

内存碎片:

什么是内存碎片:已经释放了使用权,但无法被malloc再次分配出的内存块叫内存碎片。

int* p1 = malloc(4);

int* p2 = malloc(4);

int* p3 = malloc(4);

free(p2);

int* p4 = malloc(16); // 此时p2内存块就是内存碎片

内存碎片产生的原因:释放和分配的时间、大小不协调导致的。

内存碎片无法杜绝只能尽量减少,减少内存碎片的方法:

1、尽量使用栈内(Linux系统查看栈内存的使用上限:ulimit -s,设置栈内存的使用上限:ulimit -s <size>,当前系统栈内存的使用上限:8192kb->8mb)。

2、尽量分配大块内存,让用户自己管理。

int* p1 = malloc(16);

int* p2 = p1+1;

int* p3 = p2+1;

int* p4 = p3+1;

3、按照分配的顺序,逆序释放,也就是把堆内存当栈管理。

int* p1 = mallor(4);

int* p2 = mallor(4);

int* p3 = mallor(4);

free(p3);

free(p2);

free(p1);

4、内存碎片整理

int* p1 = mallor(4);

int* p2 = mallor(4);

int* p3 = mallor(4);

memcpy(p2,p3,4);

swap(p2,p3);

free(p2);

内存泄漏:

什么是内存泄漏:已经不再使用,但无法释放的内存叫内存泄漏。

int* p = malloc(4); // 此次分配的内存无法正常释放

*p = 1234;

printf("%d\n",*p);

p = malloc(8);

...

free(p);

产生内存泄漏的原因:

1、只写的内存分配语句,而没有内存写释放语句,可以粗心大意,也可以是以为其它人会写。

2、写了内存释放语句,但由于执行流程、条件设计有问题,导致释放语句无法执行。

3、与堆内存配合的指针变量被破坏,导致free执行无效。

如何减少内存泄漏:

1、按照规则分配、释放内存

自用:谁申请谁释放,分配和释放语句成对出现。

共用:谁知道该释放谁释放,项目组中负责分配和负责释放人进行对接。

2、封装malloc、free函数,记录每一块分配、释放的内存块。

3、使用cosnt保存与堆内存配合的指针变量不被破坏。

int* const p = malloc(4);

p = malloc(8); // 出错

free(p);

如何检测是否发生内存泄漏:

1、查看系统的内存使用情况,是否出现暴涨的情况,可通过ps命令查看或maps文件查看。

2、随着程序的长时间运行,可用的内存越来越少,则大概率出现内存泄漏。

3、使用gdb调试工具,查看内存的使用情况。

4、使用valgrind检查可执行程序

sudo apt install valgrind

valgrind --tool=memcheck --leak-check=yes <可执行程序>

5、分析malloc、free的执行日志。

内存泄漏、内存碎片的危害:

前提:当程序结束时,操作系统分配它的所胡资源都会被回收,当应用端程序出内存泄漏、内存碎片、程序卡顿时,重启程序即可。

服务端的程序一般需要7*24小时长度运行,即使程序只有很少的内存泄漏和内存碎片,长年累月下来也会导致可用的内存越来越来少,最后系统死机。

堆内存与栈内存的优缺点:

堆内存的优点:

1、空间大,理论上能使用将近3G的堆内存。

2、分配与释放时间可控,可长时间存储数据,也可以根据数据的使用情况随时释放内存。

3、根据实际情况动态调整内存块的大小,节约内存。

堆内存的缺点:

1、使用麻烦,需要手动调用标准库函数或系统接口进行分配、释放堆内存。

2、有可能产生内存碎片或内存泄漏。

3、内存块之间有空隙,内存利用率低。

栈内存的缺点:

1、空间小,数组定义过大,函数递归次数过多就会产生段错误。

2、分配和释放时间不受控制,它是根据函数的调用和结束自动分配、释放的,可能会出现,想长期用的会自动释放,已经用完的无法即时释放的情况。

3、使用栈内存的数组大小无法调用,为了保证数据能存储的下,基本上每次多定义,导致内存浪费。

栈内存的优点:

1、使用方便,由系统自动分配、释放。

2、按顺序分配、释放,不会产生内存碎片、内存泄漏。

3、变量、数组之间没有空隙,内存的使用率高。

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值