4

我的应用程序有一个嵌套 for 循环的内存问题,我不知道如何改进它。我尝试过使用 linq,但我想在内部它是相同的,因为内存泄漏仍然存在。

编辑:正如我被要求的那样,我将提供有关我的问题的更多信息。

我已经在 Lucene 文档存储中索引了我的所有客户(大约 400.000)。每个客户可以出现在多个代理机构中,其中一些客户可以在 200-300 家代理机构中退出。

我需要从“全球”客户索引中检索我的所有客户,并为每个代理商建立一个单独的索引,只包含它可以看到的客户。有一些业务规则和安全规则需要应用到每个代理索引,所以现在,我无法为我的所有代理维护一个客户索引。

我的过程如下所示:

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

关键可能是您如何初始化customerscustomer.Agencies. 如果可以,不要返回类型 of ,而是List返回类型IEnumerable<Customer>and IEnumerable<Agency>。这可能允许延迟执行发生,这应该消耗更少的内存,但可能会使操作花费更长的时间。

另一种选择是分批运行代码,因此请使用上面的代码,但要List<Customer> customers分批填充,例如一次 10,000 个。

于 2012-04-16T16:25:29.783 回答
2

首先,您应该重用您传递给的实例,Document以最大程度地减少内存使用并减轻垃圾收集器的压力。FieldIndexWriter.AddDocument()

• 重用 Document 和 Field 实例 从 Lucene 2.3 开始,有新的 setValue(...) 方法允许您更改 Field 的值。这允许您在许多添加的文档中重用单个 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 回答
1

正如@RedFilter 所说,尝试将 IEnumerable 与 yield 语句一起使用。

这可能会有所帮助:

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

如果您的意思是要减少内存使用量,那么基本答案就是将其分解。

因此,将一个机构的所有客户都放入 CustomersForAgency 集合中,然后进行处理。

清除或让 CustomersForAgency 集合超出范围,将使所有这些客户和(可选该机构)超出范围,从而允许 .net 重用内存。

这当然是假设大部分内存分配是给客户的,而不是用于处理的其他持久实例,你简化了。

于 2012-04-16T16:41:57.280 回答