コスモリサーチの凄腕エンジニアたちが日々、難題と格闘した記録
Copyright Cosmo Research Corp.

pthreadの仮想メモリ解放

LinuxPosix Thread(pthread)で仮想メモリが解放されない時があるので 条件を調べてみました。 環境はZYNQのLinuxなのでPCのLinuxとは動作が違う部分があるかもしれません。

結果的にはpthread_createで作成したスレッドではスレッド終了時には仮想メモリを解放されませんが、 再度pthread_createをした時に前回確保したメモリを再利用しているようです。 (=新たな確保をしていない)

pthread_createで確保されるメモリはスレッドのスタックサイズに依存するのですが 初期状態だと8MBがスタックサイズになっているので組み込みのプログラムとしては大量の メモリを使用しているように見えてしまいます。 解放しないのは仮想メモリだけで物理メモリが解放されず残っているわけではないです。 プログラム動作的には問題ないのですがPSコマンドやtop/htopコマンドを使用してメモリ使用量を見るとき、 スレッドでで自動的に確保されるサイズが加算されて見えるていることには注意しないといけません。

動作例

スレッドを6つ作成、終了後、新たにスレッドを2つ作成するプログラムです。 6つスレッドが終了するとpthread_createで確保されたメモリは全て解放される動作を期待するのですが、 実際に動かしてみると6つのスレッドが終了した時点で仮想メモリが確保されたままになります。

メモリの使用量がわかるよう動かしている途中でコンソールからhtopコマンドを使用して VIRT(仮想メモリ)とRES(物理メモリ)を見てみます。

ステップ1  初回のpthread_create

スレッドを6個作成すると1スレッド当たり約8MB程度、6スレッド作成完了した時点でこのプログラムでは 50MBのメモリが仮想メモリとして確保されます。 6つのスレッドが終了し、joinsした段階でメモリは解放されるはずですが、topのVIRTを見るとまだ42Mbyte程度使用しています。

ステップ2  2回目以降のpthread_create

この後、2つのスレッドを再度作成するとVIRTは42MBのままです。 これは前回確保した仮想メモリを流用しているため、新たなメモリ確保が行われてなかったものと考えられます。 2つのスレッドが終了後もVIRTは42MBを維持し、プログラム終了で解放されます。

//スレッド本体関数
void *func_null(void *arg){
    printf("new thread created\n");
    sleep(10);
}


int main()
{
    pthread_t th_cli1;
    pthread_t th_cli2;
    pthread_t th_cli3;
    pthread_t th_cli4;
    pthread_t th_cli5;
    pthread_t th_cli6;

    pthread_attr_t th_cli_attr;
    int status;


    //--------------------------------------------------------------------------------//
    // スレッド属性を作ってスレッド作成の準備
    //--------------------------------------------------------------------------------//
    pthread_attr_init(&th_cli_attr);
    //--------------------------------------------------------------------------------//
    // TEST1 スレッドを6個作成
    //--------------------------------------------------------------------------------//
    status = pthread_create(&th_cli1,&th_cli_attr,func_null,(void*)0);
    status = pthread_create(&th_cli2,&th_cli_attr,func_null,(void*)0);
    status = pthread_create(&th_cli3,&th_cli_attr,func_null,(void*)0);
    status = pthread_create(&th_cli4,&th_cli_attr,func_null,(void*)0);
    status = pthread_create(&th_cli5,&th_cli_attr,func_null,(void*)0);
    status = pthread_create(&th_cli6,&th_cli_attr,func_null,(void*)0);

    pthread_join(th_cli1,NULL);
    pthread_join(th_cli2,NULL);
    pthread_join(th_cli3,NULL);
    pthread_join(th_cli4,NULL);
    pthread_join(th_cli5,NULL);
    pthread_join(th_cli6,NULL);

    printf("6 thread finished\n");
    sleep(10);
    //--------------------------------------------------------------------------------//
    // TEST2 スレッドを2個作成
    //--------------------------------------------------------------------------------//
    status = pthread_create(&th_cli1,&th_cli_attr,func_null,(void*)0);
    status = pthread_create(&th_cli2,&th_cli_attr,func_null,(void*)0);

    pthread_join(th_cli1,NULL);
    pthread_join(th_cli2,NULL);
    printf("2 thread finished\n");
    sleep(10);
    return;
}