10

実際の質問から始める前に、ここで詳細の一部が間違っている可能性があるとだけ言っておきます。もしそうなら、それらだけでなく、あるいは私の質問に答える代わりに、私を逮捕してください。

私の質問は、基本的にDLLと.NETについてです。かなりのメモリを使用しているアプリケーションがあり、特に問題が主にクライアントのコンピュータで発生している場合に、これを正しく測定する方法を見つけようとしています。

私を驚かせたのは、ORMコードが生成されたかなり大きな.NETアセンブリがあることです。

一意のベースアドレスを持つアンマネージ(Win32)DLLを使用していた場合、同じマシン上の複数の同時プロセスがDLLを物理メモリに1回ロードし、すべてのアプリケーションの仮想メモリにマップするだけでした。したがって、物理メモリはこのDLLに1回使用されます。

問題は、.NETアセンブリで何が起こるかです。このDLLにはILが含まれており、この部分はアプリケーション間で共有される可能性がありますが、このILから生じるJITtedコードはどうでしょうか。共有されていますか?そうでない場合、これが実際に問題に寄与しているかどうかを把握するためにどのように測定しますか?(はい、私は知っています、それは貢献するでしょう、しかしそれが最大の問題になるまで私はこれに多くの時間を費やすつもりはありません)。

また、ソリューション内のすべての.NETアセンブリのベースアドレスを確認していないことも知っていますが、.NETアセンブリで確認する必要がありますか?もしそうなら、これらのアドレスを決定する方法について利用可能ないくつかのガイドラインはありますか?

これが大きな問題ではない、またはまったく問題ではないことが判明したとしても、この領域への洞察は大歓迎です。


編集:ちょうどこの質問を見つけました:私の質問に部分的に答える.NETアセンブリとDLLリベースですが、JITtedコードがこれらすべてにどのように影響するかを知りたいです。

その質問と受け入れられた回答から、JITtedコードはヒープに配置されているように見えます。つまり、各プロセスは共有バイナリアセンブリイメージをロードし、独自のメモリスペース内にコードのプライベートJITtedコピーを生成します。

これを測定する方法はありますか?これで大量のコードが生成されることが判明した場合は、生成されたコードをさらに調べて、調整する必要があるかどうかを判断する必要があります。


編集:ここに質問の短いリストを追加しました:

  1. .NETアセンブリのベースアドレスが一意で重複しないようにして、JITtingのためにILコードを取り出すために主に使用されるdllのリベースを回避することに意味はありますか?
  2. これが本当に問題であるかどうかを判断するために、JITtedコードに使用されているメモリの量をどのように測定できますか?

ここでの@BrianRasmussen の回答は、予想どおり、JITtingがJITtedコードのプロセスごとのコピーを生成することを示していますが、アセンブリのリベースは、メモリ使用量の削減に関して実際に効果があります。私は彼が言及しているWinDbg+SoSツールを掘り下げる必要があります。これは私がしばらくの間リストに載せていたものですが、今ではもう延期できないと思います:)


編集:私が主題に関して見つけたいくつかのリンク:

4

3 に答える 3

6

これは質問1)用です

jittedコードは特別なヒープに配置されます。!eeheapWinDbg + SoSのコマンドを使用して、このヒープを検査できます。したがって、すべてのプロセスには、jittedコードの独自のコピーがあります。このコマンドは、コードヒープの合計サイズも表示します。

WinDbgからこの情報を取得するための追加の詳細が必要な場合は、お知らせください。

これは質問2)用です

書籍Expert.NET2.0 IL Assemblyによると、純粋なIL PEファイルの.reloc一部には、CLRスタートアップスタブの修正エントリが1つだけ含まれています。したがって、リベース中にマネージDLLに必要な修正の量はかなり制限されます。

ただし、特定の管理対象プロセスを一覧表示すると、Microsoftが管理対象DLLの大部分(またはすべて)をリベースしたことに気付くでしょう。それがリベースの理由と見なされるべきかどうかはあなた次第です。

于 2009-01-26T09:23:44.523 に答える
3

次の情報が.NETの新しいバージョンやWindowsのバージョンでどれほど正確かわかりません。MSは、.NETの初期の頃から、DLLのロード/共有の問題のいくつかに対処してきた可能性があります。しかし、私は以下の多くがまだ当てはまると信じています。

.NETアセンブリでは、JITがネイティブコードをオンザフライで作成する必要があるため、プロセス間(およびターミナルサーバーセッション間)でページを共有することの多くの利点が失われます。ネイティブコードをバックアップするためのイメージファイルはありません。したがって、各プロセスは、jittedコード用に独自の個別のメモリページを取得します。

これは、DLLのベースが不適切なために発生する問題に似ています。OSがロード時に標準のWin32 DLLで修正を実行する必要がある場合、修正された部分のメモリページを共有できません。

ただし、jittedコードを共有できない場合でも、DLLがメタデータ(およびIL)用にロードされているため、.NET DLLをリベースすることには利点があります。また、修正が必要ない場合は、そのようなものを共有できます。

ngenを使用すると、メモリページを.NETアセンブリと共有するのに役立ちます。しかし、それはそれ自身の一連の問題をもたらします。

詳細については、JasonZanderによるこの古いブログ投稿を参照してください。

http://blogs.msdn.com/jasonz/archive/2003/09/24/53574.aspx

Larry Ostermanには、DLLページの共有と修正の効果に関するまともなブログ記事があります。

http://blogs.msdn.com/larryosterman/archive/2004/07/06/174516.aspx

于 2009-01-26T09:34:50.413 に答える
0

共有アセンブリとdll、およびプロセスメモリスペースについて混乱していると思います。

.NETと標準のWin32DLLはどちらも、それらを使用するさまざまなプロセス間でコードを共有します。.NETの場合、これは同じバージョン署名を持つDLLにのみ当てはまるため、同じdllの2つの異なるバージョンを同時にメモリにロードできます。

問題は、ライブラリ呼び出しによって割り当てられたメモリも共有されることを期待しているように見えることです。まあ、(ほとんど)決して起こりません。ライブラリ内の関数がメモリを割り当てる場合、ORM DLLで多く発生すると思いますが、そのメモリは呼び出し元のプロセスのメモリスペース内に割り当てられ、各プロセスにはデータの一意のインスタンスがあります。

そうです、実際、DLLコードは一度ロードされて呼び出し元間で共有されますが、コード命令(したがって割り当て)は呼び出し元のプロセススペースで個別に行われます。

編集: わかりました。JITが.NETアセンブリでどのように機能するかを見てみましょう。

コードのJITについて話すとき、プロセスは比較的単純です。内部的には、仮想メソッドテーブルと呼ばれる構造があります。この構造には、基本的に、呼び出し中に呼び出される仮想アドレスが含まれています。.NETでは、JITは基本的にそのテーブルを編集して、すべての呼び出しがJITコンパイラにリダイレクトされるように機能します。このように、JITがステップインして実際のマシン命令にコードをコンパイルするメソッドを呼び出すと(したがって、ジャストインタイム)、それが完了すると、JITはVMTに戻り、呼び出された古いエントリを置き換えます。生成された低レベルコードをポイントします。そうすれば、後続のすべての呼び出しはコンパイルされたコードにリダイレクトされます(したがって、コンパイルは1回だけです)。したがって、JITは毎回呼び出されるわけではなく、後続のすべての呼び出しは同じコンパイル済みコードにリダイレクトされます。DLLの場合、プロセスは同じである可能性があります(ただし、完全に同じであることを保証することはできません)。

于 2009-01-26T09:08:39.093 に答える