| /* |
| * Copyright (c) 1998, 2019 Oracle and/or its affiliates. All rights reserved. |
| * |
| * This program and the accompanying materials are made available under the |
| * terms of the Eclipse Public License v. 2.0 which is available at |
| * http://www.eclipse.org/legal/epl-2.0, |
| * or the Eclipse Distribution License v. 1.0 which is available at |
| * http://www.eclipse.org/org/documents/edl-v10.php. |
| * |
| * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause |
| */ |
| |
| // Contributors: |
| // Oracle - initial API and implementation from Oracle TopLink |
| package org.eclipse.persistence.internal.identitymaps; |
| |
| import org.eclipse.persistence.descriptors.ClassDescriptor; |
| import org.eclipse.persistence.internal.helper.linkedlist.ExposedNodeLinkedList; |
| import org.eclipse.persistence.internal.helper.linkedlist.LinkedNode; |
| import org.eclipse.persistence.internal.sessions.AbstractSession; |
| |
| /** |
| * <p><b>Purpose</b>: A HardCacheWeakIdentityMap is identical to the weak identity map, however the weak reference |
| * can be a performance problem for some types of apps because it can cause too much garbage collection |
| * of objects read causing them to be re-read and re-built (this defeats the purpose of the cache). |
| * The hard weak cache solves this by also holding a fixed number of objects in memory to improve caching.<br> |
| * This class makes use of an exposed node linked list to maintain the objects by storing the link nodes in the cache key. |
| * <p><b>Responsibilities</b>:<ul> |
| * <li> Guarantees identity |
| * <li> Allows garbage collection |
| * <li> Increases performance by maintaining a fixed size cache of LRU objects when memory is available |
| * <li> The default size of the reference cache is half the max size |
| * </ul> |
| * @since TOPLink/Java 1.2 |
| */ |
| public class HardCacheWeakIdentityMap extends WeakIdentityMap { |
| /** A subset of cache entries have hard references maintained in this list to reduce garbage collection frequency */ |
| protected ExposedNodeLinkedList referenceCache; |
| |
| public HardCacheWeakIdentityMap(int size, ClassDescriptor descriptor, AbstractSession session, boolean isIsolated) { |
| super(size, descriptor, session, isIsolated); |
| this.referenceCache = new ExposedNodeLinkedList(); |
| } |
| |
| /** |
| * Use a ReferenceCacheKey that also stores the linked list node to manage |
| * the LRU sub-cache of references. |
| */ |
| @Override |
| public CacheKey createCacheKey(Object primaryKey, Object object, Object writeLockValue, long readTime) { |
| return new ReferenceCacheKey(primaryKey, object, writeLockValue, readTime, isIsolated); |
| } |
| |
| /** |
| * Return the linked reference cache. |
| */ |
| public ExposedNodeLinkedList getReferenceCache() { |
| return referenceCache; |
| } |
| |
| /** |
| * Allows subclass to create a SoftReference to the object. |
| * @param object is the domain object to cache. |
| */ |
| public Object buildReference(Object object) { |
| return object; |
| } |
| |
| /** |
| * Checks if the object is null, or reference's object is null. |
| * @param reference the object for hard or the reference for soft. |
| */ |
| public boolean hasReference(Object reference) { |
| return reference != null; |
| } |
| |
| /** |
| * Remove the cache key from the map and the sub-cache list. |
| */ |
| @Override |
| public Object remove(CacheKey cacheKey) { |
| if (cacheKey == null) { |
| return null; |
| } |
| LinkedNode node = ((ReferenceCacheKey)cacheKey).getReferenceCacheNode(); |
| // Node is initially null while object is being built. |
| if (node != null) { |
| synchronized (this.referenceCache) { |
| this.referenceCache.remove(node); |
| } |
| } |
| return super.remove(cacheKey); |
| } |
| |
| /** |
| * Store the object in the cache at its primary key, and add to sub-cache list. |
| */ |
| @Override |
| public CacheKey put(Object primaryKey, Object object, Object writeLockValue, long readTime) { |
| CacheKey cacheKey = super.put(primaryKey, object, writeLockValue, readTime); |
| cacheKey.updateAccess(); |
| return cacheKey; |
| } |
| |
| /** |
| * This method will be used to update the max cache size. |
| */ |
| @Override |
| public synchronized void updateMaxSize(int maxSize) { |
| setMaxSize(maxSize); |
| synchronized (this.referenceCache) { |
| // Remove the LRU items if max size exceeded. |
| while (this.referenceCache.size() > this.maxSize) { |
| this.referenceCache.removeLast(); |
| } |
| } |
| } |
| |
| /** |
| * Inner class to define the specialized weak cache key. |
| * Keeps track of the linked list node to allow quick repositioning. |
| */ |
| public class ReferenceCacheKey extends WeakCacheKey { |
| protected LinkedNode referenceNode; |
| |
| public ReferenceCacheKey(Object primaryKey, Object object, Object writeLockValue, long readTime, boolean isIsolated) { |
| super(primaryKey, object, writeLockValue, readTime, isIsolated); |
| } |
| |
| public LinkedNode getReferenceCacheNode() { |
| return referenceNode; |
| } |
| |
| public void setReferenceCacheNode(LinkedNode referenceNode) { |
| this.referenceNode = referenceNode; |
| } |
| |
| public ExposedNodeLinkedList getReferenceCache() { |
| return referenceCache; |
| } |
| |
| /** |
| * Notifies that cache key that it has been accessed. |
| * Allows the LRU sub-cache to be maintained, |
| * the cache node must be moved to the front of the list. |
| */ |
| @Override |
| public void updateAccess() { |
| // Check if the node's contents is null (was removed), |
| // or ref value may have garbage collected so reset it. |
| if ((this.referenceNode != null) && (!hasReference(this.referenceNode.getContents()))) { |
| this.referenceNode.setContents(buildReference(getObject())); |
| } |
| // PERF: Synchronize on the linked list. |
| synchronized (referenceCache) { |
| // If reference node is null, add to start (new cache key). |
| if (this.referenceNode == null) { |
| this.referenceNode = referenceCache.addFirst(buildReference(getObject())); |
| } else { |
| // This is a fast constant time operations because of the linked list usage. |
| referenceCache.moveFirst(getReferenceCacheNode()); |
| } |
| // Remove the old LRU items if max size exceeded (if was removed). |
| while (referenceCache.size() > maxSize) { |
| referenceCache.removeLast(); |
| } |
| } |
| } |
| } |
| } |