在运行 Solr 查询时,存活的代数不断增加

Posted

技术标签:

【中文标题】在运行 Solr 查询时,存活的代数不断增加【英文标题】:Surviving generations keep increasing while running Solr query 【发布时间】:2020-06-16 13:29:50 【问题描述】:

我正在使用 jSolr (7.4) 测试查询,因为我相信它会导致我的程序出现内存泄漏。但不确定是否确实是内存泄漏,求教!

此方法在我的索引程序运行期间被多次调用(应该能够运行数周/数月而不会出现任何问题)。这就是为什么我在使用 Netbeans Profiler 进行分析的循环中对其进行测试的原因。

如果我只是从给定索引中的所有文档(有 33k)中检索 id:

public class MyIndex 
    // This is used as a cache variable to avoid querying the index everytime the list of documents is needed
 private List<MyDocument> listOfMyDocumentsAlreadyIndexed = null;

 public final List<MyDocument> getListOfMyDocumentsAlreadyIndexed() throws SolrServerException, HttpSolrClient.RemoteSolrException, IOException 

  SolrQuery query = new SolrQuery("*:*");

  query.addField("id");
  query.setRows(Integer.MAX_VALUE); // we want ALL documents in the index not only the first ones

  SolrDocumentList results = this.getSolrClient().
    query(query).getResults();

    /**
    * The following was commented for the test, 
    * so that it can be told where the leak comes from.
    *
    */

    //            listOfMyDocumentsAlreadyIndexed = results.parallelStream()
    //                    .map((doc) ->  // different stuff ...
    //                                  return myDocument;
    //                                  )
    //                    .collect(Collectors.toList());

    return listOfMyDocumentsAlreadyIndexed; 
    /** The number of surviving generations 
     *  keeps increasing whereas if null is 
     * returned then the number of surviving 
     * generations is not increasing anymore
    */

我从分析器中得到这个(经过近 200 次运行,可以为我的程序模拟一年的运行时间):

存活最多的对象是String

在查询索引中的所有文档时,存活代数的增加是否是预期的行为?

如果是这样,这是我在生产服务器上一段时间后得到的“OOM Java 堆空间”错误的根本原因,因为它似乎来自堆栈跟踪:

Exception in thread "Timer-0" java.lang.OutOfMemoryError: Java heap space
at org.noggit.CharArr.resize(CharArr.java:110)
at org.noggit.CharArr.reserve(CharArr.java:116)
at org.apache.solr.common.util.ByteUtils.UTF8toUTF16(ByteUtils.java:68)
at org.apache.solr.common.util.JavaBinCodec.readStr(JavaBinCodec.java:868)
at org.apache.solr.common.util.JavaBinCodec.readStr(JavaBinCodec.java:857)
at org.apache.solr.common.util.JavaBinCodec.readObject(JavaBinCodec.java:266)
at org.apache.solr.common.util.JavaBinCodec.readVal(JavaBinCodec.java:256)
at org.apache.solr.common.util.JavaBinCodec.readSolrDocument(JavaBinCodec.java:541)
at org.apache.solr.common.util.JavaBinCodec.readObject(JavaBinCodec.java:305)
at org.apache.solr.common.util.JavaBinCodec.readVal(JavaBinCodec.java:256)
at org.apache.solr.common.util.JavaBinCodec.readArray(JavaBinCodec.java:747)
at org.apache.solr.common.util.JavaBinCodec.readObject(JavaBinCodec.java:272)
at org.apache.solr.common.util.JavaBinCodec.readVal(JavaBinCodec.java:256)
at org.apache.solr.common.util.JavaBinCodec.readSolrDocumentList(JavaBinCodec.java:555)
at org.apache.solr.common.util.JavaBinCodec.readObject(JavaBinCodec.java:307)
at org.apache.solr.common.util.JavaBinCodec.readVal(JavaBinCodec.java:256)
at org.apache.solr.common.util.JavaBinCodec.readOrderedMap(JavaBinCodec.java:200)
at org.apache.solr.common.util.JavaBinCodec.readObject(JavaBinCodec.java:274)
at org.apache.solr.common.util.JavaBinCodec.readVal(JavaBinCodec.java:256)
at org.apache.solr.common.util.JavaBinCodec.unmarshal(JavaBinCodec.java:178)
at org.apache.solr.client.solrj.impl.BinaryResponseParser.processResponse(BinaryResponseParser.java:50)
at org.apache.solr.client.solrj.impl.HttpSolrClient.executeMethod(HttpSolrClient.java:614)
at org.apache.solr.client.solrj.impl.HttpSolrClient.request(HttpSolrClient.java:255)
at org.apache.solr.client.solrj.impl.HttpSolrClient.request(HttpSolrClient.java:244)
at org.apache.solr.client.solrj.SolrRequest.process(SolrRequest.java:194)
at org.apache.solr.client.solrj.SolrClient.query(SolrClient.java:942)
at org.apache.solr.client.solrj.SolrClient.query(SolrClient.java:957)

将堆空间(“-Xmx”)从 8GB 增加到任何更大的值肯定会解决问题还是只是推迟它?有什么办法可以解决这个问题?

几小时后编辑

如果null从被测方法(getListOfMyDocumentsAlreadyIndexed)返回,那么在整个测试过程中存活的世代数保持稳定:

因此,即使我没有使用此测试的查询结果(因为我只想关注泄漏发生的位置),看起来返回一个实例变量(即使它为空)也不是一个好主意.我会尝试删除它。

稍后再编辑

我注意到,当我分析“定义的类”(“聚焦(仪器)”)时,遥测选项卡中幸存的世代仍在增加,而在分析“所有类”(“常规(采样)”)时它是稳定的.所以我不确定它是否解决了问题:

任何提示都非常感谢:-)

【问题讨论】:

分析器(或jheap 等)应该能够告诉您哪些对象保持活动状态。这会给你一个更好的提示,让你知道剩下的东西,以及它们为什么仍然存在的原因 谢谢@MatsLindh 我从探查器中添加了表格。字符串保持活动状态。我应该从中拿走什么?我检索的“id”存储为字符串,所以它可能是罪魁祸首,但我该怎么办? 然后我们可能会在您从 Solr 检索结果后了解您对结果的处理方式;你以某种方式把它们留在身边吗?您是否尝试过扩展分析器中的引用以查看哪些代码将这些引用保留在您自己的代码中?我不习惯从那个特定的分析器读取分析信息,但我猜 noggit 引用只是对象被创建的地方,不一定是对它的实时引用。 为了回答您的评论,我尝试从我正在测试的方法中返回 null,而不是返回我之前填充的实例变量(但为了测试而删除了哪个填充)。幸存的世代数确实保持稳定。我会试着解释一下。 【参考方案1】:

问题源于以下行:

query.setRows(Integer.MAX_VALUE);

根据这篇文章不应该这样做:

Solr 的 rows 参数可用于返回超过默认值的 10 行。我已经看到用户成功地将 rows 参数设置为 100-200 并且没有看到任何问题。但是,将 rows 参数设置得更高会产生很大的内存后果,应该不惜一切代价避免。

所以问题已经通过在this solr article on pagination 之后按 200 个文档块检索文档来解决:

SolrQuery q = (new SolrQuery(some_query)).setRows(r).setSort(SortClause.asc("id"));
String cursorMark = CursorMarkParams.CURSOR_MARK_START;
boolean done = false;
while (! done) 
  q.set(CursorMarkParams.CURSOR_MARK_PARAM, cursorMark);
  QueryResponse rsp = solrServer.query(q);
  String nextCursorMark = rsp.getNextCursorMark();
  doCustomProcessingOfResults(rsp);
  if (cursorMark.equals(nextCursorMark)) 
    done = true;
  
  cursorMark = nextCursorMark;

请注意:setRows 中的文档不能超过 200 个,否则内存泄漏仍然会发生(例如,500 个确实会发生)。

现在,剖析器提供了更好的关于存活世代的结果,因为它们不再随时间增加。

但是该方法要慢得多。

【讨论】:

以上是关于在运行 Solr 查询时,存活的代数不断增加的主要内容,如果未能解决你的问题,请参考以下文章

Solr高级查询Facet

在 iOS 中通过 HTTP 获取图像时内存分配不断增加

Solr聚合查询

solr 中的布尔子句异常过多

gtkmm 应用程序内存使用量不断增加

如何在选择查询中增加 sql 计数器