【问题标题】:Reduce Java Heap Size减少 Java 堆大小
【发布时间】:2017-08-21 01:46:46
【问题描述】:

我正在使用带有Apache OpenNLP 的Java8。我有一项服务可以从一段中提取所有名词。这在我的本地主机服务器上按预期工作。我也在OpenShift 服务器上运行,没有任何问题。但是,它确实会占用大量内存。我需要将我的应用程序部署到AWS Elastic Beanstalk Tomcat Server

一个解决方案是我可以从 AWS Elastic Beanstalk Tomcat Server t1.micro 升级到另一种实例类型。但我的预算很少,如果可能的话,我想避免使用extra fees

当我运行应用程序并尝试执行单词分块时,它收到以下错误:

dispatch failed; nested exception is java.lang.OutOfMemoryError: Java heap space] with root cause
 java.lang.OutOfMemoryError: Java heap space
  at opennlp.tools.ml.model.AbstractModelReader.getParameters(AbstractModelReader.java:148)
  at opennlp.tools.ml.maxent.io.GISModelReader.constructModel(GISModelReader.java:75)
  at opennlp.tools.ml.model.GenericModelReader.constructModel(GenericModelReader.java:59)
  at opennlp.tools.ml.model.AbstractModelReader.getModel(AbstractModelReader.java:87)
  at opennlp.tools.util.model.GenericModelSerializer.create(GenericModelSerializer.java:35)
  at opennlp.tools.util.model.GenericModelSerializer.create(GenericModelSerializer.java:31)
  at opennlp.tools.util.model.BaseModel.finishLoadingArtifacts(BaseModel.java:328)
  at opennlp.tools.util.model.BaseModel.loadModel(BaseModel.java:256)
  at opennlp.tools.util.model.BaseModel.<init>(BaseModel.java:179)
  at opennlp.tools.parser.ParserModel.<init>(ParserModel.java:180)
  at com.jobs.spring.service.lang.LanguageChunkerServiceImpl.init(LanguageChunkerServiceImpl.java:35)
  at com.jobs.spring.service.lang.LanguageChunkerServiceImpl.getNouns(LanguageChunkerServiceImpl.java:46)

问题

有没有办法:

  1. 减少从段落中提取名词时使用的内存量。

  2. 使用除 Apache OpenNLP 之外的其他 api,它不会占用太多内存。

  3. 一种配置AWS Elastic Beanstalk Tomcat Server 以满足需求的方法。

代码示例:

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashSet;
import java.util.Set;

import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import opennlp.tools.cmdline.parser.ParserTool;
import opennlp.tools.parser.Parse;
import opennlp.tools.parser.Parser;
import opennlp.tools.parser.ParserFactory;
import opennlp.tools.parser.ParserModel;
import opennlp.tools.util.InvalidFormatException;

@Component("languageChunkerService")
@Transactional
public class LanguageChunkerServiceImpl implements LanguageChunkerService {

    private Set<String> nouns = null;
    private InputStream modelInParse = null;
    private ParserModel model = null;
    private Parser parser = null;

    public void init() throws InvalidFormatException, IOException {
        ClassLoader classLoader = getClass().getClassLoader();
        File file = new File(classLoader.getResource("en-parser-chunking.bin").getFile());
        modelInParse = new FileInputStream(file.getAbsolutePath());

        // load chunking model
        model = new ParserModel(modelInParse); // line 35
        // create parse tree
        parser = ParserFactory.create(model);
    }

    @Override
    public Set<String> getNouns(String sentenceToExtract) {
        Set<String> extractedNouns = new HashSet<String>();
        nouns = new HashSet<>();
        try {
            if (parser == null) {
                init();
            }

            Parse topParses[] = ParserTool.parseLine(sentenceToExtract, parser, 1);

            // call subroutine to extract noun phrases
            for (Parse p : topParses) {
                getNounPhrases(p);
            }

            // print noun phrases
            for (String s : nouns) {
                String word = s.replaceAll("[^a-zA-Z ]", "").toLowerCase();// .split("\\s+");
                //System.out.println(word);
                extractedNouns.add(word);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (modelInParse != null) {
                try {
                    modelInParse.close();
                } catch (IOException e) {
                }
            }
        }
        return extractedNouns;
    }

    // recursively loop through tree, extracting noun phrases
    private void getNounPhrases(Parse p) {
        if (p.getType().equals("NN")) { // NP=noun phrase
            // System.out.println(p.getCoveredText()+" "+p.getType());
            nouns.add(p.getCoveredText());
        }
        for (Parse child : p.getChildren())
            getNounPhrases(child);
    }
}

更新

Tomcat8 配置:

【问题讨论】:

  • 在您的计算机上运行分析器以查看程序在正常运行期间需要什么样的内存量,然后您可以确定需要增加多少预算。即使你可以减少一些小块,你仍然会在内存边缘运行,这会使其非常不稳定。
  • 好主意。会做。另外,你认为有办法减少en-parser-chunking.bin吗?即它可能正在加载一些我可能不需要的功能。我知道 OpenNLP 做了很多不同的语言解析,我只需要提取名词。
  • 我对 OpenNLP 不熟悉,但由于它可能占用了您的大部分内存,您可能需要非常仔细地阅读文档。
  • 也许我应该使用不同的工具包。有没有人有这方面的经验,可以推荐一个:en.wikipedia.org/wiki/…
  • 顺便问一下,你有多少内存(1GB?)?你给 Tomcat 多少堆,系统上还有其他东西在运行吗?

标签: java amazon-web-services amazon-elastic-beanstalk opennlp


【解决方案1】:

首先,您应该尝试优化您的代码。首先在使用 replaceAll 之前使用正则表达式和预编译语句,因为 replaceAll 替换了内存中的内容。 (https://eyalsch.wordpress.com/2009/05/21/regex/)

其次,您不应该将解析后的句子存储在数组中。第三个提示:您应该尝试使用 bytebuffer 将内存分配给您的数组。另一个最多可能影响您的提示,您应该使用 BufferedReader 来读取分块文件。 (out of memory error, java heap space)

在此之后,您应该已经看到内存使用量减少了。如果这些提示对您没有帮助,请提供内存转储/分配图。

另一个提示:HashSet 占用的内存是无序列表的 5.5 倍。 (Performance and Memory allocation comparision between List and Set)

【讨论】:

  • 我看不到 anything 这个答案。使用Pattern 而不是replaceAll 可能会影响速度,但不会影响内存。使用ByteBuffer 而不是数组,也没有任何意义,除非他正在使用需要缓冲区的代码。 BufferedReader 与此处无关,因为它也会影响速度而不是内存使用。最后,确实会影响内存的一件事 (HashSet) 也可能以显着的方式影响速度。
  • “没那么多”。如果他的预算不足并且内存不足,他必须优化所有内容,即使只占用几 kb 的内存。
  • 嗨,伊曼纽尔,感谢您提供的这些提示,我将实施它们以尝试提高效率。从堆栈跟踪中,您可以看到line 35 是代码与OutOfMemoryError 一起失败的地方,因此它在OpenNLP api 的初始化中。所以我同意,您的上述建议将改进代码,我认为它不会解决OutOfMemoryError。 (附:我不知道为什么有人给你投了反对票,我觉得你的建议很有用。)
  • @Richard 你只是认为它们很有用。正如我在评论中解释的那样,它们几乎是猜测,并且与“使用 short 而不是 int 为每个变量节省 2 个字节”一样有用。由于您在本地主机上运行它,因此您可以使用分析器来检查内存使用情况,而不是尝试几乎没有机会帮助您的事情。
  • 还是微优化,不,不适合这种情况。当您谈论嵌入式环境时,您可以开始计划节省字节。如果您尝试做 NLP 或其他需要资源的事情,您将获得这些资源。我们在这里谈论的是t1.micro 实例,这是你能得到的最小的东西。如果你付不起更多的钱,那么你真的应该考虑找一份工作,而不是试图优化内存。
猜你喜欢
  • 2016-04-25
  • 2020-04-24
  • 2016-11-12
  • 2012-01-23
  • 1970-01-01
  • 2012-04-20
  • 2014-01-08
  • 2016-04-05
相关资源
最近更新 更多