org.eclipse.persistence.internal.identitymaps.HardCacheWeakIdentityMap Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of eclipselink Show documentation
Show all versions of eclipselink Show documentation
EclipseLink build based upon Git transaction f2b9fc5
/*
* 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;
/**
* Purpose: 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.
* This class makes use of an exposed node linked list to maintain the objects by storing the link nodes in the cache key.
*
Responsibilities:
* - Guarantees identity
*
- Allows garbage collection
*
- Increases performance by maintaining a fixed size cache of LRU objects when memory is available
*
- The default size of the reference cache is half the max size
*
* @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();
}
}
}
}
}