2

私は最近、私のプログラムで奇妙な振る舞いを見ました。大量のオブジェクト(500MBのRAM)を作成して解放した後、プログラムのメモリフットプリントは元のサイズに戻りません。それでも160MBのフットプリントを示しています(プライベートワーキングセット)。

通常の動作?

Borlandのメモリマネージャはこのように動作しないため、可能であれば、これがFastMMの正常な動作であることを確認(または確認)してください。かなり複雑なMDI子(複数のコントロール/オブジェクトを含む)を作成する便利なプログラムがある場合は、次のことができます。そのMDI子の250個のインスタンスを(同時に)ループで作成し、それらをすべて解放して、メモリフットプリントを確認します。これらのMDIの子で少なくとも200〜300MBまたはRAMを消費することを確認してください。

特に、まだDelphi 7を使用している場合は、FastMMを一時的に無効にすることで違いを確認できます。

ありがとう


誰かが興味を持っている場合、特に何らかの証拠が必要な場合、これはメモリリークではありません(私のコードのメモリリークではないことを願っています-これはこの投稿のポイントの1つでもあります:それが私のせいであるかどうかを確認すること)、ここに元の議論があります:

私のプログラムはメモリを解放しません。なんで?
未使用のメモリを解放するようにメモリマネージャを説得する方法

4

5 に答える 5

3

親愛なるアルターさん、あなたの推測がどれほど的外れで、以前に何度も人々があなたに言ったことを聞いていないことに、私は目がくらみました。

いくつかのことを整理しましょう。メモリ管理 101. よくお読みください。

Delphi でメモリを割り当てる場合、2 つのメモリ マネージャが関与します。

システムメモリマネージャー

1 つ目は、システム メモリ マネージャーです。これは Windows に組み込まれており、4kb サイズのページでメモリを提供します。

ただし、常に RAM (または物理メモリ) にメモリが提供されるとは限りません。データをハード ドライブに保存し、アクセスする必要があるたびに読み返すことができます。これは非常に遅いです。

つまり、512Mb の物理メモリがあるとします。2 つのプログラムを実行し、それぞれが 1Gb のメモリを要求します。OSは何をしますか?

両方の要求を許可します。どちらのアプリもそれぞれ 1Gb のメモリを取得します。どちらも、すべてのメモリが「メモリ内」にあると考えています。しかし実際には、RAM に保持できるのは 512Mb だけです。残りはページ ファイルに保存されますが、アプリはそれを認識しません。動作が遅いだけです。

ワーキング セットのサイズ

さて、あなたが測定している「ワーキングセットサイズ」とは何ですか?

RAMに保持されるのは、割り当てられたメモリの一部です。

1Gb のメモリを割り当てるアプリケーションがあり、RAM が 512Mb しかない場合、ワーキング セットのサイズは 512Mb になります。1Gbのメモリを「使用」しますが!

メモリを必要とする別のアプリケーションを実行すると、OS はめったに使用されない「メモリ」のブロックをハード ドライブに移動して、RAM を自動的に解放します。

仮想メモリの割り当ては変わりませんが、ハード ドライブのページ数が増え、RAM のページ数が減ります。ワーキング セットのサイズが減少します。

このことから、ワーキング セットのサイズを最小化しようとするのは無意味であることを、この時点で理解しているはずです。あなたは何も達成していません。何らかの意味でメモリを解放していません。データをハードドライブにオフロードしているだけです。

ただし、システムは必要なときに自動的にそれを行います。また、必要になるまで RAM にスペースを確保しても意味がありません。アプリケーションの速度が低下しているだけです。それだけです。

TLDR : 「ワーキング セット サイズ」は「アプリケーションが使用するメモリ量」ではありません。それは「今どれだけ準備ができているか」です。それを最小限に抑えようとしないでください。事態を悪化させるだけです。

Delphi メモリ マネージャ

OS は 4Kb のページ単位で仮想メモリを提供します。しかし、多くの場合、はるかに小さなチャンクでそれが必要になります。たとえば、整数の場合は 4 バイト、構造体の場合は 32 バイトです。ソリューション?

FastMM や BorlandMM などのアプリケーション メモリ マネージャー。

その仕事は、オペレーティング システムからメモリをページに割り当て、必要なときにそれらのページの小さなチャンクを提供することです。

つまり、14 バイトのメモリを要求すると、次のようになります。

  1. FastMM に 14 バイトのメモリを要求します。
  2. FastMM は OS に 1 ページのメモリ (4096 バイト) を要求します。
  3. OS は 1 ページのメモリを許可し、RAM でバックアップします (実際の RAM に保存されます)。
  4. FastMM はそのページを保存し、14 バイトを切り取って提供します。

さらに 14 バイトを要求すると、FastMM は同じページからさらに 14 バイトをカットします。

メモリを解放するとどうなりますか?同じことを逆に:

  1. 14 バイトを FastMM に解放します。何も起こりません。
  2. さらに 14 バイトを解放します。FastMM は、割り当てた 4096 バイトのページが完全に使用されていないことを確認します。
  3. したがって、ページを解放してシステムに戻します。

FastMMは14 バイトだけをシステムに解放できないことに注意してください。ページ単位でメモリを解放する必要があります。ページ全体が解放されるまで、FastMM は何もできません。誰もできません。

では、すべてをリリースしたにもかかわらず、ワーキング セットのサイズがこれほど大きいのはなぜでしょうか。

まず、ワーキング セットのサイズは測定すべきサイズではありません。仮想メモリの消費量です。ただし、ワーキング セットのサイズが大きいと、仮想メモリの消費量も多くなります。

どうしたの?この時点でわかるはずです。

1kb を割り当て、次に 3kb のメモリを割り当てるとします。どのくらいの仮想メモリを割り当てましたか? 4KB、1ページ。

ここで 3Kb を解放します。現在、どのくらいの仮想メモリを使用していますか? 1Kb? いいえ、まだ 1 ページです。システムから 1 ページ未満を割り当てることはできません。まだ 4096 バイトの仮想メモリを使用しています。

それを1000回行ったとします。1kb、3kb、1kb、3kb、1kb、3kbなど。そのように 1000 * 4kb = 4 mb を割り当ててから、3kb の部分をすべて解放します。現在、どのくらいの仮想メモリを使用していますか?

それでも4MB。最初に 1000 ページを割り当てたからです。すべてのページのうち、1kb と 3kb のチャンクを取得しました。3kb のチャンクを解放しても、1kb のチャンクはメモリに割り当てたすべてのページを保持し続けます。また、各ページには 4kb の仮想メモリが必要です。

メモリ マネージャーは、魔法のように 1kb のチャンクすべてをまとめて「移動」することはできません。仮想アドレスはコードのどこかから参照できるため、これは不可能です。これは FastMM の特徴ではありません。

しかし、BorlandMM を使用すると、すべてがうまく機能するのはなぜでしょうか?

一致。BorlandMM が FastMM とは少し異なる方法でメモリを提供するのは、たまたまのことかもしれません。次に、アプリで何かを変更すると、BorlandMM は FastMM と同じように動作します。メモリ マネージャが、メモリの断片化と呼ばれるこの影響を完全に防ぐことは不可能です。

それで、私は何をしますか?

短い答えは、これが気になるまではあまりありません。

おわかりのように、最新のオペレーティング システムでは、実際に誰かの RAM を消費することはありません。上記のとおり、OS は、他のアプリケーションのために RAM が必要になると、自動的にページをスワップアウトします。これは心配する必要はありません。

そして、「過剰な」記憶は失われません。ページは割り当てられていますが、それぞれの 3kb は「空き」としてマークされています。次にアプリがメモリを必要とするとき、メモリ マネージャーはその領域を使用します。

しかし、本当にそれを助けたい場合は、割り当てを再編成して、保持する予定の割り当てが最初に行われ、すぐにリリースする割り当てがすべてその後に割り当てられるようにする必要があります。

このように: 1kb、1kb、1kb、...、3kb、3kb、3kb...

ここで 3kb のチャンクをすべて解放すると、仮想メモリの消費量が大幅に減少します。

これは常に可能であるとは限りません。それが不可能な場合は、何もしないでください。ほぼそのままで大丈夫です。

そして追伸

そもそも 500 フォームを割り当てるべきではありません。これは明らかに進むべき道ではありません。これを修正すると、メモリの割り当てと解放について考える必要さえなくなります。

率直に言って、同じトピックに関する4つの投稿は少し多すぎるため、これで問題が解決することを願っています.

于 2010-12-27T14:18:14.547 に答える
2

IIRC、Delphi メモリ マネージャは、解放されたメモリをすぐに OS に返しません。

メモリは、ブロックと呼ばれる小、中、大のサイズのチャンクで割り当てられます。これらのブロックは、後で別の割り当てが要求されたときにすぐに使用できるように、コンテンツが破棄された後もしばらく保持されます。

これにより、複数のオブジェクトを連続して割り当てるために必要なシステム コールの量が制限され、ヒープの断片化を回避するのに役立ちます。

于 2010-12-21T12:59:21.937 に答える
2

Infirming: Delphi 2007、デフォルトのメモリ マネージャー (FastMM バリエーションである必要があります)。重い物体に対するいくつかのテスト:

  1. 初期メモリ 2Mb、ピーク メモリ 30Mb、最終メモリ 4Mb。
  2. 初期メモリ 2Mb、ピーク メモリ 1Gb、最終メモリ 5.5Mb。
于 2010-12-21T13:10:14.210 に答える
1

解決した

この動作が FastMM によって生成されることを確認するために (Barry Kelly の提案による)、大量の RAM を割り当てる 2 つ目のプログラムを作成しました。Windows が RAM を使い果たすとすぐに、プログラムのメモリ使用率が元の値に戻りました。

問題が解決しました。本当の「問題」を指摘してくれた唯一の人物である Barry Kelly に特に感謝します。

于 2010-12-27T16:41:50.300 に答える
1

160MB がまだ割り当てられている時点での heapmanager 統計 (GetHeapStatus) は何ですか?

于 2010-12-21T15:35:31.880 に答える