ARM GCC堆未完全使用

时间:2018-12-16 12:03:51

标签: c++ c gcc arm heap

我正在设置Cortex-M4平台以使用堆内存并遇到一些问题。 我将堆区域大小设置为512字节,并且它仅分配9个字节。然后我将堆设置为10kB,它只能分配362个字节。 这是我的gcc存根:

int _sbrk(int a)
{
    //align a to 4 bytes
    if (a & 3)
    {
        a += (4 - (a & 0x3));
    }

    extern long __heap_start__;
    extern long __heap_end__;
    static char* heap_ptr = (char*)&__heap_start__;

    if (heap_ptr + a < (char*)&__heap_end__)
    {
        int res = (int)heap_ptr;
        heap_ptr += a;
        return res;
    }
    else
    {
        return -1;
    }
}

__heap_start____heap_end__是正确的,并且它们的差异显示正确的区域大小。 我在_sbrk函数中添加了debug,以查看调用此函数时传递了a参数,并且该参数的值分别类似于每次调用中的这些值:

2552
1708
4096

如何使它使用完整的堆内存?以及_sbrk参数如何计算?基本上,这是怎么了?

使用new (std::nothrow)构建C ++代码。

编辑 如果我使用的是malloc(C风格),则它分配524个字节,并且在main之前没有_sbrk调用,这与使用operator new的情况不同。

arm-none-eabi-g++.exe (GNU Tools for ARM Embedded Processors 6-2017-q2-update) 6.3.1 20170620 (release) [ARM/embedded-6-branch revision 249437]
Copyright (C) 2016 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

EDIT2 最少的完整可验证示例
这是我的应用程序代码和_sbrk(带有信息打印):

void foo()
{
    while (true)
    {
        uint8_t * byte = new (std::nothrow) uint8_t;
        if (byte)
        {
            DBB("Byte allocated");
            cnt++;
        }
        else
        {
            DBB_ERROR("Allocated %d bytes", cnt);
        }
    }
}

int _sbrk(int a)
{
    //align a to 4 bytes
    if (a & 3)
    {
        a += (4 - (a & 0x3));
    }

    extern long __heap_start__;
    extern long __heap_end__;
    static char* heap_ptr = (char*)&__heap_start__;

    DBB("%d 0x%08X", a, a);
    DBB("0x%08X", heap_ptr);
    DBB("0x%08X", &__heap_start__);
    DBB("0x%08X", &__heap_end__);

    if (heap_ptr + a < (char*)&__heap_end__)
    {
        int res = (int)heap_ptr;
        heap_ptr += a;
        DBB("OK 0x%08X 0x%08X", res, heap_ptr);
        return res;
    }
    else
    {
        DBB("ERROR");
        return -1;
    }
}

产生的输出是:

enter image description here

1 个答案:

答案 0 :(得分:4)

您的输出显示C ++内存分配系统首先要求32个字节,然后要求132个字节。这样便可以在该空间内满足九个对new uint8_t的请求。大概它使用164个字节中的一些进行内部记录。这可能涉及保持分配了块的链接列表或映射,或其他某种数据结构。同样,为了提高效率,它可能不跟踪单字节分配,而是为每个分配提供一些最小的块大小,可能是8或16字节。当空间用完时,它会再请求4096字节。您的sbrk然后失败,因为该按钮不可用。

C ++内存分配系统正在按设计工作。为了进行操作,它需要的空间比为单个请求分配的空间要大。为了为请求提供更多的内存,您必须在堆中提供更多的内存。从sbrk提供给内存分配系统的内存与从内存分配系统提供给客户端的内存之间不能一一对应或任何简单的对应。

无法告诉C ++内存分配系统使用“全堆内存”来满足对它的请求。需要跟踪动态分配和内存释放。由于其客户端可能会提出各种大小的请求,并且可能以任何顺序释放它们,因此它需要能够跟踪当前分配了哪些块,哪些没有分配,简单的堆栈就无法满足需求。因此,它必须使用其他数据结构来跟踪内存,并且这些数据结构将占用空间。因此,并不是所有的堆空间都可以分配给客户端。其中一些必须用于开销。

如果在C ++实现中对内存分配系统的内存使用对于您的目的而言效率太低,则可以用自己编写的程序或第三方软件来代替它。内存分配系统的任何实现方式都会在速度和块大小方面做出各种折衷,并且可以针对特定的情况和目标进行量身定制。