【问题标题】:Java OutOfMemoryError caused by ReplaceAll regex由 ReplaceAll 正则表达式引起的 Java OutOfMemoryError
【发布时间】:2017-05-27 10:40:20
【问题描述】:

我正在对相当大规模(1000 个项目)进行分析,我正在从源代码中提取测试框架的使用情况(例如,检测 assertEquals 以测量断言密度)。为此,我不想考虑任何已被注释掉的陈述。为了做到这一点,我有以下方法:

public static CharSequence replaceAllRegexInFile(CharSequence input, String regex) {
    if (regex == null || input == null) {
        return input;
    }
    Pattern pattern = Pattern.compile(regex);
    return pattern.matcher(input).replaceAll("");
}

我正在使用以下正则表达式运行此方法来替换 Java cmets:

(\/\*([\S\s]+?)\*\/|(?s)/\*.*?\*/)". 

我很清楚 replaceAll 在聚合和返回最终结果的同时分配了大量的中间结果。当然,我可以使用替换,但这不允许我使用正则表达式来替换 cmets。

我明白为什么会抛出堆空间错误,尤其是因为我正在整个机器上同时流式传输所有文件和所有项目。当然,这会占用大量资源,但我无法找到解决我的问题的替代解决方案,因为绝对需要正则表达式替换。

任何建议将不胜感激。

您可以在下面找到堆栈跟踪:

Exception in thread "main" java.lang.OutOfMemoryError
  at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
  at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
  at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
  at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
  at java.util.concurrent.ForkJoinTask.getThrowableException(ForkJoinTask.java:598)
  at java.util.concurrent.ForkJoinTask.reportException(ForkJoinTask.java:677)
  at java.util.concurrent.ForkJoinTask.invoke(ForkJoinTask.java:735)
  at java.util.stream.ReduceOps$ReduceOp.evaluateParallel(ReduceOps.java:714)
  at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:233)
  at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:499)
  at AnalysisRunner.startAnalysis(AnalysisRunner.java:33)
  at AnalysisRunner.main(AnalysisRunner.java:26) 
Caused by: java.lang.OutOfMemoryError: Java heap space
  at java.util.Arrays.copyOf(Arrays.java:3332)
  at java.lang.AbstractStringBuilder.ensureCapacityInternal(AbstractStringBuilder.java:124)
  at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:541)
  at java.lang.StringBuffer.append(StringBuffer.java:350)
  at java.util.regex.Matcher.appendReplacement(Matcher.java:888)
  at java.util.regex.Matcher.replaceAll(Matcher.java:955)
  at Business.RegexService.replaceAllRegexInFile(RegexService.java:64)
  at Business.FrameWorkDetectionService.extractAllResultsForFile(FrameWorkDetectionService.java:58)
  at Business.FrameWorkDetectionService.lambda$extractFrameworkDependencies$0(FrameWorkDetectionService.java:39)
  at Business.FrameWorkDetectionService$$Lambda$19/1175339539.apply(Unknown Source)
  at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193)
  at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1374)
  at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
  at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
  at java.util.stream.ReduceOps$ReduceTask.doLeaf(ReduceOps.java:747)
  at java.util.stream.ReduceOps$ReduceTask.doLeaf(ReduceOps.java:721)
  at java.util.stream.AbstractTask.compute(AbstractTask.java:316)
  at java.util.concurrent.CountedCompleter.exec(CountedCompleter.java:731)
  at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:289)
  at java.util.concurrent.ForkJoinPool.helpComplete(ForkJoinPool.java:1870)
  at java.util.concurrent.ForkJoinPool.awaitJoin(ForkJoinPool.java:2045)
  at java.util.concurrent.ForkJoinTask.doInvoke(ForkJoinTask.java:404)
  at java.util.concurrent.ForkJoinTask.invoke(ForkJoinTask.java:734)
  at java.util.stream.ReduceOps$ReduceOp.evaluateParallel(ReduceOps.java:714)
  at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:233)
  at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:499)
  at Business.FrameWorkDetectionService.extractFrameworkDependencies(FrameWorkDetectionService.java:39)
  at Business.FrameWorkDetectionService.detectFrameworks(FrameWorkDetectionService.java:26)
  at Business.FrameworkService.projectResults(FrameworkService.java:59)
  at AnalysisRunner$$Lambda$13/1712669532.apply(Unknown Source)
  at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193)
  at java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:175)

是否有替代解决方案不会分配这么多堆空间,但仍允许我同时替换大量文件中的所有 cmets?

非常感谢任何帮助!

【问题讨论】:

  • 当您使用低效的正则表达式模式和交替时,Java 中的一个常见问题。使用来自Regex to match a C-style multiline comment 的正则表达式。顺便说一句,在一个模式中重复模式是没有意义的:/\*([\S\s]+?)\*/ 匹配与 (?s)/\*.*?\*/ 相同的文本

标签: java regex replace out-of-memory heap-memory


【解决方案1】:

可能 Java 没有为您的应用分配足够的内存。您可以尝试使用-Xmx-Xms 标志来增加分配的初始内存和最大内存,例如:

java -Xmx2048m -Xms512m yourApp

调整这些参数,使应用程序不会崩溃。

运行java -X可以查看所有可能的参数

如果更改分配的内存没有帮助,请尝试在应用程序运行时使用jmap -heap:format=b <process-id> 创建堆转储。然后在某种内存分析器中打开它(例如http://www.eclipse.org/mat/)。也许代码的其他部分存在一些内存泄漏。这将检测到它们。

【讨论】:

    【解决方案2】:

    我认为这更像是一个重要的评论而不是一个答案,但作为答案发布,因为它的格式更丰富。

    您的正则表达式性能不佳,可能会导致如此大的内存错误。例如,这是您的正则表达式的图表:

    我从中了解到的是,您只是想摆脱块 cmets。所以,你的正则表达式存在不同的问题,最重要的是你有不同的模式来做完全相同的事情,因此你应该只使用其中一个,这样你就可以摆脱捕获组和交替而只使用他们喜欢:

    \/\*[\S\s]+?\*\/".   <--- I removed the capturing group to make it more efficient, since you didn't need it
    or 
    (?s)/\*.*?\*/".
    

    如您所见,正则表达式模式效率更高,它没有 2 个模式,也没有 2 个捕获组,也没有非常昂贵的交替。

    无论如何,如果您不需要 java,那么我认为有更好的工具来执行这些替换,例如 sed-i 标志(就地替换)

    但是,如果您仍然想使用您的正则表达式,那么您可以通过删除不需要的捕获组并将捕获组转换为非捕获组来改进,如下所示:

    (?:\/\*[\S\s]+?\*\/|(?s)/\*.*?\*/)". 
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-05-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-01-05
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多