4

ネストされた for ループを使用するアプリケーションでメモリの問題が発生しており、それを改善する方法がわかりません。私はlinqを使ってみましたが、メモリリークがまだ残っているので、内部的には同じだと思います。

編集:私が要求されたように、私は私の問題についてより多くの情報を提供します.

すべての顧客 (約 400.000) を Lucene ドキュメント ストアにインデックス化しました。各顧客は、200 ~ 300 の代理店にいるよりも、複数の代理店に存在し、そのうちのいくつかを終了することができます。

「グローバル」顧客インデックスからすべての顧客を取得し、代理店ごとに個別のインデックスを作成して、表示できる顧客のみを含める必要があります。各エージェンシー インデックスに適用する必要があるビジネス ルールとセキュリティ ルールがいくつかあるため、現在、すべてのエージェンシーに対して 1 つの顧客インデックスを維持する余裕はありません。

私のプロセスは次のようになります。

int numDocuments = 400000;

// Get a Lucene Index Searcher from an Index Factory
IndexSearcher searcher = SearcherFactory.Instance.GetSearcher(Enums.CUSTOMER);

// Builds a query that gets everything in the index
Query query = QueryHelper.GetEverythingQuery();
Filter filter = new CachingWrapperFilter(new QueryWrapperFilter(query));

// Sorts by Agency Id
SortField sortField = new SortField("AgencyId, SortField.LONG);
Sort sort = new Sort(sortField);

TopDocs documents = searcher.Search(query, filter, numDocuments, sort);

for (int i = 0; i < numDocuments; i++)
{
     Document document = searcher.Doc(documents.scoreDocs[i].doc);

     // Builds a customer object from the lucene document
     Customer customer = new Customer(document);

     // If this nested loop is removed, the memory doesn't grow
     foreach(Agency agency in customer.Agencies)
     {
          // Gets a writer from a factory for the agency id.
          IndexWriter writer = WriterFactory.Instance.GetWriter(agency.Id);

          // Builds an agency-specific document from the customer
          Document customerDocument = customer.GetAgencyDocument(agency.Id);

          // Adds the document to the agency's lucene index
          writer.AddDocument(customerDocument);
     }
}

編集:解決策

問題は、内側のループで「ドキュメント」オブジェクトのインスタンスを再利用していなかったため、サービスのメモリ使用量が著しく増加したことです。完全なプロセスで Document の単一のインスタンスを再利用するだけで、私の問題は解決しました。

みんな、ありがとう。

4

6 に答える 6

4

ここで起こっていると私が信じていることは次のとおりです。

ループ内でのオブジェクト作成が多すぎます。可能な限り、ループ内で new() キーワードを使用しないでください。ループ全体で再利用可能なオブジェクトを初期化し、データを渡して処理します。ガベージ コレクションが深刻な問題になり、ガベージ コレクタが追いつかなくなり、コレクションが遅延する可能性があるため、多くのループ内で新しいオブジェクトを作成しないでください。

これが真である場合に最初に試すことができることは、すべての X ループでガベージ コレクションを強制し、保留中のファイナライザーを待機することです。これによりメモリがダウンする場合は、これが問題であることがわかります。そして、それを解決するのは簡単です: ループの反復ごとに新しいインスタンスを作成しないことです。

于 2012-04-16T16:46:59.870 に答える
2

最初に、渡すインスタンスDocumentとインスタンスを再利用して、メモリ使用量を最小限に抑え、ガベージ コレクターへの負担を軽減する必要があります。FieldIndexWriter.AddDocument()

• Document および Field インスタンスの再利用 Lucene 2.3 では、Field の値を変更できる新しい setValue(...) メソッドがあります。これにより、追加された多くのドキュメントで単一の Field インスタンスを再利用できるため、GC コストを大幅に節約できます。単一の Document インスタンスを作成し、それに複数の Field インスタンスを追加するのが最善ですが、これらの Field インスタンスを保持し、追加されたドキュメントごとに値を変更して再利用します。たとえば、idField、bodyField、nameField、storedField1 などがあるとします。ドキュメントが追加された後、フィールドの値 (idField.setValue(...) など) を直接変更してから、Document インスタンスを再度追加します。 .

Document 内で単一の Field インスタンスを再利用することはできないことに注意してください。また、Field を含む Document がインデックスに追加されるまで、Field の値を変更しないでください。

http://wiki.apache.org/lucene-java/ImproveIndexingSpeed

于 2012-04-17T13:22:05.890 に答える
2

鍵は、初期化の方法customerscustomer.Agencies. の型を返すのではなく、可能であればList、戻り型IEnumerable<Customer>を および にしIEnumerable<Agency>ます。これにより、遅延実行が発生する可能性があり、メモリの消費は少なくなりますが、操作に時間がかかる場合があります。

別のオプションは、コードをバッチで実行することです。そのため、上記のコードを使用しますがList<Customer> customers、一度に 10,000 のバッチで入力します。

于 2012-04-16T16:25:29.783 に答える
1

@RedFilter が言ったように、yield ステートメントと一緒に IEnumerable を使用してみてください。

これは役立つかもしれません:

http://csharpindepth.com/Articles/Chapter11/StreamingAndIterators.aspx

http://www.alteridem.net/2007/08/22/the-yield-statement-in-c/

于 2012-04-16T16:34:15.017 に答える
1

既にメモリにロードされているメモリ内のリストをループしても、リストが使用しているメモリの量は変更されません。

メモリ使用量の原因となっているのは、リスト内のアイテムに対して行っていることである必要があります。

達成しようとしていることを確認し、すべてのデータを同時にメモリに保持しないようにプログラムを再設計する必要があります。

于 2012-04-16T16:34:45.383 に答える
1

メモリ使用量を減らしたいということであれば、基本的な答えはそれを分割することです。

したがって、1 つの代理店のすべての顧客を CustomersForAgency コレクションに取得し、それを処理します。

CustomersForAgency コレクションをクリアまたは範囲外にすると、すべての顧客と (オプションでその代理店) が範囲外になり、.net がメモリを再利用できるようになります。

もちろん、メモリ割り当ての大部分が顧客用であり、処理に使用される他の永続的なインスタンスではないことを前提としています。

于 2012-04-16T16:41:57.280 に答える