/kernel/src/main/java/org/neo4j/kernel/impl/cache/AdaptiveCacheManager.java

https://github.com/boyaxxx/community · Java · 377 lines · 314 code · 37 blank · 26 comment · 39 complexity · 9cf772f4c684225272745a265ebfd62c MD5 · raw file

  1. /**
  2. * Copyright (c) 2002-2011 "Neo Technology,"
  3. * Network Engine for Objects in Lund AB [http://neotechnology.com]
  4. *
  5. * This file is part of Neo4j.
  6. *
  7. * Neo4j is free software: you can redistribute it and/or modify
  8. * it under the terms of the GNU General Public License as published by
  9. * the Free Software Foundation, either version 3 of the License, or
  10. * (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  19. */
  20. package org.neo4j.kernel.impl.cache;
  21. import java.util.ArrayList;
  22. import java.util.Collection;
  23. import java.util.Iterator;
  24. import java.util.LinkedList;
  25. import java.util.List;
  26. import java.util.Map;
  27. import java.util.logging.Logger;
  28. public class AdaptiveCacheManager
  29. {
  30. private static final Logger log = Logger
  31. .getLogger( AdaptiveCacheManager.class.getName() );
  32. private float decreaseRatio = 1.15f;
  33. private float increaseRatio = 1.1f;
  34. private final List<AdaptiveCacheElement> caches =
  35. new LinkedList<AdaptiveCacheElement>();
  36. private final List<ReferenceCache<?,?>> referenceCaches =
  37. new ArrayList<ReferenceCache<?,?>>();
  38. private AdaptiveCacheWorker workerThread;
  39. public synchronized void registerCache( Cache<?,?> cache, float ratio,
  40. int minSize )
  41. {
  42. if ( cache == null || ratio >= 1 || ratio <= 0 || minSize < 0 )
  43. {
  44. throw new IllegalArgumentException( " cache=" + cache + " ratio ="
  45. + ratio + " minSize=" + minSize );
  46. }
  47. if ( cache instanceof ReferenceCache<?,?> )
  48. {
  49. referenceCaches.add( (ReferenceCache<?,?>) cache );
  50. return;
  51. }
  52. for ( AdaptiveCacheElement element : caches )
  53. {
  54. if ( element.getCache() == cache )
  55. {
  56. log.fine( "Cache[" + cache.getName() +
  57. "] already registered." );
  58. return;
  59. }
  60. }
  61. AdaptiveCacheElement element = new AdaptiveCacheElement( cache, ratio,
  62. minSize );
  63. caches.add( element );
  64. cache.setAdaptiveStatus( true );
  65. log.fine( "Cache[" + cache.getName() + "] threshold=" + ratio
  66. + "minSize=" + minSize + " registered." );
  67. }
  68. public synchronized void unregisterCache( Cache<?,?> cache )
  69. {
  70. if ( cache == null )
  71. {
  72. throw new IllegalArgumentException( "Null cache" );
  73. }
  74. if ( cache instanceof SoftLruCache<?,?> )
  75. {
  76. referenceCaches.remove( cache );
  77. return;
  78. }
  79. Iterator<AdaptiveCacheElement> itr = caches.iterator();
  80. while ( itr.hasNext() )
  81. {
  82. AdaptiveCacheElement element = itr.next();
  83. if ( element.getCache() == cache )
  84. {
  85. itr.remove();
  86. break;
  87. }
  88. }
  89. log.fine( "Cache[" + cache.getName() + "] removed." );
  90. }
  91. synchronized AdaptiveCacheElement getAdaptiveCacheElementIndex(
  92. Cache<?,?> cache )
  93. {
  94. for ( AdaptiveCacheElement element : caches )
  95. {
  96. if ( element.getCache() == cache )
  97. {
  98. return element;
  99. }
  100. }
  101. return null;
  102. }
  103. private void parseParams( Map<Object,Object> params )
  104. {
  105. if ( params == null )
  106. {
  107. return;
  108. }
  109. if ( params.containsKey( "adaptive_cache_worker_sleep_time" ) )
  110. {
  111. Object value = params.get( "adaptive_cache_worker_sleep_time" );
  112. int sleepTime = 3000;
  113. try
  114. {
  115. sleepTime = Integer.parseInt( (String) value );
  116. }
  117. catch ( NumberFormatException e )
  118. {
  119. log.warning(
  120. "Unable to parse apdaptive_cache_worker_sleep_time " +
  121. value );
  122. }
  123. workerThread.setSleepTime( sleepTime );
  124. }
  125. if ( params.containsKey( "adaptive_cache_manager_decrease_ratio" ) )
  126. {
  127. Object value = params.get(
  128. "adaptive_cache_manager_decrease_ratio" );
  129. try
  130. {
  131. decreaseRatio = Float.parseFloat( (String) value );
  132. }
  133. catch ( NumberFormatException e )
  134. {
  135. log.warning(
  136. "Unable to parse adaptive_cache_manager_decrease_ratio " +
  137. value );
  138. }
  139. if ( decreaseRatio < 1 )
  140. {
  141. decreaseRatio = 1.0f;
  142. }
  143. }
  144. if ( params.containsKey( "adaptive_cache_manager_increase_ratio" ) )
  145. {
  146. Object value = params.get(
  147. "adaptive_cache_manager_increase_ratio" );
  148. try
  149. {
  150. increaseRatio = Float.parseFloat( (String) value );
  151. }
  152. catch ( NumberFormatException e )
  153. {
  154. log.warning(
  155. "Unable to parse adaptive_cache_manager_increase_ratio " +
  156. value );
  157. }
  158. if ( increaseRatio < 1 )
  159. {
  160. increaseRatio = 1.0f;
  161. }
  162. }
  163. }
  164. public void start( Map<Object,Object> params )
  165. {
  166. workerThread = new AdaptiveCacheWorker();
  167. parseParams( params );
  168. workerThread.start();
  169. }
  170. public void stop()
  171. {
  172. workerThread.markDone();
  173. workerThread = null;
  174. }
  175. Collection<AdaptiveCacheElement> getCaches()
  176. {
  177. return caches;
  178. }
  179. private class AdaptiveCacheWorker extends Thread
  180. {
  181. private boolean done = false;
  182. private int sleepTime = 3000;
  183. AdaptiveCacheWorker()
  184. {
  185. super( "AdaptiveCacheWorker" );
  186. }
  187. void setSleepTime( int sleepTime )
  188. {
  189. this.sleepTime = sleepTime;
  190. }
  191. public synchronized void run()
  192. {
  193. while ( !done )
  194. {
  195. try
  196. {
  197. adaptReferenceCaches();
  198. adaptCaches();
  199. this.wait( sleepTime );
  200. }
  201. catch ( InterruptedException e )
  202. {
  203. Thread.interrupted();
  204. }
  205. }
  206. }
  207. void markDone()
  208. {
  209. done = true;
  210. }
  211. }
  212. public void adaptCaches()
  213. {
  214. List<AdaptiveCacheElement> copy =
  215. new LinkedList<AdaptiveCacheElement>();
  216. synchronized ( this )
  217. {
  218. copy.addAll( caches );
  219. }
  220. for ( AdaptiveCacheElement element : copy )
  221. {
  222. adaptCache( element );
  223. }
  224. }
  225. public synchronized void adaptReferenceCaches()
  226. {
  227. for ( ReferenceCache<?,?> cache : referenceCaches )
  228. {
  229. cache.pollClearedValues();
  230. }
  231. }
  232. public void adaptCache( Cache<?,?> cache )
  233. {
  234. if ( cache == null )
  235. {
  236. throw new IllegalArgumentException( "Null cache" );
  237. }
  238. AdaptiveCacheElement element = getAdaptiveCacheElementIndex( cache );
  239. if ( element != null )
  240. {
  241. adaptCache( element );
  242. }
  243. }
  244. private void adaptCache( AdaptiveCacheElement element )
  245. {
  246. long max = Runtime.getRuntime().maxMemory();
  247. long total = Runtime.getRuntime().totalMemory();
  248. long free = Runtime.getRuntime().freeMemory();
  249. float ratio = (float) (max - free) / max;
  250. float allocationRatio = (float) total / max;
  251. if ( allocationRatio < element.getRatio() )
  252. {
  253. // allocation ratio < 1 means JVM can still increase heap size
  254. // we won't decrease caches until element ratio is less
  255. // then allocationRatio
  256. ratio = 0;
  257. }
  258. if ( ratio > element.getRatio() )
  259. {
  260. // decrease cache size
  261. // after decrease we resize again with +1000 to avoid
  262. // spam of this method
  263. Cache<?,?> cache = element.getCache();
  264. int newCacheSize = (int) ( cache.maxSize() / decreaseRatio /
  265. (1 + (ratio - element.getRatio())) );
  266. int minSize = element.minSize();
  267. if ( newCacheSize < minSize )
  268. {
  269. log.fine( "Cache[" + cache.getName() +
  270. "] cannot decrease under " + minSize +
  271. " (allocation ratio=" + allocationRatio +
  272. " threshold status=" + ratio + ")" );
  273. cache.resize( minSize );
  274. cache.resize( minSize + 1000 );
  275. return;
  276. }
  277. if ( newCacheSize + 1200 > cache.size() )
  278. {
  279. if ( cache.size() - 1200 >= minSize )
  280. {
  281. newCacheSize = cache.size() - 1200;
  282. }
  283. else
  284. {
  285. newCacheSize = minSize;
  286. }
  287. }
  288. log.fine( "Cache[" + cache.getName() + "] decreasing from " +
  289. cache.size() + " to " + newCacheSize + " (allocation ratio=" +
  290. allocationRatio + " threshold status=" + ratio + ")" );
  291. if ( newCacheSize <= 1000 )
  292. {
  293. cache.clear();
  294. }
  295. else
  296. {
  297. cache.resize( newCacheSize );
  298. }
  299. cache.resize( newCacheSize + 1000 );
  300. }
  301. else
  302. {
  303. // increase cache size
  304. Cache<?,?> cache = element.getCache();
  305. if ( cache.size() / (float) cache.maxSize() < 0.9f )
  306. {
  307. return;
  308. }
  309. int newCacheSize = (int) (cache.maxSize() * increaseRatio);
  310. log.fine( "Cache[" + cache.getName() + "] increasing from " +
  311. cache.size() + " to " + newCacheSize + " (allocation ratio=" +
  312. allocationRatio + " threshold status=" + ratio + ")" );
  313. cache.resize( newCacheSize );
  314. }
  315. }
  316. private static class AdaptiveCacheElement
  317. {
  318. private final Cache<?,?> cache;
  319. private final float ratio;
  320. private final int minSize;
  321. AdaptiveCacheElement( Cache<?,?> cache, float ratio, int minSize )
  322. {
  323. this.cache = cache;
  324. this.ratio = ratio;
  325. this.minSize = minSize;
  326. }
  327. Cache<?,?> getCache()
  328. {
  329. return cache;
  330. }
  331. float getRatio()
  332. {
  333. return ratio;
  334. }
  335. int minSize()
  336. {
  337. return minSize;
  338. }
  339. }
  340. }