0

XP マシンで実行していた ac# アプリケーションがあります。最近、Windows 7.0 マシンに切り替えました。

デバッガーを使用すると、次のエラー メッセージが表示されます:「System.StackOverflowException」。まだXPマシンを持っていますが、これには問題はありません。

再帰アルゴリズムの途中でオーバーフローしています。この問題に詳しい人はいますか?それはこれに関係するOSですか、それともマシン自体ですか?

助けてくれて本当にありがとうございます、

マイケル

4

3 に答える 3

1

基本ケースに到達する前に XP で再帰がどの程度深いか、および Win7 でエラーが発生する場所を知ることは役に立ちます。

理論的には、Windows 7 プロセスには、WinXP プロセスよりも多くの利用可能なスタック スペースが必要です。少なくとも、それらは同じでなければなりません。ただし、ここには他の要因が関係しています。このブログ投稿をチェックしてください: http://blogs.technet.com/b/markrussinovich/archive/2009/07/08/3261309.aspx

つまり、制限要因は通常、「常駐の使用可能なメモリ」です。これは、そこに保持する必要があり、ページ ファイルにスワップできないデータに使用できる物理 RAM (ページ ファイル領域ではない) です。多くのものを平均的なコンピューターに「常駐」させておく必要があり、ページ ファイルにスワップ アウトすることはできません。最も重要なことは、「カーネル モード」 (コア システムへの直接アクセスが必要) で実行する必要があるものはすべて、その時点でそのプロセスのアクティブなスレッドがない場合でも、ページ フォールトを回避するために RAM に保持する必要があるということです。

Windows 7 には、これらの「カーネル モード」プロセスがさらに多くあります。たとえば、Windows Aero (WinXP の一部ではありません) はグラフィック カードを使用してデスクトップのレンダリングを高速化するため、カーネル モードで実行する必要があります。Windows 7 カーネル自体は、追加のセキュリティと追加の組み込みハードウェア サポートが含まれているため、より大きくなっています。Windows 7 には、WinXP にはなかったカーネル モードで実行される追加のバックグラウンド プロセスなどもあります。

したがって、他のすべての条件 (RAM を含む) が等しい場合、Windows 7 マシンでは、実際には、再帰アルゴリズムにコミットするために使用できる常駐メモリが少なくなります。つまり、アルゴリズムは、呼び出しの前に基本ケースに到達するのに十分なほど深く再帰できません。新しい呼び出しに必要な「コミット」を満たすのに十分な常駐メモリが Windows にないため、StackOverflowException がトリガーされます。

さらに、Windows 7 ではメモリ内の配置が異なります。Windows の古いバージョン (XP 以前) では、新しいプロセスごとにメモリ領域がほぼ連続して予約されていました。N+1 番目のプロセス (またはスレッド) には、N 番目のプロセス/スレッド用に予約された最後のブロックの 1 ブロック後のメモリ アドレスが与えられます。Windows Vista 以降、メモリはより「ランダム」な方法で割り当てられました。Windows は、他の予約済みブロックに隣接している場合と隣接していない場合があるメモリ内の場所を選択します (他の予約済みブロックの一部ではないことが保証されているだけです)。これは、マルウェアを混乱させ、他のプロセスのメモリ内でマルウェアが正常にスヌーピングするのを防ぐように設計されたセキュリティ機能です。ただし、スペース効率の低い割り当てスキームは、OS が新しいスレッドごとに割り当てる連続した RAM の 1MB ブロックをすぐに使い果たしてしまうことを意味します。その時点で、ギャップの割り当てが開始されます。そのため、Windows 7 マシンの特定のメモリ使用フットプリントによっては、再帰関数のスレッドが通常の 1MB のスタック スペースを要求し、実際には 128K の連続スペースしかない OS によってポインターが与えられる場合があります。プログラムは、予約したと思っていたすべてのスペースを実際にコミットできなくなるまで、違いを見分けることができません。これにより、Windows が毎回スレッド用に予約する正確なメモリ領域の非決定論的な違いのために、一度は機能しても次は失敗する Heisenbugs が生成される可能性があります。実際には128Kの連続スペースしかないOSからポインターが与えられます。プログラムは、予約したと思っていたすべてのスペースを実際にコミットできなくなるまで、違いを見分けることができません。これにより、Windows が毎回スレッド用に予約する正確なメモリ領域の非決定論的な違いのために、一度は機能しても次は失敗する Heisenbugs が生成される可能性があります。実際には128Kの連続スペースしかないOSからポインターが与えられます。プログラムは、予約したと思っていたすべてのスペースを実際にコミットできなくなるまで、違いを見分けることができません。これにより、Windows が毎回スレッド用に予約する正確なメモリ領域の非決定論的な違いのために、一度は機能しても次は失敗する Heisenbugs が生成される可能性があります。

これらすべてに対する答えは、「より多くの RAM」です。コア カーネル モード プロセスで必要とされる量は比較的静的であるため、追加できる RAM の各 GB は、ユーザー プログラム プロセスおよびスレッドのみに使用できる GB になります。

于 2012-07-17T17:14:48.830 に答える
0

これはPCの物理RAMとは何の関係もないと思います。XPで見られなかった理由は、Windows 7の.Netのバージョンが(わずかに?)異なるためだと思います。

明らかに、再帰の深さを何らかの方法で制限する必要があります(または非再帰ループに置き換える必要があります)。

ただし、.Netスタックを構成できる可能性があります。これらのリンクを見てください:

于 2012-07-17T16:09:34.050 に答える
0

再帰はどのくらい再帰的ですか?

約 10 かそこらより深いものは危険です。

スタックを使い果たし、それがバグではないと確信している場合は、独自のスタックを管理できます...

例えば:

void Process(SomeType foo)
{
    DoWork(foo); //work on foo
    foreach(var child in foo.Children)
    {
        Process(child);
    }
}

なる可能性があります

void Process(SomeType foo)
{
    Stack<SomeType> bar=new Stack<SomeType>();
    bar.Push(foo);
    while(bar.Any())
    {
        var item=bar.Pop();
        DoWork(item);//work on item
        foreach(var child in item.Children)
        {
            bar.Push(child);
        }
    }
}

これにより、CLR コール スタックの問題が解消されます。

もちろん、これは無制限の再帰を修正しません。

于 2012-07-17T16:04:52.933 に答える