001package org.cache2k.impl; 002 003/* 004 * #%L 005 * cache2k core package 006 * %% 007 * Copyright (C) 2000 - 2015 headissue GmbH, Munich 008 * %% 009 * This program is free software: you can redistribute it and/or modify 010 * it under the terms of the GNU General Public License as 011 * published by the Free Software Foundation, either version 3 of the 012 * License, or (at your option) any later version. 013 * 014 * This program is distributed in the hope that it will be useful, 015 * but WITHOUT ANY WARRANTY; without even the implied warranty of 016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 017 * GNU General Public License for more details. 018 * 019 * You should have received a copy of the GNU General Public 020 * License along with this program. If not, see 021 * <http://www.gnu.org/licenses/gpl-3.0.html>. 022 * #L% 023 */ 024 025import org.apache.commons.logging.Log; 026import org.apache.commons.logging.LogFactory; 027 028import java.util.Random; 029 030/** 031 * @author Jens Wilke; created: 2013-07-18 032 */ 033public class CacheSizeEstimator { 034 035 final static Log log = LogFactory.getLog(CacheSizeEstimator.class); 036 037 final int MIN_ENTRY_COUNT = 3; 038 final int ADDED_ENTRY_COUNT = 27; 039 final int DEPTH_COUNT = 12; 040 041 int accuracy; 042 Random random; 043 Entry lastEntry; 044 Entry[] hash1; 045 Entry[] hash2; 046 047 final void switchHash() { 048 Entry[] tmp = hash1; 049 hash1 = hash2; 050 hash2 = tmp; 051 } 052 053 final Entry nextEntry() { 054 if (lastEntry.another != null) { 055 return lastEntry = lastEntry.another; 056 } 057 int idx = Hash.index(hash1, lastEntry.hashCode); 058 Entry e; 059 do { 060 idx++; 061 if (idx >= hash1.length) { 062 idx = 0; 063 064 } 065 e = hash1[idx]; 066 } while (e == null); 067 return lastEntry = e; 068 } 069 070 final void findAnyEntry() { 071 int idx = 0; 072 while (hash1[idx] != null) { 073 idx++; 074 if (idx >= hash1.length) { 075 idx = 0; 076 switchHash(); 077 } 078 } 079 lastEntry = hash1[idx]; 080 } 081 082 final void randomizeStartEntry() { 083 findAnyEntry(); 084 int _forward = random.nextInt(hash1.length + hash2.length); 085 for (;_forward != 0; _forward--) { 086 nextEntry(); 087 } 088 } 089 090 final void addEntries(DepthSearchAndSizeCounter _counter, int _count) { 091 for (int i = 0; i < _count; i++) { 092 _counter.insert(nextEntry().value); 093 _counter.insert(nextEntry().key); 094 } 095 } 096 097 final int getSizeEstimationForAnEntry() { 098 try { 099 DepthSearchAndSizeCounter cse = new DepthSearchAndSizeCounter(); 100 addEntries(cse, ADDED_ENTRY_COUNT); 101 for (int i = 0; (i < DEPTH_COUNT) && cse.hasNext(); i++) { 102 cse.descend(); 103 if (log.isDebugEnabled()) { 104 log.debug("CSE: depth=" + i + ", counter=" + cse.getCounter() + ", objectCount=" + cse.getObjectCount() + ", memUsage=" + cse.getByteCount() + ", nextCnt=" + cse.next.size()); 105 } 106 } 107 accuracy = 108 (cse.hasCircles() ? 1 : 0) + 109 (cse.hasCommonObjects() ? 2 : 0) + // this may apply quite often, e.g. for strings, locale etc. 110 (cse.hasNext() ? 4 : 0); 111 return cse.getByteCount() / ADDED_ENTRY_COUNT; 112 113 } catch (DepthSearchAndSizeCounter.EstimationException ex) { 114 StringBuilder sb = new StringBuilder(); 115 sb.append("Problems descending object tree for size estimation, path: "); 116 for (Class<?> c : ex.getPath()) { 117 sb.append(" -> "); 118 sb.append(c.getSimpleName()); 119 } 120 log.warn(sb.toString(), ex.getCause()); 121 accuracy = 8; 122 return 0; 123 } 124 } 125 126 final int getAccuracy() { 127 return accuracy; 128 } 129 130}