Windows では、最近解放されたメモリへのポインターを逆参照しようとするとクラッシュが発生し、メモリが無効であることを示す Visual Studio によってトラップされることに気付きました。これは予想通りです。ただし、同じアプリケーションとコード パスを実行して、最近解放されたメモリへのポインターを逆参照しても、Linux ですぐにクラッシュが発生するわけではありません。これは、デバッグ ビルドであっても、Linux カーネル (または GNU C++ ランタイム) が解放されたメモリをすぐに無効にしないことを示唆しています。アプリケーションがクラッシュするまでに、はるかに長い時間がかかります。これは事実ですか?もしそうなら、メモリをより迅速にマップ解除するように強制できますか? そうでない場合、何が起こっているのですか?
2 に答える
http://valgrind.org/を試しましたか? その目的は、あなたが説明したような問題を追跡するのに役立つことです。
new
/のほとんどの実装は、delete
メモリをすぐにシステムに返さないか、少なくとも小さいブロックを返しません。メモリへのポインターを逆参照するだけで、コードが Windows でクラッシュしたことにかなり驚いています。それ以上のことをしていないのですか (たとえば、ポインターから読み取った値を使用します)。
ブロックの大きさは?多くの実装では、ブロックのサイズに応じて異なる戦略を使用し、非常に大きなブロックをすぐに解放します。(IIRC、Linuxはmmap
非常に大きなブロックに対してすぐに実行し、解放するとすぐにアンマップします。もちろん、解放とポインターの逆参照の間にメモリを再割り当てすると、アドレスが新しく割り当てられたスペースにある可能性があります。崩れません。)
結局のところ、マッピングの粒度はページであり、アロケーターが割り当てごとに完全なページをブロックすることを期待することはできません。これは、割り当て解除時にすぐにメモリを無効にすることができるためです。(ページはおそらく少なくとも 4K であり、16 バイトを割り当てるたびに 4K のアドレス空間を失いたくありません。) すべてのメモリ アクセス (ValGrand や Purify など) を追跡することと、実行速度が大幅に遅くなること以外に、唯一の代替手段があります。ガベージコレクションを使用して、メモリへのポインタがある限りメモリが再割り当てされないようにし、割り当て解除時に(delete
またはでfree
)完全に上書きすると、問題が発生する可能性がある値で上書きされます(0xDEADBEEF
または何か)そのように)。それでも、クラッシュするという保証はありません—0xDEADBEEF
あなたが読んでいると思うものの有効な値である可能性があります。(ただし、これにより、コンストラクターでフラグを設定したり、デストラクタでフラグをリセットしたり、各関数でテストしたりすることができます。積極的に防御する必要があるコードの場合。)