Java 由 ReplaceAll 正则表达式引起的 OutOfMemoryError
Java OutOfMemoryError caused by ReplaceAll regex
我正在进行相当大规模的分析(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评论:
(\/\*([\S\s]+?)\*\/|(?s)/\*.*?\*/)".
我很清楚 replaceAll 在聚合和返回最终结果的同时分配了很多中间结果。当然,我可以求助于使用替换,但这不允许我使用正则表达式来替换注释。
我明白为什么会抛出堆space 错误,特别是因为我在我的整个机器上同时流式传输所有文件和所有项目。当然,这会占用大量资源,但我无法为我的问题找到替代解决方案,因为肯定需要替换正则表达式。
如有任何建议,我们将不胜感激。
您可以在下面找到堆栈跟踪:
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[=13=](FrameWorkDetectionService.java:39)
at Business.FrameWorkDetectionService$$Lambda/1175339539.apply(Unknown Source)
at java.util.stream.ReferencePipeline.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/1712669532.apply(Unknown Source)
at java.util.stream.ReferencePipeline.accept(ReferencePipeline.java:193)
at java.util.stream.ReferencePipeline.accept(ReferencePipeline.java:175)
有没有不会分配这么多堆的替代解决方案 space 仍然允许我同时替换很多文件中的所有注释?
非常感谢任何帮助!
可能 Java 没有为您的应用程序分配足够的内存。您可以尝试使用 -Xmx
和 -Xms
标志来增加分配的初始内存和最大内存,例如:
java -Xmx2048m -Xms512m yourApp
调整这些参数,使应用程序不会崩溃。
您可以通过运行 java -X
查看所有可能的参数
如果更改分配的内存没有帮助,请尝试在您的应用程序处于 运行 时使用 jmap -heap:format=b <process-id>
创建堆转储。然后在某种内存分析器中打开它(例如 http://www.eclipse.org/mat/)。也许代码的其他部分存在一些内存泄漏。这将检测到它们。
我认为这更像是一个大评论而不是一个答案,但作为答案发布是因为它的格式更丰富。
您的正则表达式性能不佳,可能会导致如此大的内存错误。例如,这是您的正则表达式的图表:
据我了解,您只是想去掉块注释。所以,你的正则表达式有不同的问题,最重要的是你有不同的模式来做完全相同的事情,因此你应该只使用其中一个,这样你就可以摆脱捕获组和交替,只使用他们喜欢:
\/\*[\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
标志(就地替换)
但是,如果您仍想使用正则表达式,则可以通过删除 un-needed 捕获组并将捕获组转换为 non-capturing 组来改进,如下所示:
(?:\/\*[\S\s]+?\*\/|(?s)/\*.*?\*/)".
我正在进行相当大规模的分析(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评论:
(\/\*([\S\s]+?)\*\/|(?s)/\*.*?\*/)".
我很清楚 replaceAll 在聚合和返回最终结果的同时分配了很多中间结果。当然,我可以求助于使用替换,但这不允许我使用正则表达式来替换注释。
我明白为什么会抛出堆space 错误,特别是因为我在我的整个机器上同时流式传输所有文件和所有项目。当然,这会占用大量资源,但我无法为我的问题找到替代解决方案,因为肯定需要替换正则表达式。
如有任何建议,我们将不胜感激。
您可以在下面找到堆栈跟踪:
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[=13=](FrameWorkDetectionService.java:39)
at Business.FrameWorkDetectionService$$Lambda/1175339539.apply(Unknown Source)
at java.util.stream.ReferencePipeline.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/1712669532.apply(Unknown Source)
at java.util.stream.ReferencePipeline.accept(ReferencePipeline.java:193)
at java.util.stream.ReferencePipeline.accept(ReferencePipeline.java:175)
有没有不会分配这么多堆的替代解决方案 space 仍然允许我同时替换很多文件中的所有注释?
非常感谢任何帮助!
可能 Java 没有为您的应用程序分配足够的内存。您可以尝试使用 -Xmx
和 -Xms
标志来增加分配的初始内存和最大内存,例如:
java -Xmx2048m -Xms512m yourApp
调整这些参数,使应用程序不会崩溃。
您可以通过运行 java -X
如果更改分配的内存没有帮助,请尝试在您的应用程序处于 运行 时使用 jmap -heap:format=b <process-id>
创建堆转储。然后在某种内存分析器中打开它(例如 http://www.eclipse.org/mat/)。也许代码的其他部分存在一些内存泄漏。这将检测到它们。
我认为这更像是一个大评论而不是一个答案,但作为答案发布是因为它的格式更丰富。
您的正则表达式性能不佳,可能会导致如此大的内存错误。例如,这是您的正则表达式的图表:
据我了解,您只是想去掉块注释。所以,你的正则表达式有不同的问题,最重要的是你有不同的模式来做完全相同的事情,因此你应该只使用其中一个,这样你就可以摆脱捕获组和交替,只使用他们喜欢:
\/\*[\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
标志(就地替换)
但是,如果您仍想使用正则表达式,则可以通过删除 un-needed 捕获组并将捕获组转换为 non-capturing 组来改进,如下所示:
(?:\/\*[\S\s]+?\*\/|(?s)/\*.*?\*/)".