第47章 ファーストビンの解放

使われていないファーストビンのチャンクは、他のチャンクと合成することで解放することができます。

チャンクを合成する場合は malloc_consolidate() 関数をコールします。

malloc.c(https://github.com/MacKomatsu/glibc/blob/release/2.27/master/malloc/malloc.c). 

1003 struct malloc_chunk;
1004 typedef struct malloc_chunk* mchunkptr;
1587 typedef struct malloc_chunk *mfastbinptr;
1588 #define fastbin(ar_ptr, idx) ((ar_ptr)->fastbinsY[idx])
4392 /*
4393   ------------------------- malloc_consolidate -------------------------
4394
4395   malloc_consolidate is a specialized version of free() that tears
4396   down chunks held in fastbins.  Free itself cannot be used for this
4397   purpose since, among other things, it might place chunks back onto
4398   fastbins.  So, instead, we need to use a minor variant of the same
4399   code.
4400 */
4401
4402 static void malloc_consolidate(mstate av)
4403 {
4404   mfastbinptr*    fb;                 /* current fastbin being consolidated */
4405   mfastbinptr*    maxfb;              /* last fastbin (for loop control) */
4406   mchunkptr       p;                  /* current chunk being consolidated */
4407   mchunkptr       nextp;              /* next chunk to consolidate */
4408   mchunkptr       unsorted_bin;       /* bin header */
4409   mchunkptr       first_unsorted;     /* chunk to link to */
4410
4411   /* These have same use as in free() */
4412   mchunkptr       nextchunk;
4413   INTERNAL_SIZE_T size;
4414   INTERNAL_SIZE_T nextsize;
4415   INTERNAL_SIZE_T prevsize;
4416   int             nextinuse;
4417   mchunkptr       bck;
4418   mchunkptr       fwd;
4419
4420   atomic_store_relaxed (&av->have_fastchunks, false);
4421
4422   unsorted_bin = unsorted_chunks(av);
4423
4424   /*
4425     Remove each chunk from fast bin and consolidate it, placing it
4426     then in unsorted bin. Among other reasons for doing this,
4427     placing in unsorted bin avoids needing to calculate actual bins
4428     until malloc is sure that chunks aren't immediately going to be
4429     reused anyway.
4430   */
4431
4432   maxfb = &fastbin (av, NFASTBINS - 1);
4433   fb = &fastbin (av, 0);
4434   do {
4435     p = atomic_exchange_acq (fb, NULL);
4436     if (p != 0) {
4437       do {
4438   {
4439     unsigned int idx = fastbin_index (chunksize (p));
4440     if ((&fastbin (av, idx)) != fb)
4441       malloc_printerr ("malloc_consolidate(): invalid chunk size");
4442   }
4443
4444   check_inuse_chunk(av, p);
4445   nextp = p->fd;
4446
4447   /* Slightly streamlined version of consolidation code in free() */
4448   size = chunksize (p);
4449   nextchunk = chunk_at_offset(p, size);
4450   nextsize = chunksize(nextchunk);
4451
4452   if (!prev_inuse(p)) {
4453     prevsize = prev_size (p);
4454     size += prevsize;
4455     p = chunk_at_offset(p, -((long) prevsize));
4456     unlink(av, p, bck, fwd);
4457   }
4458
4459   if (nextchunk != av->top) {
4460     nextinuse = inuse_bit_at_offset(nextchunk, nextsize);
4461
4462     if (!nextinuse) {
4463       size += nextsize;
4464       unlink(av, nextchunk, bck, fwd);
4465     } else
4466       clear_inuse_bit_at_offset(nextchunk, 0);
4467
4468     first_unsorted = unsorted_bin->fd;
4469     unsorted_bin->fd = p;
4470     first_unsorted->bk = p;
4471
4472     if (!in_smallbin_range (size)) {
4473       p->fd_nextsize = NULL;
4474       p->bk_nextsize = NULL;
4475     }
4476
4477     set_head(p, size | PREV_INUSE);
4478     p->bk = unsorted_bin;
4479     p->fd = first_unsorted;
4480     set_foot(p, size);
4481   }
4482
4483   else {
4484     size += nextsize;
4485     set_head(p, size | PREV_INUSE);
4486     av->top = p;
4487   }
4488
4489       } while ( (p = nextp) != 0);
4490
4491     }
4492   } while (fb++ != maxfb);
4493 }

まずは fb のポインターを NULL と交換してチャンク p をゲットします。

4432   maxfb = &fastbin (av, NFASTBINS - 1);
4433   fb = &fastbin (av, 0);
4434   do {
4435     p = atomic_exchange_acq (fb, NULL);
4436     if (p != 0) {
4437       do {
//中略
4444   check_inuse_chunk(av, p);
4445   nextp = p->fd;
//中略
4489       } while ( (p = nextp) != 0);
4490
4491     }
4492   } while (fb++ != maxfb);

これによりファーストビンでループを回します。

内部ループとして p が 0 に評価されるなら次のループに移行します。

前のチャンクが使用されていない場合は unlink() マクロをコールします。

4452   if (!prev_inuse(p)) {
4453     prevsize = prev_size (p);
4454     size += prevsize;
4455     p = chunk_at_offset(p, -((long) prevsize));
4456     unlink(av, p, bck, fwd);
4457   }

次のチャンク(nextchunk)がトップチャンクでなく、次のチャンクが使用されていない場合は unlink() マクロをコールします

4459   if (nextchunk != av->top) {
4460     nextinuse = inuse_bit_at_offset(nextchunk, nextsize);
4461
4462     if (!nextinuse) {
4463       size += nextsize;
4464       unlink(av, nextchunk, bck, fwd);
4465     } else
4466       clear_inuse_bit_at_offset(nextchunk, 0);

続けて、前のチャンクとマージします。

4468     first_unsorted = unsorted_bin->fd;
4469     unsorted_bin->fd = p;
4470     first_unsorted->bk = p;
4471
4472     if (!in_smallbin_range (size)) {
4473       p->fd_nextsize = NULL;
4474       p->bk_nextsize = NULL;
4475     }
4476
4477     set_head(p, size | PREV_INUSE);
4478     p->bk = unsorted_bin;
4479     p->fd = first_unsorted;
4480     set_foot(p, size);

後は合成したチャンクを未整理ビン( Unsorted Bin )のヘッドに追加するだけです。

もし次のチャンク(nextchunk)がトップチャンクであるなら、トップチャンクにマージします。

4483   else {
4484     size += nextsize;
4485     set_head(p, size | PREV_INUSE);
4486     av->top = p;
4487   }

この場合は単にチャンク p のヘッドサイズを変更して、トップチャンクに設定しているだけです。

Copyright 2018-2019, by Masaki Komatsu