メモリリークがソフトの機能停止を起こす仕組み

2022年5月31日バグの巣

メモリリークとは動的メモリの残量が減っていく事です

メモリリークのバグが潜在していると、それまでは問題なく動作していたソフトがある日突然に機能しなくなります。それまでは問題なく動作しているので、出荷前のテストでも見つけにくいくせに、ソフトが機能しなくなるので影響が大きく、ソフトエンジニアにとってはとても嫌なバグです。

では、メモリリークが起きるとなぜソフトが機能停止するのでしょうか? リークとは漏れ出す事なのでメモリリークとはメモリの漏れ出しという意味です。ここで漏れだしているメモリとは、OSがアプリケーションソフトに貸し出す動的メモリと呼ばれる種類のメモリの事です。動的メモリが漏れ出して残量が減っていく事をメモリがリークしていると言います。動的メモリの漏れ出しが続いて動的メモリが枯渇してしまう(残量がゼロ)事で、ソフトの機能停止が起きます。

この記事では、メモリリークがどんな仕組みで起きて、なぜソフトが機能しなくなるのかを順番に紹介します。なお、メモリリークのバグが入り込まないようにするに何に注意すれば良いのかは、こちらの記事で紹介していますので、そちらも合わせてご覧ください。

動的メモリはOSからのメモリの貸し出しと返却で成り立っています

メモリリークは動的メモリの漏れ出しの事なのですが、まずは動的メモリについて簡単に紹介しましょう。アプリケーションソフトが使うメモリは静的メモリと動的メモリに分けられます。英語で言うと Static Memory と Dybamic Memory ですね。一般によく使うメモリの多くは静的メモリで、アプリケーションソフトが起動する時に必要な容量のメモリが確保されて、ソフトが終了するまでそのメモリの容量は変わりません

もう一方の動的メモリは、アプリケーションソフトが起動する時にはまだ確保されていません。アプリケーションソフトが実行される中で、メモリが必要になったタイミングでアプリケーションソフトからOSに対して動的メモリの獲得要求を発行して、必要な容量の動的メモリをOSから貸して貰います。そして、アプリケーションソフトがその動的メモリを使い終えたら、アプリケーションソフトからOSに動的メモリの解放要求を発行する事で、動的メモリをOSに返します。

アプリケーションソフトが必要なタイミングで必要な容量の動的メモリをOSから借りる仕組みが、OSの提供する動的メモリの機能です。アプリケーションソフト-1を例として、使用するメモリの容量がソフトの実行に従って時間とともに変わっていく様子をグラフにすると、以下のようになります。グラフの青い部分は静的メモリで、最初からずっと同じ容量です。それに対して赤い部分が動的メモリで、アプリケーションソフトー1の実行に従って必要な容量をOSから借りて使うので、時間の経過と供に使っている(借りている)動的メモリの容量は変化しています。

動的メモリを貸し出すためにOSは動的メモリプール(貯蔵庫)を持っています

動的メモリの貸し出し/返却の動きを、この機能を提供するOSの側から見てみましょう。動的メモリをアプリケーションソフトに貸し出す機能を実現するために、OSは動的メモリプール(動的メモリの貯蔵庫)を持っています。アプリケーションソフトから動的メモリの獲得の要求が来ると、OSはこの動的メモリプールから必要な容量の動的メモリを取り出して、アプリケーションソフトに貸し出します。アプリケーションソフトから動的メモリが解放されると、OSはそれを動的メモリプールに戻します。

アプリケーションソフトからの動的メモリの獲得/解放の要求に従って、OSが動的メモリプールから動的メモリを貸し出したり返したりするので、それに従って動的メモリプールの残量も時間と供に変わります。その様子をグラフにすると、以下のようになります。この例では、OSは動的メモリに使える動的メモリプールの容量として 30M を起動時に用意していたとします。赤い部分がアプリケーションソフトに貸し出し中の動的メモリの容量で、青い部分が動的メモリプールの残量です。

時刻2~時刻5では、アプリケーションソフトー1が先のグラフのように 10M, 25M, 5M, 10M を借りて使っています。それに加えて時刻4~時刻5では、別のアプリケーションソフトー2が 15M を借りて使っているので、時刻4~時刻5ではOSから見ると2つのアプリケーションで合計して 20M, 25M の動的メモリを貸し出しています。このように、時間と供に貸し出し中の動的メモリ容量と動的メモリプールの残量が変わってきます。

動的メモリの残量が不足するとソフトの提供する機能に少し影響が出ます

これがOSの提供する動的メモリの仕組みですが、この仕組みが正しく動作するには動的メモリプールに十分な残量が残っている必要があります。上のグラフだと、動的メモリプールの残量が常に残っているので問題は起きてはいません。しかしもし、時刻-5の時点でさらに別のアプリケーンソフト-3が10M の容量の動的メモリの獲得を要求してきたとすると、残量が5Mしかないので要求されている10M の動的メモリを貸しせなくなり、アプリケーションソフト-3の実行に影響がでて、場合によっては機能が提供できなくなります。

もちろん、アプリケーションソフト-3には、動的メモリの獲得に関するリトライ処理などが組み込まれているでしょうから、動的メモリの獲得要求がエラーになっても、少し時間を空けて再度動的メモリの要求を行うと思います。その時までに、アプリケーションソフトー1が 5M以上の容量の動的メモリをOSに返していれば、アプリケーションソフトー3は 10M の動的メモリをOSから借りられるので、機能が正常に動作します。

この様に、動的メモリを使うアプリケーションソフトでは、一時的な動的メモリの獲得要求の失敗は想定の事態として、リトライによる再実行の仕組みが予め組み込まれてます(準正常系の処理)。ですので、一時的にOSの動的メモリの残量が枯渇しても、暫くして残量に余裕が出るならば、機能の応答性のが少し遅くなるというような軽微な影響は出るかもしれませんが、ソフトウエアは全体として正常な動作が続きます。

アプリケーションソフトが動的メモリの解放を忘れると何がおきるでしょうか

このようにOSから動的メモリを借りて処理を実行するアプリケーションソフトで、借りたメモリを返し忘れる事をメモリリークと呼びます。例えば、前に出てきたアプリケーションソフト-1が時刻-3で借りていた25Mのメモリのうちで、10Mの動的メモリに関して何等かのバグで時刻-4でOSへの解放処理(メモリのOSへの返却)が抜けたとします。

バグで一旦実行が抜けてしまったメモリの解放処理は、ソフト的にはエラーとは認識できないのでリトライ等も行われないために、もう2度と実行される事はありません。ですので、アプリケーションー1の側でのメモリの使用量の変化は、下の図のようになります。一旦解放処理が抜けた 10M はずっとメモリ返却忘れのままでになってしまいます。

メモリのリークがあると動的メモリプールの貸し出し可能な容量が減ります

このような動的メモリの返却忘れ(アプリケーションソフトによる解放処理の漏れ)を、動的メモリを提供するOSの側から見るとどうなるでしょうか? OSは、動的メモリプールからメモリを取り出してアプケーションソフトに貸し出し、アプリケーションソフトから返却された動的メモリはまた動的メモリプールに戻します。ここで、アプリケーションソフトで返却忘れのバグがあり、永久に返却されない動的メモリがあると、その後はその容量分だけ貸し出しに使える動的メモリプールの容量が減った事になります。

今回の例だと、アプリケーションソフトー1が時刻-4で10Mの動的メモリの返却忘れを起こしています。この10Mの動的メモリは永久にOSに返却されないので、OSの動的メモリプールでは下の図のように実際にアプリケーションソフトに貸し出せる動的メモリの容量が、時刻-4以降は最初の 30M から 20M まで減ってしまった状態になります。

このように、OSが用意していた動的メモリプール(メモリの貯蔵庫)の実質的な容量が減ってしまう状態の事を、メモリ容量が漏れ出していいると表現しメモリリークと呼んでいるのです。

動的メモリの返却が忘れられると貸しせるメモリが無くなります

アプリケーションソフトによる動的メモリの解放処理漏れ(動的メモリの返却忘れ)が少々あっても、OSの動的メモリプールの残量用に余裕がある間は何も問題は起きません。ソフトの機能は全て正常に動作します。

しかし、一旦リークした動的メモリは二度と復活する事はありません。そのため、アプリケーションソフトでメモリリークのバグが実行される条件が揃う都度OSの動的メモリからのメモリがリークしていきます。動的メモリのリークがが積み重なっていき、ついにはOSの動的メモリプールが必要最低限の容量よりも減ってしまうと、アプリケーションソフトに動的メモリを貸し出す事ができなくなります。そうすると、動的メモリを使うアプリケーションソフトは機能を停止していしまいます。これが、メモリリークが進んでメモリ枯渇(動的メモリの残量がゼロ)となって、ソフトの機能が停止してしまう仕組みです。

メモリリークによ機能停止は見つけにくいです

このように、メモリリークのバグがアプリケーションソフトに潜在していても、OSの動的メモリプールの残量に余裕があるうちは、ソフトは問題なく動作するので、通常のテストではメモリリークのバグはなかなか見つけ難いです。メモリリークのバグを見付けるには、メモリ枯渇を起こしやすいように動的メモリプールの容量を小さくしたり、様々な条件でのエージングを行うなど、メモリリークによる影響を見付けるための工夫をしたリークテストを実施する必要が在ります。

メモリリークのバグは一旦入り込んでしまうと見つけにくいですので、ソースコードを作成する時にはメモリリークのバグを作りこまないように注意しましょう。どんな点に注意すれば良いかは、こちらの記事で紹介していますので、興味のある方はご覧ください。

バグの巣に戻る