Skip to content

Commit

Permalink
Increase JavaModelCache #1929
Browse files Browse the repository at this point in the history
* independent on JVM memory
* all caches scale with org.eclipse.jdt.core.javamodelcache.ratio
* improve tracing output for DEBUG_CACHE_INSERTIONS
* possibility to trace to stdout only (much faster)

#1929
  • Loading branch information
EcljpseB0T authored and jukzi committed Feb 1, 2024
1 parent 4c52c0c commit d80b1ba
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 70 deletions.
Expand Up @@ -23,18 +23,21 @@
public class ElementCache<K extends IJavaElement & IOpenable> extends OverflowingLRUCache<K, JavaElementInfo> {

IJavaElement spaceLimitParent = null;
private final int initialSpaceLimit;

/**
* Constructs a new element cache of the given size.
*/
public ElementCache(int size) {
super(size);
this.initialSpaceLimit = size;
}
/**
* Constructs a new element cache of the given size.
*/
public ElementCache(int size, int overflow) {
super(size, overflow);
this.initialSpaceLimit = size;
}
/**
* Returns true if the element is successfully closed and
Expand Down Expand Up @@ -89,9 +92,9 @@ protected ElementCache<K> newInstance(int size, int newOverflow) {
* If the given parent was the one that increased the space limit, reset
* the space limit to the given default value.
*/
protected void resetSpaceLimit(int defaultLimit, IJavaElement parent) {
protected void resetSpaceLimit(IJavaElement parent) {
if (parent.equals(this.spaceLimitParent)) {
setSpaceLimit(defaultLimit);
setSpaceLimit(this.initialSpaceLimit);
this.spaceLimitParent = null;
}
}
Expand Down
Expand Up @@ -35,11 +35,10 @@ public class JavaModelCache {
public static boolean VERBOSE = false;
public static boolean DEBUG_CACHE_INSERTIONS = false;

public static final int DEFAULT_PROJECT_SIZE = 5; // average 25552 bytes per project.
public static final int DEFAULT_ROOT_SIZE = 50; // average 2590 bytes per root -> maximum size : 25900*BASE_VALUE bytes
public static final int DEFAULT_PKG_SIZE = 500; // average 1782 bytes per pkg -> maximum size : 178200*BASE_VALUE bytes
public static final int DEFAULT_OPENABLE_SIZE = 250; // average 6629 bytes per openable (includes children) -> maximum size : 662900*BASE_VALUE bytes
public static final int DEFAULT_CHILDREN_SIZE = 250*20; // average 20 children per openable
public static final int DEFAULT_ROOT_SIZE = 2_000;
public static final int DEFAULT_PKG_SIZE = 20_000;
public static final int DEFAULT_OPENABLE_SIZE = 200_000;

public static final String RATIO_PROPERTY = "org.eclipse.jdt.core.javamodelcache.ratio"; //$NON-NLS-1$
public static final String JAR_TYPE_RATIO_PROPERTY = "org.eclipse.jdt.core.javamodelcache.jartyperatio"; //$NON-NLS-1$

Expand Down Expand Up @@ -87,24 +86,31 @@ public class JavaModelCache {
protected LRUCache<IJavaElement, IElementInfo> jarTypeCache;

public JavaModelCache() {
// set the size of the caches as a function of the maximum amount of memory available
double ratio = getMemoryRatio();
// adjust the size of the openable cache using the RATIO_PROPERTY property
double openableRatio = getOpenableRatio();
this.projectCache = new HashMap<>(DEFAULT_PROJECT_SIZE); // NB: Don't use a LRUCache for projects as they are constantly reopened (e.g. during delta processing)
int rootCacheSize = sizeLimit(DEFAULT_ROOT_SIZE * openableRatio);
int packageCacheeSize = sizeLimit(DEFAULT_PKG_SIZE * openableRatio);
int openableCacheSize = sizeLimit(DEFAULT_OPENABLE_SIZE * openableRatio);

// NB: Don't use a LRUCache for projects as they are
// constantly reopened (e.g. during delta processing)
this.projectCache = new HashMap<>(); // HashMap size adjusts automatically
if (VERBOSE) {
this.rootCache = new VerboseElementCache<>((int) (DEFAULT_ROOT_SIZE * ratio), "Root cache"); //$NON-NLS-1$
this.pkgCache = new VerboseElementCache<>((int) (DEFAULT_PKG_SIZE * ratio), "Package cache"); //$NON-NLS-1$
this.openableCache = new VerboseElementCache<>((int) (DEFAULT_OPENABLE_SIZE * ratio * openableRatio), "Openable cache"); //$NON-NLS-1$
this.rootCache = new VerboseElementCache<>(rootCacheSize, "Root cache"); //$NON-NLS-1$
this.pkgCache = new VerboseElementCache<>(packageCacheeSize, "Package cache"); //$NON-NLS-1$
this.openableCache = new VerboseElementCache<>(openableCacheSize, "Openable cache"); //$NON-NLS-1$
} else {
this.rootCache = new ElementCache<>((int) (DEFAULT_ROOT_SIZE * ratio));
this.pkgCache = new ElementCache<>((int) (DEFAULT_PKG_SIZE * ratio));
this.openableCache = new ElementCache<>((int) (DEFAULT_OPENABLE_SIZE * ratio * openableRatio));
this.rootCache = new ElementCache<>(rootCacheSize);
this.pkgCache = new ElementCache<>(packageCacheeSize);
this.openableCache = new ElementCache<>(openableCacheSize);
}
this.childrenCache = new HashMap<>((int) (DEFAULT_CHILDREN_SIZE * ratio * openableRatio));
this.childrenCache = new HashMap<>(); // HashMap size adjusts automatically
resetJarTypeCache();
}

private int sizeLimit(double d) {
return (int) Double.min(Integer.MAX_VALUE/2,d);
}

private double getOpenableRatio() {
return getRatioForProperty(RATIO_PROPERTY);
}
Expand Down Expand Up @@ -177,16 +183,6 @@ public IJavaElement getExistingElement(IJavaElement element) {
}
}

protected double getMemoryRatio() {
if ((int) this.memoryRatio == -1) {
long maxMemory = Runtime.getRuntime().maxMemory();
// if max memory is infinite, set the ratio to 4d which corresponds to the 256MB that Eclipse defaults to
// (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=111299)
this.memoryRatio = maxMemory == Long.MAX_VALUE ? 4d : ((double) maxMemory) / (64 * 0x100000); // 64MB is the base memory for most JVM
}
return this.memoryRatio;
}

/**
* Returns the info for this element without
* disturbing the cache ordering.
Expand Down Expand Up @@ -219,9 +215,6 @@ protected IElementInfo peekAtInfo(IJavaElement element) {
* Remember the info for the element.
*/
protected void putInfo(IJavaElement element, IElementInfo info) {
if (DEBUG_CACHE_INSERTIONS) {
JavaModelManager.trace(Thread.currentThread() + " cache putInfo (" + getElementType(element) + " " + element.toString() + ", " + info + ")"); //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$//$NON-NLS-4$
}
switch (element.getElementType()) {
case IJavaElement.JAVA_MODEL:
this.modelInfo = (JavaElementInfo) info;
Expand All @@ -244,67 +237,75 @@ protected void putInfo(IJavaElement element, IElementInfo info) {
break;
default:
this.childrenCache.put(element, info);
return; // don't trace children -- too many
}
if (DEBUG_CACHE_INSERTIONS) {
JavaModelManager.trace(Thread.currentThread() + " cache putInfo " + getCacheType(element) + " " + element.getElementName() + ", " + info.getClass().getSimpleName()); //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$
}
}

public static String getElementType(IJavaElement element) {
static String getCacheType(IJavaElement element) {
String elementType;
switch (element.getElementType()) {
case IJavaElement.JAVA_MODEL:
elementType = "JAVA_MODEL"; //$NON-NLS-1$
break;
case IJavaElement.JAVA_PROJECT:
elementType = "project"; //$NON-NLS-1$
elementType = "JAVA_PROJECT"; //$NON-NLS-1$
break;
case IJavaElement.PACKAGE_FRAGMENT_ROOT:
elementType = "root"; //$NON-NLS-1$
elementType = "PACKAGE_FRAGMENT_ROOT"; //$NON-NLS-1$
break;
case IJavaElement.PACKAGE_FRAGMENT:
elementType = "package"; //$NON-NLS-1$
elementType = "PACKAGE_FRAGMENT"; //$NON-NLS-1$
break;
case IJavaElement.CLASS_FILE:
elementType = "class file"; //$NON-NLS-1$
elementType = "CLASS_FILE"; //$NON-NLS-1$
break;
case IJavaElement.COMPILATION_UNIT:
elementType = "compilation unit"; //$NON-NLS-1$
elementType = "COMPILATION_UNIT"; //$NON-NLS-1$
break;
default:
elementType = "element"; //$NON-NLS-1$
elementType = "CHILD"; //$NON-NLS-1$
}
return elementType;
return elementType + ", " + element.getClass().getSimpleName(); //$NON-NLS-1$
}

/**
* Removes the info of the element from the cache.
*/
protected void removeInfo(JavaElement element) {
if (DEBUG_CACHE_INSERTIONS) {
String elementToString = element.toString();
JavaModelManager.trace(Thread.currentThread() + " cache removeInfo " + getElementType(element) + " " + elementToString); //$NON-NLS-1$//$NON-NLS-2$
}
switch (element.getElementType()) {
case IJavaElement.JAVA_MODEL:
this.modelInfo = null;
break;
case IJavaElement.JAVA_PROJECT:
this.projectCache.remove((IJavaProject)element);
this.rootCache.resetSpaceLimit((int) (DEFAULT_ROOT_SIZE * getMemoryRatio()), element);
this.rootCache.resetSpaceLimit(element);
break;
case IJavaElement.PACKAGE_FRAGMENT_ROOT:
this.rootCache.remove((IPackageFragmentRoot) element);
this.pkgCache.resetSpaceLimit((int) (DEFAULT_PKG_SIZE * getMemoryRatio()), element);
this.pkgCache.resetSpaceLimit(element);
break;
case IJavaElement.PACKAGE_FRAGMENT:
this.pkgCache.remove((IPackageFragment) element);
this.openableCache.resetSpaceLimit((int) (DEFAULT_OPENABLE_SIZE * getMemoryRatio() * getOpenableRatio()), element);
this.openableCache.resetSpaceLimit(element);
break;
case IJavaElement.COMPILATION_UNIT:
case IJavaElement.CLASS_FILE:
this.openableCache.remove((ITypeRoot) element);
break;
default:
this.childrenCache.remove(element);
return; // don't trace children -- too many
}
if (DEBUG_CACHE_INSERTIONS) {
JavaModelManager.trace(Thread.currentThread() + " cache removeInfo " + getCacheType(element) + " " + element.getElementName()); //$NON-NLS-1$//$NON-NLS-2$
}
}
protected void resetJarTypeCache() {
this.jarTypeCache = new LRUCache<>((int) (DEFAULT_OPENABLE_SIZE * getMemoryRatio() * getJarTypeRatio()));
int jarTypeCacheSize = sizeLimit(DEFAULT_OPENABLE_SIZE * getJarTypeRatio());
this.jarTypeCache = new LRUCache<>(jarTypeCacheSize);
}
protected void removeFromJarTypeCache(BinaryType type) {
this.jarTypeCache.flush(type);
Expand Down
Expand Up @@ -375,6 +375,7 @@ public void setCache(IPath path, ZipFile zipFile) {
private static final String COMPILER_DEBUG = JavaCore.PLUGIN_ID + "/debug/compiler" ; //$NON-NLS-1$
private static final String JAVAMODEL_CLASSPATH = JavaCore.PLUGIN_ID + "/debug/javamodel/classpath" ; //$NON-NLS-1$
private static final String JAVAMODEL_DEBUG = JavaCore.PLUGIN_ID + "/debug/javamodel" ; //$NON-NLS-1$
private static final String JAVAMODEL_STDOUT = JavaCore.PLUGIN_ID + "/debug/traceToStdOut" ; //$NON-NLS-1$
private static final String JAVAMODEL_INVALID_ARCHIVES = JavaCore.PLUGIN_ID + "/debug/javamodel/invalid_archives" ; //$NON-NLS-1$
private static final String JAVAMODELCACHE_DEBUG = JavaCore.PLUGIN_ID + "/debug/javamodel/cache" ; //$NON-NLS-1$
private static final String JAVAMODELCACHE_INSERTIONS_DEBUG = JavaCore.PLUGIN_ID + "/debug/javamodel/insertions" ; //$NON-NLS-1$
Expand Down Expand Up @@ -1717,6 +1718,7 @@ public String toString() {

// The amount of time from when an invalid archive is first sensed until that state is considered stale.
private static long INVALID_ARCHIVE_TTL_MILLISECONDS = 2 * 60 * 1000;
private static boolean TRACE_TO_STDOUT;

private static class InvalidArchiveInfo {
/**
Expand Down Expand Up @@ -1995,6 +1997,7 @@ public void optionsChanged(DebugOptions options) {
JavaModelManager.DEBUG_CLASSPATH = debug && options.getBooleanOption(JAVAMODEL_CLASSPATH, false);
JavaModelManager.DEBUG_INVALID_ARCHIVES = debug && options.getBooleanOption(JAVAMODEL_INVALID_ARCHIVES, false);
JavaModelManager.VERBOSE = debug && options.getBooleanOption(JAVAMODEL_DEBUG, false);
JavaModelManager.TRACE_TO_STDOUT = debug && options.getBooleanOption(JAVAMODEL_STDOUT, false);
JavaModelCache.VERBOSE = debug && options.getBooleanOption(JAVAMODELCACHE_DEBUG, false);
JavaModelCache.DEBUG_CACHE_INSERTIONS = debug && options.getBooleanOption(JAVAMODELCACHE_INSERTIONS_DEBUG, false);
JavaModelOperation.POST_ACTION_VERBOSE = debug && options.getBooleanOption(POST_ACTION_DEBUG, false);
Expand Down Expand Up @@ -4344,7 +4347,7 @@ public synchronized Object removeInfoAndChildren(JavaElement element) throws Jav
boolean wasVerbose = false;
try {
if (JavaModelCache.VERBOSE) {
String elementType = JavaModelCache.getElementType(element);
String elementType = JavaModelCache.getCacheType(element);
trace(Thread.currentThread() + " CLOSING "+ elementType + " " + element.toStringWithAncestors()); //$NON-NLS-1$//$NON-NLS-2$
wasVerbose = true;
JavaModelCache.VERBOSE = false;
Expand Down Expand Up @@ -4784,7 +4787,11 @@ private void traceVariableAndContainers(String action, long start) {
}

public static void trace(String msg) {
DEBUG_TRACE.trace(null, msg);
if (TRACE_TO_STDOUT) {
System.out.println(msg);
} else {
DEBUG_TRACE.trace(null, msg);
}
}

public static void trace(String msg, Exception e) {
Expand Down
Expand Up @@ -220,27 +220,7 @@ public String findRecommendedLineSeparator() throws JavaModelException {
protected void generateInfos(IElementInfo info, Map<IJavaElement, IElementInfo> newElements, IProgressMonitor monitor) throws JavaModelException {

if (JavaModelCache.VERBOSE){
String element;
switch (getElementType()) {
case JAVA_PROJECT:
element = "project"; //$NON-NLS-1$
break;
case PACKAGE_FRAGMENT_ROOT:
element = "root"; //$NON-NLS-1$
break;
case PACKAGE_FRAGMENT:
element = "package"; //$NON-NLS-1$
break;
case CLASS_FILE:
element = "class file"; //$NON-NLS-1$
break;
case COMPILATION_UNIT:
element = "compilation unit"; //$NON-NLS-1$
break;
default:
element = "element"; //$NON-NLS-1$
}
JavaModelManager.trace(Thread.currentThread() +" OPENING " + element + " " + this.toStringWithAncestors()); //$NON-NLS-1$//$NON-NLS-2$
JavaModelManager.trace(Thread.currentThread() +" OPENING " + JavaModelCache.getCacheType(this) + " " + this.toStringWithAncestors()); //$NON-NLS-1$//$NON-NLS-2$
}

// open its ancestors if needed
Expand Down

0 comments on commit d80b1ba

Please sign in to comment.