diff --git a/config/alfresco/cache-context.xml b/config/alfresco/cache-context.xml index 1e3d51054f..dd1b190301 100644 --- a/config/alfresco/cache-context.xml +++ b/config/alfresco/cache-context.xml @@ -1,14 +1,21 @@ + + - + - classpath:ehcache.xml + classpath:ehcache-transactional.xml @@ -42,7 +49,7 @@ - + @@ -83,7 +90,7 @@ - + @@ -124,7 +131,7 @@ - + @@ -165,7 +172,7 @@ - + diff --git a/config/alfresco/scheduled-jobs-context.xml b/config/alfresco/scheduled-jobs-context.xml index 1cb09935c8..226d44f53c 100644 --- a/config/alfresco/scheduled-jobs-context.xml +++ b/config/alfresco/scheduled-jobs-context.xml @@ -131,6 +131,23 @@ + + + + + + org.alfresco.repo.cache.EhCacheTracerJob + + + + + 3600000 + + + 3600000 + + + @@ -144,7 +161,9 @@ --> - + diff --git a/config/ehcache-transactional.xml b/config/ehcache-transactional.xml new file mode 100644 index 0000000000..4d92925923 --- /dev/null +++ b/config/ehcache-transactional.xml @@ -0,0 +1,14 @@ + + + + + \ No newline at end of file diff --git a/config/ehcache.xml b/config/ehcache.xml index 95ebd4e911..f4cc598289 100644 --- a/config/ehcache.xml +++ b/config/ehcache.xml @@ -1,4 +1,5 @@ + + + + - - + * To activate this class, call the {@link #init()} method. + * + * @author Derek Hulley + */ +public class EhCacheTracerJob implements Job +{ + private static Log logger = LogFactory.getLog(EhCacheTracerJob.class); + + private CacheManager cacheManager; + + /** + * Set the cache manager to analyze. The default cache manager will be analyzed + * if this property is not set. + * + * @param cacheManager optional cache manager to analyze + */ + public void setCacheManager(CacheManager cacheManager) + { + this.cacheManager = cacheManager; + } + + public void execute(JobExecutionContext context) throws JobExecutionException + { + try + { + if (logger.isDebugEnabled()) + { + execute(); + } + } + catch (Throwable e) + { + logger.error("Exception during execution of job", e); + } + } + + private void execute() throws Exception + { + if (cacheManager == null) + { + cacheManager = CacheManager.getInstance(); + } + + long maxHeapSize = Runtime.getRuntime().maxMemory(); + long totalSize = 0L; + // get all the caches + String[] cacheNames = cacheManager.getCacheNames(); + logger.debug("Dumping EHCache info:"); + for (String cacheName : cacheNames) + { + Cache cache = cacheManager.getCache(cacheName); + if (cache == null) // perhaps a temporary cache + { + continue; + } + // dump + CacheAnalysis analysis = new CacheAnalysis(cache); + logger.debug(analysis); + // get the size + totalSize += analysis.getSize(); + } + // check the size + double sizePercentage = (double)totalSize / (double)maxHeapSize * 100.0; + String msg = String.format( + "EHCaches currently consume %5.2f MB or %3.2f percent of system VM size", + (double)totalSize / 1024.0 / 1024.0, + sizePercentage); + logger.debug(msg); + } + + private static class CacheAnalysis + { + private Cache cache; + private long size = 0L; + + public CacheAnalysis(Cache cache) throws CacheException + { + this.cache = cache; + calculateSize(); + } + + public synchronized long getSize() + { + return size; + } + + @SuppressWarnings("unchecked") + private synchronized void calculateSize() throws CacheException + { + // calculate the cache deep size - EHCache 1.1 is always returning 0L + List keys = cache.getKeys(); + for (Serializable key : keys) + { + Element element = cache.get(key); + size += getSize(element); + } + } + + private long getSize(Serializable obj) + { + ByteArrayOutputStream bout = new ByteArrayOutputStream(1024); + ObjectOutputStream oos = null; + try + { + oos = new ObjectOutputStream(bout); + oos.writeObject(obj); + return bout.size(); + } + catch (IOException e) + { + logger.warn("Deep size calculation failed for cache: \n" + cache); + return 0L; + } + finally + { + try { oos.close(); } catch (IOException e) {} + } + } + + public String getStatusStr() + { + switch (cache.getStatus()) + { + case Cache.STATUS_ALIVE: + return "ALIVE"; + case Cache.STATUS_DISPOSED: + return "DISPOSED"; + case Cache.STATUS_UNINITIALISED: + return "UNINITIALIZED"; + default: + throw new AlfrescoRuntimeException("Unknown cache status: " + cache.getStatus()); + } + } + + public String toString() + { + double sizeMB = (double)getSize()/1024.0/1024.0; + long maxSize = cache.getMaxElementsInMemory(); + long currentSize = cache.getMemoryStoreSize(); + double percentageFull = (double)currentSize / (double)maxSize * 100.0; + double estMaxSize = sizeMB / (double) currentSize * (double) maxSize; + + StringBuilder sb = new StringBuilder(512); + sb.append(" Analyzing EHCache: \n") + .append("===> ").append(cache).append("\n") + .append(" Deep Size: ").append(String.format("%5.2f MB", sizeMB)).append("\n") + .append(" Percentage used: ").append(String.format("%5.2f percent", percentageFull)).append("\n") + .append(" Estimated maximum size: ").append(String.format("%5.2f MB", estMaxSize)); + return sb.toString(); + } + } +}