(Trying again. Last post, ~50 minutes to compose, failed, possibly due to an attachment upload size limit being exceeded before the post attempt. =( )
I desire to have my persistent objects be cached until the Java Virtual Machine (JVM) does a full Garbage Collection (GC) when heap RAM gets low for my application. This would use a Least Recently Used (LRU) mechanism to get rid of older objects before more recently accessed ones. I attempted to use Java SoftReference to my large byte[] and instruct ObjectDB to use soft reference for its Level One (L1) object data cache. I disable the L2 cache and use 1 MB for the ObjectDB Datastore page file cache with page file size of 64 KB. Here are relevant objectdb.conf settings:
<size initial="64kb" resize="64kb" page="64kb" /> <processing cache="1mb" max-threads="10" /> <query-cache results="16kb" programs="10" /> <cache ref="soft" level2="0" /> <dirty-tracking arrays="false" />
My persisted class, "JdoBlob2", only contains a byte[] and uses a soft reference to make that array eligible for GC when the JVM does a full GC.
private byte[] data;// use makeDirty() when changing this array or any of its elements private transient SoftReference<byte[]> softReferenceData;
I use StoreCallback and StoreLifecycleListener to have my code called to manipulate the softReferenceData in my JdoBlob2 instances, so there is a strong reference to the byte[] when data needs to be written back to ObjectDB datastore, and a soft reference when the byte[] only needs to be read and sits in the cache until more heap RAM is needed.
@Override public void jdoPreClear(){ if(null != softReferenceData) softReferenceData.clear(); softReferenceData = null; } @Override public void jdoPreDelete(){ if(null != softReferenceData) softReferenceData.clear(); softReferenceData = null; } @Override public void jdoPostLoad(){ softReferenceData = new SoftReference<>(data); data = null; } @Override public void jdoPreStore(){ // A dirty object, via setData(), strongly references byte[] via "data" field, so ignore softReferenceData. if(null != softReferenceData) softReferenceData.clear(); softReferenceData = null; } public void postStore(){ if(null != softReferenceData) softReferenceData.clear(); softReferenceData = new SoftReference<>(data); data = null; } private static final StoreLifecycleListener storeLifecycleListener = new StoreLifecycleListener(){ @Override public void preStore(InstanceLifecycleEvent instanceLifecycleEvent){ // We are using jdoPreStore() in JdoBlob2, which is called after this, so do nothing here. } @Override public void postStore(InstanceLifecycleEvent instanceLifecycleEvent){ JdoBlob2 jdoBlob2 = (JdoBlob2)instanceLifecycleEvent.getPersistentInstance(); jdoBlob2.postStore(); } };
Everything seems to work great, but some byte[]s still hang around after a full JVM GC, which I think is due to ObjectDB datastore page file cache keeping strong references to the pages that comprise the data of the byte[] until the JdoBlob2 object that owns it is evicted from the ObjectDB cache, such as when the JdoBlob2 gets deleted.
I use JavaVirtualVm to look at my application heap to see the references to the byte[] and to infer that the byte[]s cannot be released back to the heap after a full GC because of the ObjectDB owned references. I have attached a screenshot (the heap file was too large to upload).
I desire to have a JdoBlob2 object cache that flushes automatically when JVM full GC happens, which I think should be possible by configuring ObjectDB to use soft references and correct use of my JdoBlob2 in the ObjectDB environment. Do you have any recommendations on how to achieve this? Perhaps I am just missing something that you can see?
If I am unable to use ObjectDB to achieve the desired JdoBlob2 caching, then I think I must need to track all JdoBlob2s myself and, upon detecting a full JVM GC, manually evict JdoBlob2s from their relevant PersistenceManager (via persistenceManager.evict(jdoBlob2)). I would have to maintain the list with a LRU ordering to rid the cache of older JdoBlob2s, leaving the newer ones in the cache, such as delete 1/2 of the list, the older ones, for each full GC detected.