Processing math: 100%

highlight.pack.js

2015年2月20日金曜日

make_shared の弱点

make_shared の弱点

make_shared で確保されたメモリ領域は,それを参照する weak_ptr が無くならない限り解放されない がイマイチしっくり来なかったので、自分でコード作る+make_sharedのコードを追いかけて理解したので、 それのメモ。

以下のようなコードをメモリ使用量を監視しながらステップ実行して確かにそのようなことを起こっていることを確認出来た。

#include <array>
#include <iostream>
#include <memory>
using namespace std;
struct BigData {
std::array<char, 100000000> data;
};
int main(void){
{
std::weak_ptr<BigData> weak;
{
auto num = std::make_shared<BigData>();
weak = std::weak_ptr<BigData>(num);
double sum = 0;
for (int i = 0; i < 100000000; ++i) sum += num->datai;
cout << "Create weak pointer." << endl;
cout << "Sum: " << sum << endl;
}
cout << "Weak pointer size: " << sizeof(weak) << "MB" << endl;
}
cout << "Weak pointer destroyed." << endl;
return 0;
}
view raw gistfile1.cpp hosted with ❤ by GitHub

というわけで、どうしてそうなっているのかをソース追いかけてみる。 そうすると内部で、

_Ref_count_obj<_Ty> *_Rx =
        new _Ref_count_obj<_Ty>(_STD forward<_Types>(_Args)...);

と内部オブジェクトを作っているようだが、事実上これが shared_ptrの中身だと思って良さそうだ。 で、実際にそのコンストラクタのコードがこんな感じ。

template<class... _Types>
  _Ref_count_obj(_Types&&... _Args)
  : _Ref_count_base()
  {    // construct from argument list
  ::new ((void *)&_Storage) _Ty(_STD forward<_Types>(_Args)...);
  }

つまり、placement new を使って _Ref_count_obj の中に、そのままオブジェクトを構築していることになる。

この _Ref_count_obj_Ref_count_base を継承しているが、これが参照カウンタを持っている。 管理用のカウンタとオブジェクト本体が一緒になっているので、 オブジェクトを参照している weak_ptrshared_ptr がなくならなければオブジェクトが開放されないというのも納得である。

make_shared の中ではメモリの確保回数を減らすためにこのような処理を行っている、ということがわかっていればそれほど難しい事ではなかった。