6

私は組み込みプロセッサ (400 MHz Intel PXA255 XScale) に取り組んでいますが、「新しい」操作を満たすのに十分なメモリがないケースが 1 つあると思いました。プログラムはクラッシュしなかったので、他のスレッドがメモリを解放したと推測しましたが、これは一時的なものでした。これは非常に重要なコードであるため、終了することはできず、何らかのエラーをリモート ユーザーに返す必要があります。

問題を解決するには、次の小さな修正で十分でしょうか、それとももっと良い方法がありますか? すべての 'new' を次のコードに置き換える前に、質問したいと思いました。

char someArr[];
do{ 
    someArr = new char[10]; 
    Sleep(100); // no justification for choosing 100 ms
} while ( someArr == NULL );

睡眠は役に立ちますか?再試行の最大回数を設定する必要がありますか? どこでも静的初期化を使用できますか?

最終更新:有益な回答をありがとうございましたが、メモリ割り当ての失敗をチェックするコードにエラーがあったことが判明しました。私はこれらの答えをすべて心に留めておき、できるだけ多くの malloc と new を置き換えます (特にエラー処理コードで)。

4

9 に答える 9

15

あなたはローカルな理由付けによってグローバルな問題を解決しようとしています。全体的な問題は、デバイス全体で、オペレーティング システムとすべてのアプリケーション用の RAM (および場合によってはバッキング ストア) の量が限られていることです。この RAM の量を超えないようにするために、いくつかのオプションがあります。

  • 各プロセスは、起動時にプロセスごとに決定される固定量の RAM で動作します。プログラマーは、すべてが適合することを確認するために推論を行います。そうです、すべてを静的に割り当てることが可能です。これは大変な作業であり、システムの構成を変更するたびに割り当てを再検討する必要があります

  • プロセスは、自身のメモリ使用量とニーズを認識しており、必要なメモリ量について継続的に互いにアドバイスします。メモリ不足にならないように連携します。これは、システム内の少なくとも一部のプロセスが独自のメモリ要件を調整できることを前提としています (たとえば、内部キャッシュのサイズを変更することによって)。Alonso と Appel は、このアプローチに関する論文を書きました。

  • 各プロセスは、メモリーが枯渇する可能性があることを認識しており、最小量のメモリーを消費する状態にフェイルオーバーできます。多くの場合、この戦略は、メモリ不足の例外を持つことによって実装されます。例外は main() 内またはその近くで処理され、メモリ不足イベントは基本的にプログラムを最初から再起動します。このフェイルオーバー モードは、ユーザーの要求に応じてメモリが増加する場合に機能します。プログラムのメモリ要件がユーザーの操作とは無関係に増加すると、スラッシングが発生する可能性があります。

上記の提案は、どのシナリオにも一致しません。代わりに、他のプロセスが問題を解決し、必要なメモリが最終的に表示される ことを期待しています. あなたは幸運になるかもしれません。あなたはしないかもしれません。

システムを確実に動作させたい場合は、限られたメモリを共有する必要があることを考慮して、システムで実行されているすべてのプロセスの設計を再検討することをお勧めします。思ったよりも大変な作業かもしれませんが、問題を理解していれば、これを行うことができます。幸運を!

于 2008-12-15T02:44:28.717 に答える
2

他の回答には多くの良い点がありますが、すべてのスレッドが同様のループに入ると、プログラムがデッドロックすることを追加する価値があると思いました。

この状況に対する「正しい」答えは、おそらく、プログラムのさまざまな部分に厳密な制限を設けて、メモリを過剰に消費しないようにすることです。それにはおそらく、プログラムのすべての部分にわたって主要なセクションを書き直す必要があります。

次善の策は、割り当ての試行が失敗した場合に、プログラムの残りの部分に、より多くのメモリが必要であることを伝えるコールバックを用意することです。おそらく、プログラムの他の部分が通常より積極的に一部のバッファを解放したり、検索結果のキャッシュに使用されたメモリを解放したりできます。これには、プログラムの他の部分に新しいコードが必要になります。ただし、これは、プログラム全体の書き直しを必要とするのではなく、段階的に行うことができます。

別の解決策は、ミューテックスを使用して大規模な (一時的な) メモリ要求をプログラムで保護することです。後でもう一度試すことができれば、すぐにメモリが解放されると確信しているようです。大量のメモリを消費する可能性のある操作にはミューテックスを使用することをお勧めします。これにより、別のスレッドが必要なメモリを解放したときにすぐにスレッドを起動できます。そうしないと、メモリがすぐに解放されても、スレッドは 10 分の 1 秒間スリープします。

また、sleep(0) を試すこともできます。これは、実行する準備ができている他のスレッドに制御を渡すだけです。これにより、他のすべてのスレッドがスリープ状態になった場合に、スレッドが 100 ミリ秒のセンテンスを待機するのではなく、すぐに制御を取り戻すことができます。ただし、少なくとも 1 つのスレッドがまだ実行を希望している場合は、そのスレッドが制御を放棄するまで待機する必要があります。これは通常、Linux マシンでは 10 ミリ秒です。最後に確認しました。他のプラットフォームについてはわかりません。スレッドが自発的にスリープ状態になった場合、スレッドの優先度がスケジューラーで低くなる場合もあります。

于 2008-12-15T03:31:16.377 に答える
1

他の人が述べているように、理想的には、事前の設計とソフトウェアアーキテクチャによってこの問題を回避しますが、現時点では、それは実際には選択肢ではないと思います。

別の投稿で言及されているように、一部のユーティリティ関数でロジックをラップして、メモリ不足のコードをすべての場所に記述してしまうことがないようにすることをお勧めします。

実際の問題を解決するには、共有リソースであるメモリを使用しようとしますが、その共有リソースがシステム内の別のスレッドによって使用されているため、使用できません。理想的には、システム内の他のスレッドの1つが必要なリソースを解放するのを待ってから、そのリソースを取得します。すべての割り当てと解放呼び出しをインターセプトする方法がある場合は、メモリが使用可能になるまで割り当てスレッドがブロックされ、メモリが使用可能になると解放が割り当てスレッドに通知するように設定できます。しかし、それは単に大変な作業だと思います。

システムを完全に再構築したり、メモリアロケータを書き直したりできないという制約を考えると、あなた(およびチームの他のメンバー)が制限を理解している限り、ソリューションが最も実用的だと思います。そしてそれが将来的に引き起こす問題。

ここで、特定のアプローチを改善するために、ワークロードを測定して、メモリが割り当てられて解放される頻度を確認することをお勧めします。これにより、再試行間隔を計算するためのより良い方法が得られます。

次に、各反復のタイムアウトを増やして、システム上のそのスレッドの負荷を減らしてみてください。

最後に、スレッドが数回の反復後に進行できない場合は、間違いなくエラー/パニックのケースが発生するはずです。これにより、少なくとも、すべてのスレッドがシステム内の別のスレッドがメモリを解放するのを待機している場合に発生する可能性のある潜在的なライブロックのケースを確認できます。経験的に機能することが示されているものに基づいて反復回数を選択するか、それについて賢くなり、メモリを待機しているスレッドの数を追跡し、それがすべてのスレッドでパニックになるかどうかを追跡できます。

:これは明らかに完全な解決策ではありません。他の投稿者が問題を正しく解決するには、アプリケーション全体のよりグローバルなビューが必要であると述べているため、上記は短期的に機能する実用的な手法です。

于 2008-12-29T11:46:31.887 に答える
1

次に、最も賢明なことは、メモリの静的割り当てを使用することです。これにより、何が起こっているのかがわかります。動的メモリ割り当てはデスクトップ プログラミングの悪い習慣であり、リソースが制限されたマシンには適していません (かなりの時間と労力を費やして、適切に管理および制御されたメモリ管理システムを作成しない限り)。

また、デバイスの OS (OS を実行する傾向があるこのようなハイエンド ARM デバイスが 1 つあると仮定して) がメモリを処理するためにどのような機能を持っているかを確認してください。

于 2008-12-17T08:36:48.460 に答える
1

C++ を使用します。そのため、いくつかの C++ ユーティリティを利用して、作業を楽にすることができます。たとえば、new_handler を使用しないのはなぜでしょうか。

void my_new_handler() {
    // make room for memory, then return, or throw bad_alloc if
    // nothing can be freed.
}

int main() {
    std::set_new_handler(&my_new_handler);

    // every allocation done will ask my_new_handler if there is
    // no memory for use anymore. This answer tells you what the
    // standard allocator function does: 
    // https://stackoverflow.com/questions/377178
}

new_handler では、すべてのアプリケーションにシグナルを送信して、一部のアプリケーションにメモリが必要であることを認識させ、少し待ってから他のアプリケーションにメモリの要求を満たす時間を与えることができます。重要なのは、利用可能なメモリを静かに期待しないで、何かを実行することです。new オペレーターは、まだ十分なメモリーが利用できない場合、ハンドラーを再度呼び出します。そのため、すべてのアプリケーションが必要なメモリーを既に解放しているかどうかを心配する必要はありません。new_handler で必要なメモリのサイズを知る必要がある場合は、演算子 new をオーバーロードすることもできます。それを行う方法については、私の他の回答を参照してください。このように、あなたは1つの中心的な場所を持っていますそれに関係する多くの場所ではなく、メモリの問題を処理するために。

于 2008-12-28T07:28:30.457 に答える
1

あなたの質問に基づいて、ヒープが複数のスレッド間で共有されていると想定しています。

そうでない場合、ループの実行中にヒープから何も解放されないため、上記のコードは機能しません。

ヒープが共有されている場合、上記はおそらく機能します。ただし、共有ヒープがある場合、「new」を呼び出すと、おそらくスピンロック (これと同様のループですが、CAS 命令を使用します) が発生するか、一部のカーネルリソースに基づいてブロックされます。

どちらの場合も、ループによってシステムのスループットが低下します。これは、必要以上のコンテキスト スイッチが発生するか、「メモリが利用可能になりました」イベントへの応答に時間がかかるためです。

「new」および「delete」演算子をオーバーライドすることを検討します。new が失敗すると、別のスレッドがメモリを解放するのを待ってブロック (またはある種のカウンター変数でロックをスピン) し、delete はブロックされた "新しい" スレッドに通知するか、CAS を使用してカウンター変数をインクリメントできます。

これにより、スループットが向上し、効率が少し向上するはずです

于 2008-12-15T02:16:43.813 に答える
1

いくつかのポイント:

  • 組み込みプログラムは、このような状況を回避するために、起動時にすべてのメモリを割り当てるか、静的メモリのみを使用することがよくあります。
  • 定期的にメモリを解放する何かがデバイスで実行されていない限り、ソリューションは効果的ではない可能性があります。
  • 私が持っている Viper には 64MB の RAM が搭載されていますが、32MB 未満ではないと思います。アプリケーションはどれくらいのメモリを使用していますか?
于 2008-12-15T02:21:11.947 に答える
1

これを攻撃する方法はいくつかあります。使用している Windows CE / Windows Mobile のバージョンによって、ツールの説明が少し異なることに注意してください。

いくつかの質問に答えてください:

1. アプリケーションでメモリ リークが発生しているため、メモリ不足の状態になっていますか?

2. アプリケーションが特定の段階でメモリを使いすぎて、このようなメモリ不足の状態になっていませんか?

1 と 2 は、Windows CE AppVerifier ツールを使用して調査できます。このツールは、製品の詳細なメモリ ログ ツールを提供します。製品の設計によっては、他のヒープ ラッピング ツールも同様の情報を提供できます (さらにパフォーマンスが向上する場合もあります)。

http://msdn.microsoft.com/en-us/library/aa446904.aspx

3. このプロセスで頻繁にメモリの割り当てと解放を行っていますか?

OS バージョン 6.0 より前の Windows CE (Windows Mobile 6.x と混同しないでください) には、1 プロセスあたり 32MB の仮想メモリ制限があり、多くの楽しい断片化の問題を引き起こす傾向があります。この場合、物理メモリの空き容量が十分にある場合でも、仮想メモリが不足している可能性があります。通常、カスタム ブロック アロケーターを使用すると、この問題を軽減できます。

4. 非常に大きなメモリ ブロックを割り当てていますか? (> 2MB)

3 に関連して、プロセスの仮想メモリ空​​間を使い果たしている可能性があります。プロセス空間外の共有 VM 空間にメモリを割り当てるには、OS のバージョンに多少依存するトリックがあります。VM が不足しているが、物理 RAM が不足している場合は、これが役立つ可能性があります。

5. 多数の DLL を使用していますか?

3 にも関連します。OS のバージョンによっては、DLL によって利用可能な VM の合計が非常に急速に減少する場合があります。

さらにジャンプオフポイント:

CE メモリ ツールの概要

http://blogs.msdn.com/ce_base/archive/2006/01/11/511883.aspx

ターゲット コントロール ウィンドウの「mi」ツール

http://msdn.microsoft.com/en-us/library/aa450013.aspx

于 2008-12-28T08:00:56.320 に答える
0

確かに、100(ミリ秒?)のスリープでメモリが利用可能になるという合理的な期待があるかどうかによって異なりますか?確かに、試行回数を制限する必要があります。

私には、ここで何かがにおいがしません。うーん...

組み込みシステムは通常、非常に決定論的である必要があります。おそらく、システム全体と、これが失敗する可能性を事前に検討する必要があります。そして、それが実際に実際に起こるのであれば、一生懸命失敗します。

于 2008-12-15T02:06:41.163 に答える