>  기사  >  데이터 베이스  >  MySQL MEM_ROOT 자세히 설명

MySQL MEM_ROOT 자세히 설명

高洛峰
高洛峰원래의
2016-11-05 17:07:571119검색

이 기사에서는 MySQL에서 널리 사용되는 MEM_ROOT 구조에 대해 자세히 설명하며, 디버그 정보도 생략하고 일반적인 상황에서 MySQL에서 메모리 할당에 MEM_ROOT가 사용되는 부분만 분석합니다.

구체적인 분석에 앞서 먼저 이 구조의 사용에 사용된 일부 매크로를 열거해 보겠습니다.

#define MALLOC_OVERHEAD 8 //分配过程中,需要保留一部分额外的空间
#define ALLOC_MAX_BLOCK_TO_DROP 4096 //后续会继续分析该宏的用途
#define ALLOC_MAX_BLOCK_USAGE_BEFORE_DROP 10 //后续会继续分析该宏的用途

#define ALIGN_SIZE(A) MY_ALIGN((A),sizeof(double))
#define MY_ALIGN(A,L) (((A) + (L) - 1) & ~((L) - 1))

#define ALLOC_ROOT_MIN_BLOCK_SIZE (MALLOC_OVERHEAD + sizeof(USED_MEM) + 8)
/* Define some useful general macros (should be done after all headers). */
/*作者:www.manongjc.com  */
#define MY_MAX(a, b) ((a) > (b) ? (a) : (b)) //求两个数值之间的最大值
#define MY_MIN(a, b) ((a) < (b) ? (a) : (b)) //求两个数值之间的最小值

MEM_ROOT 구조와 관련된 정보를 살펴보겠습니다.

typedef struct st_mem_root
{
    USED_MEM    *free;                  /* free block link list的链表头指针 */
    USED_MEM    *used;                  /* used block link list的链表头指针 */
    USED_MEM    *pre_alloc;             /* 预先分配的block */
    size_t        min_malloc;             /* 如果block剩下的可用空间小于该值,将会从free list移动到used list */
    size_t        block_size;             /* 每次初始化的空间大小 */
    unsigned int    block_num;              /* 记录实际的block数量,初始化为4 */
    unsigned int    first_block_usage;      /* free list中的第一个block 测试不满足分配空间大小的次数 */
    void (*error_handler)( void );          /* 分配失败的错误处理函数 */
} MEM_ROOT;

할당된 구체적인 블록 정보는 다음과 같습니다.

typedef struct st_used_mem
{ 
    struct st_used_mem *next; //指向下一个分配的block
    unsigned int left; //该block剩余的空间大小
    unsigned int size; //该block的总大小
} USED_MEM;

실제로 할당 과정에서 MEM_ROOT는 이중 연결 리스트를 통해 사용된 블록과 사용되지 않은 블록을 관리합니다.

MySQL MEM_ROOT 자세히 설명

MEM_ROOT의 초기화 과정은 다음과 같습니다.

void init_alloc_root( MEM_ROOT *mem_root, size_t block_size, size_t pre_alloc_size __attribute__( (unused) ) )
{
    mem_root->free            = mem_root->used = mem_root->pre_alloc = 0;
    mem_root->min_malloc        = 32;
    mem_root->block_size        = block_size - ALLOC_ROOT_MIN_BLOCK_SIZE;
    mem_root->error_handler        = 0;
    mem_root->block_num        = 4; /* We shift this with >>2 */
    mem_root->first_block_usage    = 0;
}

초기화 과정에서 block_size 공간은 block_size-ALLOC_ROOT_MIN_BLOCK_SIZE입니다. 메모리가 부족해서 확장이 필요한 경우 mem_root->block_num >>2 * block_size를 통해 용량을 확장하므로 mem_root->block_num >>2는 1 이상이기 때문에 초기화 과정에서 mem_root->block_num =4(참고:4>>2=1).

MySQL MEM_ROOT 자세히 설명

메모리 할당을 위한 구체적인 단계를 살펴보겠습니다.

void *alloc_root( MEM_ROOT *mem_root, size_t length )
{
    size_t        get_size, block_size;
    uchar        * point;
    reg1 USED_MEM    *next = 0;
    reg2 USED_MEM    **prev;

    length = ALIGN_SIZE( length );
    if ( (*(prev = &mem_root->free) ) != NULL )
    {
        if ( (*prev)->left < length &&
             mem_root->first_block_usage++ >= ALLOC_MAX_BLOCK_USAGE_BEFORE_DROP &&
             (*prev)->left < ALLOC_MAX_BLOCK_TO_DROP )
        {
            next                = *prev;
            *prev                = next->next; /* Remove block from list */
            next->next            = mem_root->used;
            mem_root->used            = next;
            mem_root->first_block_usage    = 0;
        }
        for ( next = *prev; next && next->left < length; next = next->next )
            prev = &next->next;
    }
    if ( !next )
    {       /* Time to alloc new block */
        block_size    = mem_root->block_size * (mem_root->block_num >> 2);
        get_size    = length + ALIGN_SIZE( sizeof(USED_MEM) );
        get_size    = MY_MAX( get_size, block_size );

        if ( !(next = (USED_MEM *) my_malloc( get_size, MYF( MY_WME | ME_FATALERROR ) ) ) )
        {
            if ( mem_root->error_handler )
                (*mem_root->error_handler)();
            DBUG_RETURN( (void *) 0 );                              /* purecov: inspected */
        }
        mem_root->block_num++;
        next->next    = *prev;
        next->size    = get_size;
        next->left    = get_size - ALIGN_SIZE( sizeof(USED_MEM) );    /* bug:如果该block是通过mem_root->block_size * (mem_root->block_num >> 2)计算出来的,则已经去掉了ALIGN_SIZE(sizeof(USED_MEM),这里重复了。 */
        *prev        = next;
    }

    point = (uchar *) ( (char *) next + (next->size - next->left) );
/*TODO: next part may be unneded due to mem_root->first_block_usage counter*/
/* 作者:www.manongjc.com */
    if ( (next->left -= length) < mem_root->min_malloc )
    {                                                                       /* Full block */
        *prev                = next->next;                   /* Remove block from list */
        next->next            = mem_root->used;
        mem_root->used            = next;
        mem_root->first_block_usage    = 0;
    }
}

위 코드의 구체적인 논리는 다음과 같습니다.

1. 무료 연결 리스트를 보고, 해당 공간에 맞는 블록을 찾습니다. 적절한 블록이 발견되면 다음과 같이 합니다.
1.1 크기 왼쪽부터 블록의 초기 주소를 반환합니다. 물론, free list 순회 중에 free list의 첫 번째 블록의 왼쪽 공간
이 할당해야 할 공간을 충족하지 못하는 것으로 판단되어 해당 블록을 10번 검색했습니다
(ALLOC_MAX_BLOCK_USAGE_BEFORE_DROP) 할당 길이가 만족되지 않고 블록의 남은 공간이
4k(ALLOC_MAX_BLOCK_TO_DROP) 미만인 경우 해당 블록은 사용된 연결 리스트로 이동됩니다.

2. 자유 연결 리스트에 적합한 블록이 없는 경우:
2.1 mem_root->block_size * (mem_root->block_num >> 2) 및 length+ALIGN_SIZE(sizeof(USED_MEM) 할당 )
에서 더 큰 것이 새로운 블록 메모리 공간으로 사용됩니다.
2.2 블록의 용도에 따라 블록을 사용 또는 자유 연결 리스트에 걸어 놓습니다.

여기서 주목해야 할 것은 보조 포인터의 사용입니다:

for (next= *prev ; next && next->left < length ; next= next->next)
prev= &next->next;
}

prev는 마지막 블록의 다음이 가리키는 주소를 가리킵니다:

MySQL MEM_ROOT 자세히 설명

따라서 prev의 주소를 새 블록의 주소로 바꾸십시오. 즉, 사용 가능 목록의 끝에 새 블록을 추가하십시오. *prev=next;

MySQL MEM_ROOT 자세히 설명

요약:

MEM_ROOT는 메모리 할당을 위해 경험적 할당 알고리즘을 사용합니다. 후속 블록 수가 증가하면 단일 블록의 메모리도 커집니다. block_size= mem_root->block_size * (mem_root ->block_num > > 2) .


성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.