多くのメモリリークの脆弱性を聞きましたが、メモリリークの実際の作業例を見つけることができませんでした。メモリリークの実際の作業例、おそらくいくつかの大きなオープンソースプロジェクトを提供して、解決策を説明してください。
どうも。
多くのメモリリークの脆弱性を聞きましたが、メモリリークの実際の作業例を見つけることができませんでした。メモリリークの実際の作業例、おそらくいくつかの大きなオープンソースプロジェクトを提供して、解決策を説明してください。
どうも。
実はとても簡単です。あなたの主な場所で:
char* c = new char[4];
次に終了します。これはメモリリークです。new
従わないものはdelete
リークです。
この回答にはいくつかの良い例がありますが、私のコメントが言ったように、外部の観察者が見て簡単に識別できるリークのあるリリースされたアプリケーションを見つけるのはかなり難しいでしょう。
私は、私たちの(巨大な)レガシーコードベースで、このようなコードについて毎日叫び、罵倒し、怒鳴っています:
// returns raw pointer with changing conventions who's the owner...
HelpFoo* Foo::GetFoo(Bar* pBar, OtherFoo* pFoo)
{
// all 'local' variables even those allocated on freestore declared
// and initialized in a large block at the beginning of the function/method
HelpFoo *A = new HelpFoo;
OtherHelpFoo *B, *C;
EvenMore *D = new EvenMore;
// and so on, these blocks can be huge...
// a complicated spaghetti code in here, with dozens of nested 'ifs'
if (/* some expression */) {
} else if (/* some other expression */) {
// and so on... then suddenly:
if (/* some other nested expression */) {
// I forgot that I've allocated other memory at the beginning...
return A;
}
}
// some miserably written logic here and suddenly
if (D) delete D; return A;
// call to some other function with cryptical name without any
// kind of knowledge what happens with the resource:
FooTakesReferenceToPointer(&A);
// suddenly returning something completely different
// what should I free, A, D...?
return C;
}
問題点をコメントで書いてみました。明らかに、例外を忘れてください。スパゲッティコードは非常に悪いので、ロジックが実際に何であるかは誰にもわかりません。したがって、メモリを解放することを忘れることは本当に、本当に簡単であり、それは非常に頻繁に起こります。解決策1:すべてを捨てて書き直します。new
解決策2:スパゲッティをそのままにし、すべてのedリソースをスマートポインターに置き換えるmake_shared
かmake_unique
、コンパイラーに怒鳴らせます。もちろん、最初にテストスイート(以前は存在しなかった)を作成して、すべての可能な入力セット(文書化されていない)に対して同じ(しばしばねじ込まれた)動作を保証します。
編集ジェームズが言ったように、これは未定義の振る舞いなので、約束はありません
あなたはこのようなことをすることができます:
#include <vector>
class Base
{
public:
Base()
{
baseData = new char [1024];
}
~Base()
{
delete [] baseData;
}
private:
char* baseData;
};
class Derived : public Base
{
public:
Derived()
{
derivedData = new char[1024];
}
~Derived()
{
delete [] derivedData;
}
private:
char* derivedData;
};
int main()
{
std::vector<Base*> datablocks;
datablocks.push_back(new Base());
datablocks.push_back(new Derived());
for(unsigned int i = 0; i < datablocks.size(); ++i)
{
delete datablocks[i];
}
datablocks.clear();
return 0;
}
Base *でdeleteを呼び出しており、Baseクラスが仮想デストラクタを宣言していないため、Derivedクラスのデータはここでは削除されません。
ネットワークデータを処理していて、そのデータに基づいて多形の「メッセージオブジェクト」を作成していると想像してください。
while (true)
{
char buf[1024];
size_t len = read_from_network(buf, 1024); // fictitious, for demonstration only
Message * p = Message::Parse(buf, len); // allocates new, dynamic, concrete object
engine.process(p);
}
engine
オブジェクトは、オブジェクトをどこかに保存し、後で再び使用することを選択する場合があります。誰もオブジェクトを削除しない場合は、完全なリークが発生します。
ここには多くの例があります。メモリを割り当てて、解放しないでください。
この良い例は次のとおりです。
char* pBuffer = new char[ 1024 ]; // or something else, dynamically allocated
// do something here
// now suppose, calling f() throws
f();
// do some other things
delete[] pBuffer;
スローするときf()
に、例外がキャッチされない場合、delete[]
実行されることはありません。したがって、メモリリークが発生します。
これは、スマートポインタを使用する必要がある最も良い例の1つです。
もう1つの例は、動的に割り当てられたメモリへのポインタを返す関数です。多くの場合、ユーザーはこのメモリを解放するのを忘れることがあります。何かのようなもの:
char
char* f()
{
return new char[ 1024 ];
}
//...
// in some other function
char* pSomething = f();
// do some stuff here and return
他の回答は十分なヒントを与えますが、私がアプリケーションで見たいくつかの「現実世界」のメモリリーク。これがリリース前かリリース後かは覚えていませんが、それは問題ではないと思います。
void f()
{
BYTE* b = NULL;
f = open a file;
while (!f.end())
{
int size = getNextRecordSize(f);
b = new BYTE;
readNextRecord(f,b);
process record;
}
delete b;
}
これを検出するのは少し難しいです。レビューアは、delete呼び出しを見て、メモリが適切に削除されていることを当然のことと考えるかもしれません。ただし、最後のレコードに割り当てられたメモリのみが削除されます。残りが漏れています。
class A
{
public:
BYTE* get()
{
allocate a new buffer, copy the someData buffer and return that.
The client is expected to delete it
};
private:
BYTE* someData;
};
void f()
{
A a;
B.initialize(a.get()); // It is so convenient to use the pointer. It is not obvious from the function name
// that the result of get has to be deleted.
}
私がコードでよく遭遇する1つの例は、一時的な8ビットメモリが割り当てられ、解放されない画像理解関数です(ええ、新しい操作を行うときは、すぐに削除を実行してください...)
unsigned char* dataToBeUsed = new unsigned char[imgsize];
memcpy(dataToBeUsed, original, imgsize);
// use and process the data here
return value;
割り当てられたメモリが解放されることはありません->メモリリーク。アプリケーションが完全に終了すると、Windowsはメモリを強制終了しますが、その前に、アプリケーション内でそのメモリが失われる->リークされます。
動的に割り当てられたメモリへのポインタを失うだけです。
void foo()
{
int *arr = new int[100];
}
プログラマーが割り当てられたメモリを解放するのを忘れるというメモリリークがあると、メモリリークが発生します:-)
linebuffer = new char[4096];
/* do things */
/* forget to free memory */
通常、メモリリークが発生してからプログラムを終了しても、オペレーティングシステムは通常、プログラムによって割り当てられたリソースを解放するため、害はありません。この問題は、アプリケーション(サービスなど)が長期間実行されている場合に発生します。プログラムによってメモリリークが発生した場合、オペレーティングシステムにそれを回避するメカニズムがない限り、システムのメモリが不足します。そのような場合、それはあなたのプログラムを終了させます。
だから、注意して魚を食べてください:それは記憶にとても良いです:-)
実際の例を挙げると、少しグーグルすると、389 Directory Server(RedHatオープンソース製品)でこのメモリリークが発生しました。