1 /*******************************************************************************
2 * Copyright (c) 2000, 2016 IBM Corporation and others.
3 *
4 * This program and the accompanying materials
5 * are made available under the terms of the Eclipse Public License 2.0
6 * which accompanies this distribution, and is available at
7 * https://www.eclipse.org/legal/epl-2.0/
8 *
9 * SPDX-License-Identifier: EPL-2.0
10 *
11 * Contributors:
12 * IBM Corporation - initial API and implementation
13 * Terry Parker <tparker@google.com> (Google Inc.) https://bugs.eclipse.org/365499
14 * Stephan Herrmann - Contribution for
15 * Bug 440477 - [null] Infrastructure for feeding external annotations into compilation
16 *******************************************************************************/
17 package org.eclipse.jdt.internal.core;
18 import java.util.HashMap;
19 import java.util.Map;
20
21 import org.eclipse.jdt.core.IJavaElement;
22 import org.eclipse.jdt.core.IJavaProject;
23 import org.eclipse.jdt.core.IPackageFragment;
24 import org.eclipse.jdt.core.IPackageFragmentRoot;
25 import org.eclipse.jdt.core.ITypeRoot;
26 import org.eclipse.jdt.internal.core.util.LRUCache;
27 import org.eclipse.jdt.internal.core.util.Util;
28
29 /**
30 * The cache of java elements to their respective info.
31 */
32 public class JavaModelCache {
33 public static boolean VERBOSE = false;
34 public static boolean DEBUG_CACHE_INSERTIONS = false;
35
36 public static final int DEFAULT_PROJECT_SIZE = 5; // average 25552 bytes per project.
37 public static final int DEFAULT_ROOT_SIZE = 50; // average 2590 bytes per root -> maximum size : 25900*BASE_VALUE bytes
38 public static final int DEFAULT_PKG_SIZE = 500; // average 1782 bytes per pkg -> maximum size : 178200*BASE_VALUE bytes
39 public static final int DEFAULT_OPENABLE_SIZE = 250; // average 6629 bytes per openable (includes children) -> maximum size : 662900*BASE_VALUE bytes
40 public static final int DEFAULT_CHILDREN_SIZE = 250*20; // average 20 children per openable
41 public static final int DEFAULT_ACCESSRULE_SIZE = 1024;
42 public static final String RATIO_PROPERTY = "org.eclipse.jdt.core.javamodelcache.ratio"; //$NON-NLS-1$
43 public static final String JAR_TYPE_RATIO_PROPERTY = "org.eclipse.jdt.core.javamodelcache.jartyperatio"; //$NON-NLS-1$
44
45 public static final Object NON_EXISTING_JAR_TYPE_INFO = new Object();
46
47 /*
48 * The memory ratio that should be applied to the above constants.
49 */
50 protected double memoryRatio = -1;
51
52 /**
53 * Active Java Model Info
54 */
55 protected JavaElementInfo modelInfo;
56
57 /**
58 * Cache of open projects.
59 */
60 protected HashMap<IJavaProject, JavaElementInfo> projectCache;
61
62 /**
63 * Cache of open package fragment roots.
64 */
65 protected ElementCache<IPackageFragmentRoot> rootCache;
66
67 /**
68 * Cache of open package fragments
69 */
70 protected ElementCache<IPackageFragment> pkgCache;
71
72 /**
73 * Cache of open compilation unit and class files
74 */
75 protected ElementCache<ITypeRoot> openableCache;
76
77 /**
78 * Cache of open children of openable Java Model Java elements
79 */
80 protected Map<IJavaElement, Object> childrenCache;
81
82 /**
83 * Cache of access rules
84 */
85 protected LRUCache<ClasspathAccessRule, ClasspathAccessRule> accessRuleCache;
86
87
88 /**
89 * Cache of open binary type (inside a jar) that have a non-open parent
90 * Values are either instance of IBinaryType or Object (see {@link #NON_EXISTING_JAR_TYPE_INFO})
91 */
92 protected LRUCache<IJavaElement, Object> jarTypeCache;
93
JavaModelCache()94 public JavaModelCache() {
95 // set the size of the caches as a function of the maximum amount of memory available
96 double ratio = getMemoryRatio();
97 // adjust the size of the openable cache using the RATIO_PROPERTY property
98 double openableRatio = getOpenableRatio();
99 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)
100 if (VERBOSE) {
101 this.rootCache = new VerboseElementCache<>((int) (DEFAULT_ROOT_SIZE * ratio), "Root cache"); //$NON-NLS-1$
102 this.pkgCache = new VerboseElementCache<>((int) (DEFAULT_PKG_SIZE * ratio), "Package cache"); //$NON-NLS-1$
103 this.openableCache = new VerboseElementCache<>((int) (DEFAULT_OPENABLE_SIZE * ratio * openableRatio), "Openable cache"); //$NON-NLS-1$
104 } else {
105 this.rootCache = new ElementCache<>((int) (DEFAULT_ROOT_SIZE * ratio));
106 this.pkgCache = new ElementCache<>((int) (DEFAULT_PKG_SIZE * ratio));
107 this.openableCache = new ElementCache<>((int) (DEFAULT_OPENABLE_SIZE * ratio * openableRatio));
108 }
109 this.childrenCache = new HashMap<>((int) (DEFAULT_CHILDREN_SIZE * ratio * openableRatio));
110 this.accessRuleCache = new LRUCache<>(DEFAULT_ACCESSRULE_SIZE);
111 resetJarTypeCache();
112 }
113
getOpenableRatio()114 private double getOpenableRatio() {
115 return getRatioForProperty(RATIO_PROPERTY);
116 }
117
getJarTypeRatio()118 private double getJarTypeRatio() {
119 return getRatioForProperty(JAR_TYPE_RATIO_PROPERTY);
120 }
121
getRatioForProperty(String propertyName)122 private double getRatioForProperty(String propertyName) {
123 String property = System.getProperty(propertyName);
124 if (property != null) {
125 try {
126 return Double.parseDouble(property);
127 } catch (NumberFormatException e) {
128 // ignore
129 Util.log(e, "Could not parse value for " + propertyName + ": " + property); //$NON-NLS-1$ //$NON-NLS-2$
130 }
131 }
132 return 1.0;
133 }
134
135 /**
136 * Returns the info for the element.
137 */
getInfo(IJavaElement element)138 public Object getInfo(IJavaElement element) {
139 switch (element.getElementType()) {
140 case IJavaElement.JAVA_MODEL:
141 return this.modelInfo;
142 case IJavaElement.JAVA_PROJECT:
143 return this.projectCache.get(element);
144 case IJavaElement.PACKAGE_FRAGMENT_ROOT:
145 return this.rootCache.get((IPackageFragmentRoot) element);
146 case IJavaElement.PACKAGE_FRAGMENT:
147 return this.pkgCache.get((IPackageFragment) element);
148 case IJavaElement.COMPILATION_UNIT:
149 case IJavaElement.CLASS_FILE:
150 return this.openableCache.get((ITypeRoot) element);
151 case IJavaElement.TYPE:
152 Object result = this.jarTypeCache.get(element);
153 if (result != null)
154 return result;
155 else
156 return this.childrenCache.get(element);
157 default:
158 return this.childrenCache.get(element);
159 }
160 }
161
162 /*
163 * Returns the existing element that is equal to the given element if present in the cache.
164 * Returns the given element otherwise.
165 */
getExistingElement(IJavaElement element)166 public IJavaElement getExistingElement(IJavaElement element) {
167 switch (element.getElementType()) {
168 case IJavaElement.JAVA_MODEL:
169 return element;
170 case IJavaElement.JAVA_PROJECT:
171 return element; // projectCache is a Hashtable and Hashtables don't support getKey(...)
172 case IJavaElement.PACKAGE_FRAGMENT_ROOT:
173 return this.rootCache.getKey((IPackageFragmentRoot) element);
174 case IJavaElement.PACKAGE_FRAGMENT:
175 return this.pkgCache.getKey((IPackageFragment) element);
176 case IJavaElement.COMPILATION_UNIT:
177 case IJavaElement.CLASS_FILE:
178 return this.openableCache.getKey((ITypeRoot) element);
179 case IJavaElement.TYPE:
180 return element; // jarTypeCache or childrenCache are Hashtables and Hashtables don't support getKey(...)
181 default:
182 return element; // childrenCache is a Hashtable and Hashtables don't support getKey(...)
183 }
184 }
185
getMemoryRatio()186 protected double getMemoryRatio() {
187 if ((int) this.memoryRatio == -1) {
188 long maxMemory = Runtime.getRuntime().maxMemory();
189 // if max memory is infinite, set the ratio to 4d which corresponds to the 256MB that Eclipse defaults to
190 // (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=111299)
191 this.memoryRatio = maxMemory == Long.MAX_VALUE ? 4d : ((double) maxMemory) / (64 * 0x100000); // 64MB is the base memory for most JVM
192 }
193 return this.memoryRatio;
194 }
195
196 /**
197 * Returns the info for this element without
198 * disturbing the cache ordering.
199 */
peekAtInfo(IJavaElement element)200 protected Object peekAtInfo(IJavaElement element) {
201 switch (element.getElementType()) {
202 case IJavaElement.JAVA_MODEL:
203 return this.modelInfo;
204 case IJavaElement.JAVA_PROJECT:
205 return this.projectCache.get(element);
206 case IJavaElement.PACKAGE_FRAGMENT_ROOT:
207 return this.rootCache.peek((IPackageFragmentRoot) element);
208 case IJavaElement.PACKAGE_FRAGMENT:
209 return this.pkgCache.peek((IPackageFragment) element);
210 case IJavaElement.COMPILATION_UNIT:
211 case IJavaElement.CLASS_FILE:
212 return this.openableCache.peek((ITypeRoot) element);
213 case IJavaElement.TYPE:
214 Object result = this.jarTypeCache.peek(element);
215 if (result != null)
216 return result;
217 else
218 return this.childrenCache.get(element);
219 default:
220 return this.childrenCache.get(element);
221 }
222 }
223
224 /**
225 * Remember the info for the element.
226 */
putInfo(IJavaElement element, Object info)227 protected void putInfo(IJavaElement element, Object info) {
228 if (DEBUG_CACHE_INSERTIONS) {
229 System.out.println(Thread.currentThread() + " cache putInfo (" + getElementType(element) + " " + element.toString() + ", " + info + ")"); //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$//$NON-NLS-4$
230 }
231 switch (element.getElementType()) {
232 case IJavaElement.JAVA_MODEL:
233 this.modelInfo = (JavaElementInfo) info;
234 break;
235 case IJavaElement.JAVA_PROJECT:
236 this.projectCache.put((IJavaProject) element, (JavaElementInfo) info);
237 this.rootCache.ensureSpaceLimit((JavaElementInfo) info, element);
238 break;
239 case IJavaElement.PACKAGE_FRAGMENT_ROOT:
240 this.rootCache.put((IPackageFragmentRoot) element, (JavaElementInfo) info);
241 this.pkgCache.ensureSpaceLimit((JavaElementInfo) info, element);
242 break;
243 case IJavaElement.PACKAGE_FRAGMENT:
244 this.pkgCache.put((IPackageFragment) element, (JavaElementInfo) info);
245 this.openableCache.ensureSpaceLimit((JavaElementInfo) info, element);
246 break;
247 case IJavaElement.COMPILATION_UNIT:
248 case IJavaElement.CLASS_FILE:
249 this.openableCache.put((ITypeRoot) element, (JavaElementInfo) info);
250 break;
251 default:
252 this.childrenCache.put(element, info);
253 }
254 }
255
getElementType(IJavaElement element)256 public static String getElementType(IJavaElement element) {
257 String elementType;
258 switch (element.getElementType()) {
259 case IJavaElement.JAVA_PROJECT:
260 elementType = "project"; //$NON-NLS-1$
261 break;
262 case IJavaElement.PACKAGE_FRAGMENT_ROOT:
263 elementType = "root"; //$NON-NLS-1$
264 break;
265 case IJavaElement.PACKAGE_FRAGMENT:
266 elementType = "package"; //$NON-NLS-1$
267 break;
268 case IJavaElement.CLASS_FILE:
269 elementType = "class file"; //$NON-NLS-1$
270 break;
271 case IJavaElement.COMPILATION_UNIT:
272 elementType = "compilation unit"; //$NON-NLS-1$
273 break;
274 default:
275 elementType = "element"; //$NON-NLS-1$
276 }
277 return elementType;
278 }
279
280 /**
281 * Removes the info of the element from the cache.
282 */
removeInfo(JavaElement element)283 protected void removeInfo(JavaElement element) {
284 if (DEBUG_CACHE_INSERTIONS) {
285 String elementToString = element.toString();
286 System.out.println(Thread.currentThread() + " cache removeInfo " + getElementType(element) + " " + elementToString); //$NON-NLS-1$//$NON-NLS-2$
287 }
288 switch (element.getElementType()) {
289 case IJavaElement.JAVA_MODEL:
290 this.modelInfo = null;
291 break;
292 case IJavaElement.JAVA_PROJECT:
293 this.projectCache.remove((IJavaProject)element);
294 this.rootCache.resetSpaceLimit((int) (DEFAULT_ROOT_SIZE * getMemoryRatio()), element);
295 break;
296 case IJavaElement.PACKAGE_FRAGMENT_ROOT:
297 this.rootCache.remove((IPackageFragmentRoot) element);
298 this.pkgCache.resetSpaceLimit((int) (DEFAULT_PKG_SIZE * getMemoryRatio()), element);
299 break;
300 case IJavaElement.PACKAGE_FRAGMENT:
301 this.pkgCache.remove((IPackageFragment) element);
302 this.openableCache.resetSpaceLimit((int) (DEFAULT_OPENABLE_SIZE * getMemoryRatio() * getOpenableRatio()), element);
303 break;
304 case IJavaElement.COMPILATION_UNIT:
305 case IJavaElement.CLASS_FILE:
306 this.openableCache.remove((ITypeRoot) element);
307 break;
308 default:
309 this.childrenCache.remove(element);
310 }
311 }
resetJarTypeCache()312 protected void resetJarTypeCache() {
313 this.jarTypeCache = new LRUCache<>((int) (DEFAULT_OPENABLE_SIZE * getMemoryRatio() * getJarTypeRatio()));
314 }
removeFromJarTypeCache(BinaryType type)315 protected void removeFromJarTypeCache(BinaryType type) {
316 this.jarTypeCache.flush(type);
317 }
318 @Override
toString()319 public String toString() {
320 return toStringFillingRation(""); //$NON-NLS-1$
321 }
toStringFillingRation(String prefix)322 public String toStringFillingRation(String prefix) {
323 StringBuffer buffer = new StringBuffer();
324 buffer.append(prefix);
325 buffer.append("Project cache: "); //$NON-NLS-1$
326 buffer.append(this.projectCache.size());
327 buffer.append(" projects\n"); //$NON-NLS-1$
328 buffer.append(prefix);
329 buffer.append(this.rootCache.toStringFillingRation("Root cache")); //$NON-NLS-1$
330 buffer.append('\n');
331 buffer.append(prefix);
332 buffer.append(this.pkgCache.toStringFillingRation("Package cache")); //$NON-NLS-1$
333 buffer.append('\n');
334 buffer.append(prefix);
335 buffer.append(this.openableCache.toStringFillingRation("Openable cache")); //$NON-NLS-1$
336 buffer.append('\n');
337 buffer.append(prefix);
338 buffer.append(this.jarTypeCache.toStringFillingRation("Jar type cache")); //$NON-NLS-1$
339 buffer.append('\n');
340 return buffer.toString();
341 }
342 }
343