From 55d99cde490111cad4874bc05eec03397ca5bc0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Kubitz?= Date: Mon, 30 Sep 2024 10:31:02 +0200 Subject: [PATCH] [performance] Avoid reading SourceFile twice During compile parsing happens in two stages: 1. diet parse (any blocks like method bodies are skipped) 2. parse bodies Both phases did read the source .java file from file system. With this change the file contents is kept in CompilationResult.contentRef until no longer needed. It is cached in a SoftReference to avoid OutOfMemoryError. https://github.com/eclipse-jdt/eclipse.jdt.core/issues/2691 --- .../internal/compiler/CompilationResult.java | 54 ++++++++++++++----- .../jdt/internal/compiler/Compiler.java | 1 + .../ast/CompilationUnitDeclaration.java | 1 + .../jdt/internal/compiler/parser/Parser.java | 3 +- 4 files changed, 45 insertions(+), 14 deletions(-) diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/CompilationResult.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/CompilationResult.java index b0c0bd1a96a..aada959f7e2 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/CompilationResult.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/CompilationResult.java @@ -14,6 +14,20 @@ *******************************************************************************/ package org.eclipse.jdt.internal.compiler; +import java.lang.ref.SoftReference; +import java.util.*; +import org.eclipse.jdt.core.compiler.CategorizedProblem; +import org.eclipse.jdt.core.compiler.CharOperation; +import org.eclipse.jdt.core.compiler.IProblem; +import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration; +import org.eclipse.jdt.internal.compiler.env.ICompilationUnit; +import org.eclipse.jdt.internal.compiler.impl.ReferenceContext; +import org.eclipse.jdt.internal.compiler.lookup.AnnotationBinding; +import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding; +import org.eclipse.jdt.internal.compiler.lookup.TypeConstants; +import org.eclipse.jdt.internal.compiler.parser.RecoveryScannerData; +import org.eclipse.jdt.internal.compiler.util.Util; + /** * A compilation result consists of all information returned by the compiler for * a single compiled compilation source unit. This includes: @@ -34,19 +48,6 @@ * specific fields and methods which were referenced, but does contain their * declaring types and any other types used to locate such fields or methods. */ -import java.util.*; -import org.eclipse.jdt.core.compiler.CategorizedProblem; -import org.eclipse.jdt.core.compiler.CharOperation; -import org.eclipse.jdt.core.compiler.IProblem; -import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration; -import org.eclipse.jdt.internal.compiler.env.ICompilationUnit; -import org.eclipse.jdt.internal.compiler.impl.ReferenceContext; -import org.eclipse.jdt.internal.compiler.lookup.AnnotationBinding; -import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding; -import org.eclipse.jdt.internal.compiler.lookup.TypeConstants; -import org.eclipse.jdt.internal.compiler.parser.RecoveryScannerData; -import org.eclipse.jdt.internal.compiler.util.Util; - @SuppressWarnings({ "rawtypes", "unchecked" }) public class CompilationResult { @@ -77,6 +78,8 @@ public class CompilationResult { private boolean hasMandatoryErrors; public List annotations = new ArrayList<>(1); private List scheduledProblems; + private volatile boolean cacheSource; + private volatile SoftReference contentRef; private static final int[] EMPTY_LINE_ENDS = Util.EMPTY_INT_ARRAY; private static final Comparator PROBLEM_COMPARATOR = new Comparator() { @@ -481,4 +484,29 @@ public void materializeProblems() { } } } + +public void cacheSource() { + this.cacheSource = true; +} + +public char[] getContents() { + SoftReference cr = this.contentRef; + if (cr != null) { + char[] cachedContents = cr.get(); + if (cachedContents != null) { + return cachedContents; + } + } + return this.compilationUnit.getContents(); +} + +public void cacheContents(char[] contents) { + if (this.cacheSource) { + this.contentRef = new SoftReference<>(contents); + } +} + +public void releaseContent() { + this.contentRef = null; +} } diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/Compiler.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/Compiler.java index 0c75cc484f2..5b624bd5048 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/Compiler.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/Compiler.java @@ -891,6 +891,7 @@ protected void internalBeginToCompile(ICompilationUnit[] sourceUnits, int maxUni if (this.totalUnits < this.parseThreshold) { parsedUnit = this.parser.parse(sourceUnits[i], unitResult); } else { + unitResult.cacheSource(); parsedUnit = this.parser.dietParse(sourceUnits[i], unitResult); } long resolveStart = System.currentTimeMillis(); diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/CompilationUnitDeclaration.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/CompilationUnitDeclaration.java index 1f6e2e90a6c..d98e859248d 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/CompilationUnitDeclaration.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/CompilationUnitDeclaration.java @@ -176,6 +176,7 @@ public void cleanUp() { if (this.scope != null) this.scope.cleanUpInferenceContexts(); + this.compilationResult.releaseContent(); } private void cleanUp(TypeDeclaration type) { diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/parser/Parser.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/parser/Parser.java index e7cc1fbfcfc..01f3495673b 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/parser/Parser.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/parser/Parser.java @@ -11604,7 +11604,7 @@ public void getMethodBodies(CompilationUnitDeclaration unit) { CompilationResult compilationResult = unit.compilationResult; char[] contents = this.readManager != null ? this.readManager.getContents(compilationResult.compilationUnit) - : compilationResult.compilationUnit.getContents(); + : compilationResult.getContents(); this.scanner.setSource(contents, compilationResult); if (this.javadocParser != null && this.javadocParser.checkDocComment) { @@ -12841,6 +12841,7 @@ public CompilationUnitDeclaration parse( problemReporter().cannotReadSource(this.compilationUnit, abortException, this.options.verbose); contents = CharOperation.NO_CHAR; // pretend empty from thereon } + compilationResult.cacheContents(contents); this.scanner.setSource(contents); this.compilationUnit.sourceEnd = this.scanner.source.length - 1; if (end != -1) this.scanner.resetTo(start, end);