2012-04-16 130 views
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的单个实例即可解决我的问题。

谢谢大家。

+3

这真的取决于你在做什么,这个代码本身是不渗漏。 – 2012-04-16 16:17:53

+4

“使用大量内存”和“内存泄漏”之间存在差异。 – David 2012-04-16 16:18:24

+2

你简化了它太多。 – 2012-04-16 16:19:16

回答

2

首先,你应该重新使用您传递给IndexWriter.AddDocument()以减少内存使用和减轻垃圾收集器压力的DocumentField实例。

•重新使用文档和字段实例从Lucene 2.3开始,有新的 setValue(...)方法允许您更改字段的值。 这允许您跨越许多添加的 文档重复使用单个字段实例,这可以节省大量的GC成本。最好创建一个 单个Document实例,然后向其添加多个Field实例,但 会保留这些Field实例,并通过为每个添加的文档更改它们的 值来重新使用它们。例如,您可能有一个idField, bodyField,nameField,storedField1等。在文档添加完成后,您可以直接更改字段值(idField.setValue(...), 等),然后重新添加您的文档实例。

请注意,您不能在文档中重复使用单个字段实例 ,并且在包含该字段的文档 已添加到索引之前,您不应该更改字段值。

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

2

的关键可能是你如何初始化customerscustomer.Agencies。如果可以,而不是返回List的类型,则返回类型为IEnumerable<Customer>IEnumerable<Agency>。这可能会延迟执行,这会消耗更少的内存,但可能会使操作花费更长时间。

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

+0

谢谢,我会试试这个。 – dandel 2012-04-16 16:27:42

+0

我不会去IEnumerable与400000客户 – 2012-04-16 16:28:17

+0

@memetolsen我想这取决于数据源 - 与SQL它肯定可能是一个问题。 – RedFilter 2012-04-16 16:32:35

1

通过在内存中的列表是媒体链接加载到内存中循环,你不改变这个列表是使用的内存量。

它必须是你正在做在列表中的项目引起该内存使用的东西。

你需要看看你正在努力实现和重新设计你的程序没有内存中的所有数据在同一时间的东西。

1

如果你的意思是你想减少内存使用量,那么基本的答案就是分解它。

因此,将一个代理商的所有客户转换为CustomersForAgency集合,然后处理。

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

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

+0

这是我之前尝试过的,但客户可以出现在多个代理机构中。这意味着,如果我遍历代理机构,每个代理机构都会从数据源中为客户查询一次。 – dandel 2012-04-16 16:45:06

+0

这是打破工作的一种方式,一定会更多。目前你正在采取暴力方法,优化需要对需求和实施有深入的了解。那是你。有两种方法可以开始你所知道的,攻击它,看看会发生什么,或侧面思考导致一个狡猾的计划。后者可能是可能的,但如果你生活在这个代码中,你需要“睡在它上面”,做一些其他的事情。狡猾的计划往往是因为没有考虑太多而产生的。 – 2012-04-16 21:24:26

4

我相信在这里发生的事情是:

你有环内过多的对象创建。如果可能的话,不要在循环中使用new()关键字。初始化可在循环中重用的对象并传递它们的数据以进行处理。不要在很多循环中构建新的对象,因为垃圾收集将成为一个严重的问题,垃圾收集器可能无法跟上你,并推迟收集。

你可以做的第一件事情就是如果这是真的,试着强制垃圾收集每个X循环,并等待挂起终结器。如果这导致记忆力下降,您知道这是问题所在。解决这个问题很简单:只是不要在每个循环迭代中创建新的实例。

+0

谢谢!!!这是问题所在。我相信我能解决它:) – dandel 2012-04-16 16:54:23