野良C++erの雑記帳 このページをアンテナに追加 RSSフィード Twitter

2013-04-04 エイプリルフールはなかったことになりました

make_shared で確保されたメモリ領域は,それを参照する weak_ptr が無くならない限り解放されない

std::make_shared は基本的には効率的だけど弱点もあるよ,って話.

struct Huge
{
  int data[1024][1024];
};

#include <memory>
#include <vector>

int main()
{
  std::vector<std::weak_ptr<Huge>> vec;
  for( int i = 0; i < 100000; ++i ) {
    auto p = std::make_shared<Huge>(p);
    vec.push_back(p);
    p.reset();
    // *p の寿命はここで切れるけど, weak_ptr が残ってる限りメモリ領域は解放されない
    // 結果,メモリを食いつぶしてしまう
    // std::shared_ptr<Huge> p( new Huge() );
    // なら問題ない
  }
}

殆どの実装では, std::weak_ptr は 参照カウントを確認することで 対象のオブジェクトが破棄されたか否かを判別している.

std::make_shared は,オブジェクトと参照カウントを一回のメモリ確保で連続した領域に確保するため,

std::weak_ptr が残っている限り,参照カウントのメモリ領域は解放することができず,

従って,それと同時に確保されたオブジェクトのメモリ領域も解放することができない,という理屈.


一応,領域を動かさずに realloc することは可能だけど,そうしてくれる処理系ってあるんだろうか?

少なくとも標準の realloc では,領域を縮める場合でも領域移動が起きる可能性があるので無理.

また,その場合でも, allocate_shared には対応できないので ぐぬぬ

sakisaka7sakisaka7 2013/04/12 17:57 これは重大な問題だと思います。
例えば大量の weak_ptr を
std::vector<std::shared_ptr<object_type>> all_objects;
std::unordered_map<object_id, weak_ptr_type> special_objects;

このように管理していて、 all_objects の構築を make_shared で行なっている場合:
ObjectManager::ObjectManager()
{
all_objects.push_back(std::make_shared<object_type>());
}

special_objects が保持している weak_ptr を使う場面で、
 「その weak_ptr が指す shared_ptr が解放されていた場合」に
 「その weak_ptr 自身を 管理テーブル(special_objects)から除外する」
という処理を漏れ無く書いておかないと、参照先のオブジェクト自身が永遠に解放されない という状況に容易になりうるのではないでしょうか。

これは、同様な構造を使って大量のリソースを管理するであろうプログラム、例えばゲームプログラム等で極めて重大な「限りなくメモリリークと区別のつかない」リソースの解放漏れが、いとも簡単に発生するのではないでしょうか。

gintenlabogintenlabo 2013/04/14 20:08 実際問題としては,sizeof(object_type) が よほど大きくない限りは,そこまで影響ないと思われます.

というのも,この問題で解放されないのは,あくまでも
そのオブジェクトの実体のために確保されたメモリ領域 *だけ* であり,
例えば
struct Object {
std::vector<std::string> vec;
};
のような Object に対しては, shared_ptr からの参照が破棄された時点で
~Object() が呼ばれて, vec によって確保されたメモリは解放されるからです.

なので,対象のオブジェクトが現実的な大きさの場合には,この問題によって解放されないメモリ領域は
std::weak_ptr のために必要な(スタックなりフリーストア上の)領域の高々数倍程度ですから,
よほどカツカツではない限りは,関係ありません.

また,この問題が深刻に なるほどオブジェクトのサイズが大きい場合には,
make_shared を使わなくても問題がいろいろと起きやすいので,
そのようなパターンは避けるようにしたほうが良いと思います.

sakisaka7sakisaka7 2013/04/15 02:34 あぁ、なるほど。そのコメントを見て納得しました。
まったくもってその通りだと思います。完全に僕の杞憂だったようで安心しました。

スパム対策のためのダミーです。もし見えても何も入力しないでください
ゲスト


画像認証