内存管理(一)

首先,先谈谈我们为什么要做内存管理,每次使用malloc()它不香吗?
确实不香.
当我们malloc(sizeof(int))的时候,malloc真的只分配了 4 大小的字节吗?

在这里插入图片描述
给出一个在vc6下面malloc()后,分配内存的布局
当我们需要 4 个字节的时候,多出了这么多字段,我们得到的,就是指向int的指针

上下两个cookie 各占 4 字节
Debugger Header + no man land 共占 36个字节

一共为 48 个bytes. 也就是说 多出了 44 个bytes
每次malloc() 时,都会多出 44+ 个字节(因为有时会因为内存对齐为16 的整数倍而多加1-15个字节)

为什么我们不只malloc() 一块内存,然后有我们做管理分配使用,这样只 malloc() 一次,也就不会造成特别多的浪费.这就是做内存管理的原因.

当我们new了一个数组,但是我们delete 的时候却没加 [] ,此时会发生内存泄露???
这个先说明, 这个不一定会发生内存泄露.

#include <iostream>

class A
{
public:
	A(){};
private:
	int i;
};

当我们申请一个数组时,如果这个类中没有涉及到动态分配过的内存,或者说这个类的析构函数没有写的必要(如 A 类 ).
此时当我们执行 A* apoint = new A[10]; 这条语句后,分配的内存空间图如下:
内存管理
这个 cokkie 字段就记录了我们申请了 几个 空间,这里cookie 中就记录了 10 个元素.当我们delete时,即使不加 [],free 会向上访问,访问到cookie,它就会直到free几个元素,所以不会造成内存泄露

但是当我们的类中有动态分配的空间,如 string 类.
string* p = new string[3];

这里如果delete 的时候没有加 [] ,则只会调用 一次 析构函数,另外两个空间不会释放,造成内存泄露.

上面的测试前提是不写析构函数,如果写了析构函数,那么内存块的布局就不同了在这里插入图片描述
如果写了析构函数,那么cookie 下面就会多一个字段,表明申请了几个元素,此时,free()向上访问,访问的就不是cookie,就会报错.

new 的具体行为

编译器执行这一行时, complex *pc = new complex(1,2); 会转化为

complex *pc; 
try
{
	void* mem = operator new(sizeof(complex)); //分配内存
	pc = static_cast<complex*>(mem);  //转化类型
	pc->complex::complex(1,2); //调用构造函数
	//注意这里调用的构造函数有点特殊,只有编译器可以通过类名,直接调用构造函数
}

catch(std::bad_alloc)
{
//抛出异常
}

我们再找到operator new 的源代码可以发现, operator new 里面也是调用了 malloc .

void* __CRTDECL operator new(size_t const size)
{
    for (;;)
    {
        if (void* const block = malloc(size))
        {
            return block;
        }

        if (_callnewh(size) == 0)
        {
            if (size == SIZE_MAX)
            {
                __scrt_throw_std_bad_array_new_length();
            }
            else
            {
                __scrt_throw_std_bad_alloc();
            }
        }

        // The new handler was successful; try to allocate again...
    }
}

当内存分配成功时,就return block,否则就是个死循环.

这里 解释 一下 _callnewh.
很明显,这里是分配内存失败(内存不足)才进入这个函数,进入这个函数释放内存,然后继续尝试分配.

这个c++ 提供了一个函数可以设置 --set_new_handler();

在分配内存错误之前,系统会抛出一个异常,但是在抛出异常之前,会重复调用_callnewh()

以上结论下面皆有测试.

测试如下:

#include <iostream>
#include <new>
#include <cassert>

using namespace std;

void noMoreMemory()
{
    //    cerr << "out of memory" << endl;                                  1
//      abort();                                                                                        2
}


int main()
{
//      set_new_handler(noMoreMemory);                                     3
   
        int *p = new int[100000000000000000];

        assert(p);
 
        return 0;
}

测试一: 注释 3
结果:
在这里插入图片描述
测试结果表明 : 系统抛出异常
测试二: 注释 2
结果:
在这里插入图片描述
测试结果表明: 出现了死循环,系统在不断的调用我们set_new_handler()这个函数

测试三: 都不注释
在这里插入图片描述
测试结果表明: 调用abort();退出循环

接着上面谈:
编译器将这行代码 delete pc;
转化为:

pc->~complex(); //调用析构函数
operator delete(pc);  //释放内存
void operator delete( void * p )  
{  
    RTCCALLBACK(_RTC_Free_hook, (p, 0));  
  
    free( p );  
}  

从源码可以得出-- delete -> operator delete -> free

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值