C++内存管理

目录html

C++内存管理

#include <bits/stdc++.h>

using namespace std;

int main()
{
    /**
     * c++中内存得到/释放的几种方式
     * malloc()                     free()
     * new                          delete
     * ::operator new()             ::operator delete()
     * allocator<T>::allocate()     allocator<T>::deallocate()
        固然你也能够调用操做系统API得到内存,可是这样程序就不具有可移植性
     * */

    //  初步使用

    //  [1] malloc free
    void *p1 = malloc(512);     //  512byte
    free(p1);

    // [2]
    complex<int>* p2 = new complex<int>;
    delete p2;

    // [3] 这是一种特殊的指针,底层也是调用free和malloc
    void* p3 = ::operator new(512); //512 byte
    ::operator delete(p3);

    // [4] 分配器 STL标准库的内容,在各个环境下可能不一样, 我这里是clang
    int *p4 = allocator<int>().allocate(5);  //VC下有第二个参数(int*)0
    allocator<int>().deallocate(p4, 5);

    //gnu下是 alloc::allocate(512) (这里是字节了) 上面是几个int 和 alloc::deallocator
    //alloc 这个东西是老的版本,在新版还了个名字
    //有点麻烦,还要告知还多少内存。

    //===========================================

    return 0;
}

证实new底层是malloc,大概是这个画风,在VC6的标准库里面畅游。。。c++

void * operator new( unsigned int cb )
{
    void *res = _nh_malloc( cb, 1 );

    return res;
}
void * __cdecl _nh_malloc_base (size_t size, int nhFlag)
{
        void * pvReturn;

        //  validate size
        if (size > _HEAP_MAXREQ)
            return NULL;

#ifndef WINHEAP
        /* round requested size */
        size = _ROUND2(size, _GRANULARITY);
#endif  /* WINHEAP */

        for (;;) {

            //  allocate memory block
            if (size <= _HEAP_MAXREQ)
                pvReturn = _heap_alloc_base(size);
            else
                pvReturn = NULL;

            //  if successful allocation, return pointer to memory
            //  if new handling turned off altogether, return NULL

            if (pvReturn || nhFlag == 0)
                return pvReturn;

            //  call installed new handler
            if (!_callnewh(size))
                return NULL;

            //  new handler was successful -- try to allocate again
        }
}

上面介绍了几种c++得到内存的方式的使用方式api

当咱们须要内存的时候,能够用mmap等系统调用直接向操做系统索取内存。可是这样就不具有可移植性。cookie

因而就出现了malloc函数,由这个函数去实现底层内存的索取,咱们只管要便可。函数

在c++面向对象出来后,咱们若是须要用malloc出对象,须要手动调用构造函数。是比较麻烦的,可是malloc是函数,不在编译器的控制范围内,因而就有了new。new的做用,调用malloc,把malloc的指针作类型转换,而后调用构造函数,返回this

在c++标准库中,咱们使用vector等容器历来不关心内存,是由于内部帮咱们实现好了,这就是分配器。spa

例如:操作系统

vector的头部指针

template<typename _Tp, typename _Alloc = std::allocator<_Tp> >
    class vector : protected _Vector_base<_Tp, _Alloc>

默认使用std::allocator的构造器调试

template<typename _Tp>
    class allocator: public __allocator_base<_Tp>

而后这个构造器又继承另外一个构造器

using __allocator_base = __gnu_cxx::new_allocator<_Tp>;

去找这个构造器,发现它是一个别名。最后咱们找到了new_allocator这个类,下面是这两个类的两个函数。

// NB: __n is permitted to be 0.  The C++ standard says nothing
      // about what the return value is when __n == 0.
      pointer
      allocate(size_type __n, const void* = static_cast<const void*>(0))
      {
    if (__n > this->max_size())
      std::__throw_bad_alloc();

#if __cpp_aligned_new
    if (alignof(_Tp) > __STDCPP_DEFAULT_NEW_ALIGNMENT__)
      {
        std::align_val_t __al = std::align_val_t(alignof(_Tp));
        return static_cast<_Tp*>(::operator new(__n * sizeof(_Tp), __al));
      }
#endif
    return static_cast<_Tp*>(::operator new(__n * sizeof(_Tp)));
      }
// __p is not permitted to be a null pointer.
      void
      deallocate(pointer __p, size_type)
      {
#if __cpp_aligned_new
    if (alignof(_Tp) > __STDCPP_DEFAULT_NEW_ALIGNMENT__)
      {
        ::operator delete(__p, std::align_val_t(alignof(_Tp)));
        return;
      }
#endif
    ::operator delete(__p);
      }

它是使用::operator new和::operator delete来申请和释放内存的。

咱们去寻找这两个东西

/usr/include/c++/7.3.0

https://en.cppreference.com/w/cpp/header/new

。。。没有找到我想要的侯捷老师演示的那份原代码。可是operator new的底层会用malloc尝试获取内存,如故没有得到会容许有一个处理http://www.cplusplus.com/reference/new/

这是标准库的调用内存过程,万物回归到malloc。

调试过程当中的一些记录

当你malloc一块空间的时候,

int a = 1;
    int b = 1;
    printf("%x %x\n", &a, &b);  //41db2e4c 41db2e48 相差四字节
    int *c = new int[1];
    int *d = new int[1];
    printf("%x %x\n", c, d);    //9d269280 9d2692a0
    void *e = malloc(sizeof(int));
    void *f = malloc(sizeof(int));
    printf("%x %x\n", e, f);    //aecbd2c0 aecbd2e0

发现这中间的内存间隔是碎片???

Demo*p = new Demo[3];
    delete[] p;     //delete p    

    int *q = new int[3];
    delete q;   // or delete[] q

若是没有指针的释放操做,new[],搭配delete[],没写【】也不会出错,可是这是很差的写法。

//placement new 【  new() 】
    char *buf = new char[sizeof(Complex) * 3];
    Complex *pc = new(buf)Complex(1, 2);
    //他没有分配内存,在原来的内存改
    /**
     //上面等价下面部分
     void *mem = operator new(sizeof(Complex), buf);
     pc = static_cast<Complex*>(mem);
     pc->COmplex::Complex(1, 2);  //固然这样调用构造函数不必定总可行
     * */
    delete [] buf;

另外一个问题:

咱们malloc出的东西,free操做系统怎么知道多大呢?

其实,咱们要一块空间,这块空间的头尾都会带一些东西。咱们能够称之为cookie记录着这块空间的信息。而后打包给咱们。

那么问题来了,咱们每次都要一小块空间,空间大小都同样,那么咱们为何要那么多的cookie呢。

因而就引出了内存池的概念。

咱们一次性分配出一大块空间,重载new和delete,维护一个链表来存储空间,new从链表中取,delete在吧空间抓回链表。这就避免了大量的cookie。gnu下就有相似的代码。找了半天才找到。。。

#include <bits/stdc++.h>
#include <c++/ext/pool_allocator.h>

using namespace std;

int main() {

    //https://gcc.gnu.org/onlinedocs/gcc-4.6.3/libstdc++/api/a00015.html
    //http://gcc.gnu.org/onlinedocs/
    vector<int, __gnu_cxx::__pool_alloc<int> >vec;

    return 0;
}
相关文章
相关标签/搜索