/***********内存使用************/
(1)查看程序能被分配的最大内存
int main() { int MB = 0; while(malloc(1<<20))++MB; printf("The Max Memory %d MB Can Be Alloc.\n",MB); return 0; } |
这个程序片段可以查看你的电脑能为你的程序最大分配多大的内存空间,1<<20表示将1乘以2的20次方,也就是1MB。
(2)内存申请:数据段中堆的增长
堆段在内存中的位置处于数据段里面的最高地址处,与栈段相接。
堆段用于动态分配的存储,也就是通过malloc函数获得的内存,利用指针进行访问,需要自己对它进行回收。一个malloc请求的数据大小一般都会被优化为圆整为2的次幂。
堆的回收不必像栈那么按照分配顺序回收,这也导致了动态内存像碎片一样四处分布,引起了内存回收的困难。
另外,堆的底部,即堆的最高地址,由一个break指针来标识,它有两个作用:1、保证你对内存的引用是正确的,如果你引用的内存地址超过了break指针指向的地址,那么它就会发出警报;2、通过系统调用brk和sbrk来移动break指针,这样就可以获得更多的内存
/*************内存泄漏*****************/
(1)避免内存泄漏
前面说了,堆增长(也就是动态内存的申请)会产生很对内存碎片,如果我们不对其进行回收,那么系统会识别到这些碎片是一直属于这个程序的,在程序结束之后也不会把这些碎片标记为可用,那么其他的程序就无法使用这些内存,相当于碎片会一直存在,除非你重启电脑,这就是内存泄漏,你的内存再大也可能被它整死机的。
还需要注意的是动态申请的一般会被圆整,像申请215B的内存,其实会被优化为申请256B的内存,多的41B是不会被引用的,除非你对它非法引用,所以,内存泄漏往往泄漏的比你忘记释放的数据要多得多的。
内存对齐:数据项只能存储在地址是数据项大小的整数倍的内存位置上,例如一个4字节的int型数据只能存放到地址是4的倍数的内存中。当然,如果你要检查内存是否对齐基本是不太可能的,因为编译器一般都会通过自动分配和填充数据来进行对齐,例如下面这个结构体定义:
struct student { char name[21]; int number; double id; }; |
以我自己的电脑为例,系统会一次性提取8个字节的内存,那么上面那个结构体对它sizeof,name会占3*8=24字节,多余了3个字节,但是紧接着定义的int型不能刚好放到多余的3个字节里,所以有开辟8个字节放number,这里又多余了四个字节,显然id的8字节是不能放到四个字节里的,所以有开辟8个字节放id,总共sizeof之后就是5*8=40字节了。
如果将name大小改为25,有4*8=32字节容纳它,多了7个字节,能够容纳number,那么就相当于多余了7-4=3字节了,显然id不能放到3字节里,开辟8字节容纳id,这样sizeof之后仍未5*8=40字节
为了更好的节省内存空间,我们需要注意类似的变量定义的先后顺序,将number和name的位置交换结果又不一样,因为指令是逐条执行的。
(2)段错误
(还有一种总线错误,真心还没弄懂,求高手指点)
段错误是由于内存管理单元异常所致,导致段错误有以下几个原因:
1、解引用一个包含非法值的指针,也就是对一个已经free掉的内存继续使用
2、解引用一个空指针,一般就是初始化为NULL了还对它进行解引用,还可能是从函数中返回了NULL却没有进行检查就直接使用
3、对超过了申请内存边界的内存进行解引用,有点像数组的越界一样
4、把你的电脑可用动态内存用完了。。。。这个几乎很困难