1 21 22 package org.apache.derby.impl.services.cache; 23 24 import org.apache.derby.iapi.services.cache.CacheManager; 25 import org.apache.derby.iapi.services.cache.Cacheable; 26 import org.apache.derby.iapi.services.cache.CacheableFactory; 27 import org.apache.derby.iapi.services.cache.SizedCacheable; 28 import org.apache.derby.iapi.services.context.ContextManager; 29 import org.apache.derby.iapi.services.daemon.DaemonService; 30 import org.apache.derby.iapi.services.daemon.Serviceable; 31 32 import org.apache.derby.iapi.error.StandardException; 33 34 import org.apache.derby.iapi.services.monitor.Monitor; 35 36 import org.apache.derby.iapi.services.sanity.SanityManager; 37 38 import org.apache.derby.iapi.services.cache.ClassSize; 39 import org.apache.derby.iapi.util.Matchable; 40 import org.apache.derby.iapi.util.Operator; 41 import org.apache.derby.iapi.reference.SQLState; 42 43 import java.util.ArrayList ; 44 import java.util.Hashtable ; 45 import java.util.Properties ; 46 47 48 99 100 final class Clock extends Hashtable implements CacheManager, Serviceable { 101 102 105 public final CacheStat stat; 106 private DaemonService cleaner; private final ArrayList holders; 109 private int validItemCount = 0; 110 private long maximumSize; 111 private boolean useByteCount; private long currentByteCount = 0; 113 117 118 private static final int ITEM_OVERHEAD = ClassSize.estimateBaseFromCatalog( CachedItem.class) 119 + ClassSize.getRefSize() + ClassSize.estimateHashEntrySize(); 121 122 private final CacheableFactory holderFactory; 123 124 private boolean active; private String name; private int clockHand; 128 129 private int myClientNumber; private boolean wokenToClean; private boolean cleanerRunning; 132 private boolean needService; 133 134 149 Clock(CacheableFactory holderFactory, 150 String name, 151 int initialSize, 152 long maximumSize, 153 boolean useByteCount) 154 { 155 super(initialSize, (float) 0.95); 156 this.maximumSize = maximumSize; 157 this.holderFactory = holderFactory; 158 this.useByteCount = useByteCount; 159 160 if (SanityManager.DEBUG) { 161 if (SanityManager.DEBUG_ON(ClockFactory.CacheTrace)) { 162 SanityManager.DEBUG(ClockFactory.CacheTrace, "initializing " + name + " cache to size " + initialSize); 163 } 164 } 165 166 170 holders = new ArrayList (initialSize); 171 this.name = name; 172 active = true; 173 174 this.stat = new CacheStat(); 175 stat.initialSize = initialSize; 176 stat.maxSize = maximumSize; 177 } 178 179 204 public Cacheable find(Object key) throws StandardException { 205 CachedItem item; 206 boolean add; 207 208 218 while (true) 219 { 220 add = false; 221 222 synchronized (this) { 223 224 if (!active) 225 return null; 226 227 item = (CachedItem) get(key); 228 229 if (item != null) { 230 item.keepAfterSearch(); 231 232 stat.findHit++; 233 234 if (SanityManager.DEBUG) { 235 if (SanityManager.DEBUG_ON(ClockFactory.CacheTrace)) 236 { 237 SanityManager.DEBUG(ClockFactory.CacheTrace, name + ": Found key " + 238 key + " already in cache, item " + item); 239 } 240 } 241 } 242 } 243 244 if (item == null) { 246 247 item = findFreeItem(); 249 250 stat.findMiss++; 251 252 if (SanityManager.DEBUG) { 253 if (SanityManager.DEBUG_ON(ClockFactory.CacheTrace)) 254 { 255 SanityManager.DEBUG(ClockFactory.CacheTrace, name + ": Find key " + 256 key + " Not in cache, get free item " + item); 257 } 258 } 259 260 261 if (SanityManager.DEBUG) 262 SanityManager.ASSERT(item != null, "found null item"); 263 264 synchronized (this) { 265 CachedItem inCacheItem = (CachedItem) get(key); 266 267 if (inCacheItem != null) { 268 item.unkeepForCreate(); 271 272 item = inCacheItem; 273 item.keepAfterSearch(); 274 } else { 275 put(key, item); 277 add = true; 278 if (SanityManager.DEBUG) { 279 280 if (SanityManager.DEBUG_ON("memoryLeakTrace")) { 281 282 if (size() > ((11 * maximumSize) / 10)) 283 System.out.println("memoryLeakTrace:Cache:" + name + " " + size()); 284 } 285 } 286 } 287 } 288 } 289 290 if (add) { 291 292 if (SanityManager.DEBUG) { 293 if (SanityManager.DEBUG_ON(ClockFactory.CacheTrace)) 294 { 295 SanityManager.DEBUG(ClockFactory.CacheTrace, name + " Added " + 296 key + " to cache, item " + item); 297 } 298 } 299 300 stat.findFault++; 301 302 return addEntry(item, key, false, (Object ) null); 303 } 304 305 Cacheable entry = item.use(); 306 if (entry == null) { 307 synchronized (this) { 309 item.unkeep(); 310 } 311 312 continue; 315 } 316 317 return entry; 318 } 319 } 320 321 322 333 334 public Cacheable findCached(Object key) throws StandardException { 335 336 337 CachedItem item; 338 339 synchronized (this) { 340 341 if (!active) 342 return null; 343 344 item = (CachedItem) get(key); 345 346 if (item == null) { 347 stat.findCachedMiss++; 348 return null; 349 } else 350 stat.findCachedHit++; 351 352 item.keepAfterSearch(); 353 } 354 355 Cacheable entry = item.use(); 356 if (entry == null) { 357 synchronized (this) { 359 item.unkeep(); 360 } 361 } 362 363 return entry; 364 } 365 366 367 373 public void setUsed( Object [] keys) 374 { 375 CachedItem item; 376 377 for( int i = 0; i < keys.length;) 378 { 379 synchronized (this) 381 { 382 if (!active) 383 return; 384 385 int endIdx = i + 32; 386 if( endIdx > keys.length) 387 endIdx = keys.length; 388 for( ; i < endIdx; i++) 389 { 390 if( keys[i] == null) 391 return; 392 393 item = (CachedItem) get(keys[i]); 394 if( null != item) 395 item.setUsed( true); 396 } 397 } 398 } 399 } 401 414 public Cacheable create(Object key, Object createParameter) throws StandardException { 415 416 CachedItem item = findFreeItem(); 418 419 stat.create++; 420 421 synchronized (this) { 422 423 if (!active) 424 return null; 425 426 if (get(key) != null) { 427 428 item.unkeepForCreate(); 429 430 throw StandardException.newException(SQLState.OBJECT_EXISTS_IN_CACHE, this.name, key); 431 } 432 433 put(key, item); 434 435 if (SanityManager.DEBUG) { 436 437 if (SanityManager.DEBUG_ON("memoryLeakTrace")) { 438 439 if (size() > ((11 * maximumSize) / 10)) 440 System.out.println("memoryLeakTrace:Cache:" + name + " " + size()); 441 } 442 } 443 } 444 445 Cacheable entry = addEntry(item, key, true, createParameter); 446 447 if (SanityManager.DEBUG) { 448 if (entry != null) 449 SanityManager.ASSERT(item.getEntry() == entry); 450 } 451 452 return entry; 453 } 454 455 456 477 public void release(Cacheable entry) { 478 boolean removeItem; 479 CachedItem item; 480 long toShrink = 0; 481 482 synchronized (this) { 483 484 item = (CachedItem) get(entry.getIdentity()); 485 486 if (SanityManager.DEBUG) { 487 SanityManager.ASSERT(item != null, "item null"); 488 SanityManager.ASSERT(item.getEntry() == entry, "entry not equals keyed entry"); 489 SanityManager.ASSERT(item.isKept(), "item is not kept in release(Cachable)"); 490 } 491 492 removeItem = item.unkeep(); 493 494 if (removeItem) { 495 496 remove(entry.getIdentity()); 497 498 item.keepForClean(); 501 } 502 503 if (cleaner == null) { 504 toShrink = shrinkSize( getCurrentSize()); 506 } 507 } 508 509 if (removeItem) { 510 511 item.notifyRemover(); 512 } 513 514 if (toShrink > 0) 515 performWork(true ); 516 } 517 518 protected void release(CachedItem item) { 519 520 boolean removeItem; 521 522 synchronized (this) { 523 524 if (SanityManager.DEBUG) { 525 SanityManager.ASSERT(item.isKept(), "item is not kept in released(CachedItem)"); 526 } 527 528 removeItem = item.unkeep(); 529 530 if (removeItem) { 531 532 remove(item.getEntry().getIdentity()); 533 534 item.keepForClean(); 537 } 538 } 539 540 if (removeItem) { 541 542 item.notifyRemover(); 543 } 544 } 545 546 559 public void remove(Cacheable entry) throws StandardException { 560 561 boolean removeNow; 562 CachedItem item; 563 long origItemSize = 0; 564 565 stat.remove++; 566 567 synchronized (this) { 568 569 570 571 item = (CachedItem) get(entry.getIdentity()); 572 573 if (SanityManager.DEBUG) { 574 SanityManager.ASSERT(item != null); 575 SanityManager.ASSERT(item.getEntry() == entry); 576 SanityManager.ASSERT(item.isKept()); 577 } 578 if( useByteCount) 579 origItemSize = getItemSize( item); 580 581 item.setRemoveState(); 582 removeNow = item.unkeep(); 583 584 if (removeNow) { 585 remove(entry.getIdentity()); 586 item.keepForClean(); 587 } 588 } 589 590 try { 591 item.remove(removeNow); 593 594 } finally { 595 596 synchronized (this) 597 { 598 item.unkeep(); 601 item.setValidState(false); 602 validItemCount--; 603 item.getEntry().clearIdentity(); 604 if( useByteCount) 605 currentByteCount += getItemSize( item) - origItemSize; 606 } 607 } 608 609 } 610 611 614 public void cleanAll() throws StandardException { 615 stat.cleanAll++; 616 cleanCache((Matchable) null); 617 } 618 619 622 public void clean(Matchable partialKey) throws StandardException { 623 624 cleanCache(partialKey); 625 } 626 627 634 public void ageOut() { 635 636 stat.ageOut++; 637 synchronized (this) { 638 639 int size = holders.size(); 640 long toShrink = shrinkSize( getCurrentSize()); 641 boolean shrunk = false; 642 643 for (int position = 0; position < size; position++) { 644 CachedItem item = (CachedItem) holders.get(position); 645 646 if (item.isKept()) 647 continue; 648 if (!item.isValid()) 649 continue; 650 651 if (item.getEntry().isDirty()) { 652 continue; 653 } 654 655 long itemSize = removeIdentity(item); 656 657 if (toShrink > 0) { 658 659 if (SanityManager.DEBUG) { 660 if (SanityManager.DEBUG_ON(ClockFactory.CacheTrace)) { 661 SanityManager.DEBUG(ClockFactory.CacheTrace, name + 662 " shrinking item " + item + " at position " + position); 663 } 664 } 665 666 toShrink -= itemSize; 667 shrunk = true; 668 } 669 670 } 672 if (shrunk) 673 trimToSize(); 674 675 } } 678 683 public void shutdown() throws StandardException { 684 685 if (cleaner != null) { 686 cleaner.unsubscribe(myClientNumber); 687 cleaner = null; 688 } 689 690 synchronized (this) { 691 active = false; 692 } 693 694 ageOut(); 695 cleanAll(); 696 ageOut(); 697 } 698 699 704 public void useDaemonService(DaemonService daemon) 705 { 706 if (cleaner != null) 708 cleaner.unsubscribe(myClientNumber); 709 710 cleaner = daemon; 711 myClientNumber = cleaner.subscribe(this, true ); 712 } 713 718 public boolean discard(Matchable partialKey) { 719 720 boolean noMisses = true; 722 723 synchronized (this) { 724 725 int size = holders.size(); 726 long toShrink = shrinkSize( getCurrentSize()); 727 boolean shrunk = false; 728 729 for (int position = 0; position < size; position++) { 730 CachedItem item = (CachedItem) holders.get(position); 731 732 if (!item.isValid()) 733 continue; 734 735 Object key = item.getEntry().getIdentity(); 736 737 if (partialKey != null && !partialKey.match(key)) 738 continue; 739 740 if (item.isKept()) 741 { 742 noMisses = false; 743 continue; 744 } 745 746 long itemSize = removeIdentity(item); 747 748 if (toShrink > 0) { 749 750 if (SanityManager.DEBUG) { 751 if (SanityManager.DEBUG_ON(ClockFactory.CacheTrace)) { 752 SanityManager.DEBUG(ClockFactory.CacheTrace, name + 753 " shrinking item " + item + " at position " + position); 754 } 755 } 756 757 toShrink -= itemSize; 759 shrunk = true; 760 } 761 } 762 763 if (shrunk) 764 trimToSize(); 765 } 766 767 return noMisses; 768 } 769 770 778 private Cacheable addEntry(CachedItem item, Object key, boolean forCreate, Object createParameter) 779 throws StandardException { 780 781 Cacheable entry = null; 782 long origEntrySize = 0; 783 if( useByteCount) 784 origEntrySize = getItemSize( item); 785 786 try 787 { 788 if (SanityManager.DEBUG) { 789 if (SanityManager.DEBUG_ON(ClockFactory.CacheTrace)) 790 { 791 SanityManager.DEBUG(ClockFactory.CacheTrace, name + 792 " item " + item + " take on identity " + key); 793 } 794 } 795 796 entry = item.takeOnIdentity(this, holderFactory, key, forCreate, createParameter); 798 } 799 finally 800 { 801 boolean notifyWaiters; 802 synchronized (this) { 803 804 Object removed = remove(key); 805 if (SanityManager.DEBUG) { 806 SanityManager.ASSERT(removed == item); 807 } 808 809 if (entry != null) { 810 put(entry.getIdentity(), item); 814 if( useByteCount) 815 currentByteCount += ((SizedCacheable) entry).getSize() - origEntrySize; 816 item.setValidState(true); 817 validItemCount++; 818 notifyWaiters = true; 819 } else { 820 item.unkeep(); 821 notifyWaiters = item.isKept(); 822 } 823 } 824 825 if (notifyWaiters) 827 item.settingIdentityComplete(); 828 } 829 830 return entry; 831 } 832 833 834 protected CachedItem findFreeItem() throws StandardException { 835 836 840 long currentSize = getCurrentSize(); 841 842 843 if (currentSize >= maximumSize) { 844 CachedItem item = rotateClock(0.2f); 846 if (item != null) 847 return item; 848 } 849 850 857 858 if (validItemCount < holders.size()) { 861 862 synchronized (this) { 863 864 870 int invalidItems = holders.size() - validItemCount; 871 872 878 884 for (int i = holders.size() - 1; (invalidItems > 0) && (i >= 0) ; i--) { 885 CachedItem item = (CachedItem) holders.get(i); 886 887 if (item.isKept()) { 888 if (!item.isValid()) invalidItems--; 889 continue; 890 } 891 892 if (!item.isValid()) { 894 item.keepForCreate(); 895 return item; 896 } 897 } 898 } 899 } 900 901 902 return growCache(); 903 } 904 905 911 protected CachedItem rotateClock(float percentOfClock) throws StandardException 912 { 913 int evictions = 0; 915 int cleaned = 0; 916 int resetUsed = 0; 917 int iskept = 0; 918 919 CachedItem availableItem = null; 924 925 boolean kickCleaner = false; 926 927 try { 928 929 930 int itemCount = holders.size(); 932 int itemsToCheck; 933 if (itemCount < 20) 934 itemsToCheck = 2 * itemCount; 935 else 936 itemsToCheck = (int) (((float) itemCount) * percentOfClock); 937 938 939 long toShrink = shrinkSize(getCurrentSize()); 942 943 restartClock: 944 for (; itemsToCheck > 0;) { 945 946 CachedItem item = null; 947 948 synchronized (this) { 949 950 if (SanityManager.DEBUG) { 951 if (SanityManager.DEBUG_ON(ClockFactory.CacheTrace)) 952 { 953 SanityManager.DEBUG(ClockFactory.CacheTrace, name + " rotateClock starting " + 954 clockHand + " itemsToCheck " + itemsToCheck); 955 } 956 } 957 958 int size = holders.size(); 960 for (; itemsToCheck > 0; item = null, itemsToCheck--, incrClockHand()) 961 { 962 980 981 982 if (clockHand >= size) { 983 if (size == 0) 984 break; 985 clockHand = 0; 986 } 987 988 item = (CachedItem) holders.get(clockHand); 989 990 if (item.isKept()) 991 { 992 if (SanityManager.DEBUG) iskept++; 994 continue; 995 } 996 997 if (!item.isValid()) { 999 if( null != availableItem) 1000 continue; 1002 if (SanityManager.DEBUG) { 1003 if (SanityManager.DEBUG_ON(ClockFactory.CacheTrace)) 1004 { 1005 SanityManager.DEBUG(ClockFactory.CacheTrace, 1006 name + " found free item at " + clockHand + " item " + item); 1007 } 1008 } 1009 1010 item.keepForCreate(); 1011 if( useByteCount && getCurrentSize() > maximumSize) 1012 { 1013 availableItem = item; 1014 continue; 1016 } 1017 incrClockHand(); 1019 1020 return item; 1021 } 1022 1023 if (item.recentlyUsed()) 1024 { 1025 1026 if (SanityManager.DEBUG) resetUsed++; 1028 item.setUsed(false); 1029 continue; 1030 } 1031 1032 1033 if (toShrink > 0) { 1034 if (!cleanerRunning) { 1035 1036 kickCleaner = true; 1038 cleanerRunning = true; 1039 needService = true; 1040 } 1041 } 1042 1043 if (SanityManager.DEBUG) { 1045 evictions++; 1046 1047 if (SanityManager.DEBUG_ON(ClockFactory.CacheTrace)) 1048 { 1049 SanityManager.DEBUG(ClockFactory.CacheTrace, 1050 name + " evicting item at " + 1051 clockHand + " item " + item); 1052 } 1053 } 1054 1055 if (!item.getEntry().isDirty()) { 1056 1057 if (SanityManager.DEBUG) { 1058 if (SanityManager.DEBUG_ON(ClockFactory.CacheTrace)) 1059 { 1060 SanityManager.DEBUG(ClockFactory.CacheTrace, 1061 name + " Evicting Item " + 1062 item + ", not dirty"); 1063 } 1064 } 1065 1066 long itemSize = removeIdentity(item); 1069 1070 if( useByteCount) 1071 { 1072 toShrink -= itemSize; 1073 if( getCurrentSize() > maximumSize && 0 < toShrink) 1074 { 1075 if( null == availableItem) 1076 { 1077 item.keepForCreate(); 1078 availableItem = item; 1079 } 1080 continue; 1081 } 1082 } 1083 incrClockHand(); 1085 1086 if( null != availableItem) 1087 return availableItem; 1088 1089 item.keepForCreate(); 1091 return item; 1092 } 1093 if ((cleaner != null) && !cleanerRunning) { 1095 kickCleaner = true; 1096 wokenToClean = true; 1097 cleanerRunning = true; } 1099 item.keepForClean(); 1100 1101 break; 1106 } 1107 if (item == null) { 1108 return availableItem; 1109 } 1110 1111 } 1113 try 1115 { 1116 if ( SanityManager.DEBUG) { 1117 if (SanityManager.DEBUG_ON(ClockFactory.CacheTrace)) { 1118 SanityManager.DEBUG(ClockFactory.CacheTrace,name + " cleaning item " + item); 1119 } 1120 } 1121 1122 item.clean(false); 1123 1124 if (SanityManager.DEBUG) { 1126 cleaned++; 1127 } 1128 } 1129 finally { 1130 release(item); 1131 item = null; 1132 } 1133 1134 continue restartClock; 1137 } 1138 return availableItem; 1139 } finally { 1140 1141 1142 if (SanityManager.DEBUG) 1143 { 1144 if ( 1146 SanityManager.DEBUG_ON(ClockFactory.CacheTrace)) 1147 SanityManager.DEBUG(ClockFactory.CacheTrace, name + " evictions " + evictions + 1148 ", cleaned " + cleaned + 1149 ", resetUsed " + resetUsed + 1150 ", isKept " + iskept + 1151 ", size " + holders.size()); 1152 } 1153 1154 if (kickCleaner && (cleaner != null)) 1155 { 1156 if (SanityManager.DEBUG) { 1157 if (SanityManager.DEBUG_ON(DaemonService.DaemonTrace)) { 1158 SanityManager.DEBUG(DaemonService.DaemonTrace, name + " client # " + myClientNumber + " calling cleaner "); 1159 } 1160 } 1161 1162 cleaner.serviceNow(myClientNumber); 1163 1164 if (SanityManager.DEBUG) { 1165 if (SanityManager.DEBUG_ON(DaemonService.DaemonTrace)) { 1166 SanityManager.DEBUG(DaemonService.DaemonTrace, name + Thread.currentThread().getName() + " cleaner called"); 1167 } 1168 } 1169 } 1170 } 1171 } 1173 1176 private int incrClockHand() 1177 { 1178 if (++clockHand >= holders.size()) 1179 clockHand = 0; 1180 return clockHand; 1181 } 1182 1183 1186 1187 public int performWork(ContextManager contextMgr ) { 1188 1189 int ret = performWork(false); 1190 synchronized (this) { 1191 cleanerRunning = false; 1192 } 1193 return ret; 1194 } 1195 1196 1197 1200 public boolean serviceASAP() 1201 { 1202 return needService; 1203 } 1204 1205 1206 public boolean serviceImmediately() 1208 { 1209 return false; 1210 } 1211 1212 1213 public synchronized int getNumberInUse() { 1214 1215 int size = holders.size(); 1216 int inUse = 0; 1217 1218 for (int position = 0; position < size; position++) { 1219 1220 CachedItem item = (CachedItem) holders.get(position); 1221 1222 if (item.isValid()) { 1223 inUse++; 1224 } 1225 1226 } 1227 return inUse; 1228 } 1229 1250 1251 1256 1257 private CachedItem growCache() { 1258 1259 CachedItem item = new CachedItem(); 1260 item.keepForCreate(); 1261 1262 synchronized (this) { 1266 holders.add(item); 1267 } 1269 1270 return item; 1271 } 1272 1273 1274 1283 protected long removeIdentity(CachedItem item) { 1284 1285 long shrink = 1; 1286 1287 if (SanityManager.DEBUG) { 1288 SanityManager.ASSERT(!item.isKept(), "item is kept"); 1289 SanityManager.ASSERT(item.isValid(), "item is not valid"); 1290 1291 } 1292 1293 if( useByteCount) 1294 shrink = ((SizedCacheable) item.getEntry()).getSize(); 1295 remove(item.getEntry().getIdentity()); 1296 item.setValidState(false); 1297 validItemCount--; 1298 item.getEntry().clearIdentity(); 1299 if( useByteCount) 1300 { 1301 shrink -= ((SizedCacheable) item.getEntry()).getSize(); 1302 currentByteCount -= shrink; 1303 } 1304 return shrink; 1305 } 1306 1307 1315 protected void cleanCache(Matchable partialKey) throws StandardException { 1316 1317 int position; 1318 1319 synchronized(this) 1320 { 1321 position = holders.size() - 1; 1324 } 1325 1326 1327outerscan: 1328 for (;;) { 1329 1330 CachedItem item = null; 1331 1332 synchronized (this) { 1333 1334 int size = holders.size(); 1337 if (position >= size) 1338 position = size - 1; 1339 1340innerscan: 1341 for ( ; position >= 0; position--, item = null) { 1347 1348 item = (CachedItem) holders.get(position); 1349 1350 if (!item.isValid()) 1351 continue innerscan; 1352 1353 if (!item.getEntry().isDirty()) 1354 continue innerscan; 1355 1356 if (partialKey != null) { 1357 1358 Object key = item.getEntry().getIdentity(); 1359 1360 if (!partialKey.match(key)) 1361 continue; 1362 } 1363 1364 item.keepForClean(); 1365 break innerscan; 1366 } 1367 } 1369 if (position < 0) 1370 { 1371 return; 1372 } 1373 1374 try { 1375 1376 item.clean(false); 1377 } finally { 1378 release(item); 1379 } 1380 position--; 1381 1382 } } 1384 1385 1386 protected long shrinkSize(long currentSize) { 1387 1388 long maxSize = getMaximumSize(); 1389 1390 long toShrink = currentSize - maxSize; 1391 if (toShrink <= 0) 1392 return 0; 1393 1394 long shrinkLimit = maxSize / 10; 1396 if (shrinkLimit == 0) 1397 shrinkLimit = 2; 1398 1399 if (toShrink < shrinkLimit) 1400 return toShrink; 1401 else 1402 return shrinkLimit; 1403 } 1404 1405 1426 protected int performWork(boolean shrinkOnly) 1427 { 1428 long target; 1429 long toShrink; 1430 int maxLooks; 1431 1432 synchronized(this) 1433 { 1434 if (!active) { 1435 needService = false; 1436 return Serviceable.DONE; 1437 } 1438 else { 1439 long currentSize = getCurrentSize(); 1440 target = currentSize / 20; toShrink = wokenToClean ? 0 : shrinkSize(currentSize); 1442 } 1443 1444 if (target == 0) { 1445 wokenToClean = false; 1446 needService = false; 1447 return Serviceable.DONE; 1448 } 1449 1450 if (!wokenToClean && (toShrink <= 0)) { 1451 needService = false; 1452 return Serviceable.DONE; 1453 } 1454 1455 maxLooks = useByteCount ? (holders.size()/10) : (int) (target * 2); 1456 } 1457 1458 long clean = 0; 1460 int cleaned = 0; CachedItem item = null; 1462 int currentPosition = 0; 1463 1464 String ThreadName = null; 1465 1466 if (SanityManager.DEBUG) { 1467 if (SanityManager.DEBUG_ON(DaemonService.DaemonTrace)) 1468 { 1469 ThreadName = Thread.currentThread().getName(); 1470 SanityManager.DEBUG(DaemonService.DaemonTrace, ThreadName + " Cleaning " + name + " clientNumber " + myClientNumber); 1471 } 1472 } 1473 1474 1475 synchronized(this) 1476 { 1477 int itemCount = holders.size(); 1478 currentPosition = clockHand; 1479 1480 boolean shrunk = false; 1482 long currentSize = getCurrentSize(); 1483 1484 for (; shrinkOnly ? (currentSize > maximumSize && toShrink > 0) : (clean < target); item = null) 1485 { 1486 if (++currentPosition >= itemCount) { 1487 if (itemCount == 0) 1488 break; 1489 1490 currentPosition = 0; 1491 1492 } 1493 1494 if (maxLooks-- <= 0) 1495 { 1496 if (SanityManager.DEBUG) { 1497 if (SanityManager.DEBUG_ON(DaemonService.DaemonTrace)) { 1498 SanityManager.DEBUG(DaemonService.DaemonTrace, ThreadName + " done one round of " + name); 1499 } 1500 } 1501 1502 break; } 1504 1505 item = (CachedItem) holders.get(currentPosition); 1506 1507 if (item.isKept()) 1508 continue; 1509 1510 if (!item.isValid()) 1511 { 1512 if (toShrink > 0) { 1513 1514 if (SanityManager.DEBUG) { 1515 if (SanityManager.DEBUG_ON(ClockFactory.CacheTrace)) { 1516 SanityManager.DEBUG(ClockFactory.CacheTrace, name + 1517 " shrinking item " + item + " at position " + currentPosition); 1518 } 1519 } 1520 1521 toShrink -= currentSize; 1522 holders.remove(currentPosition); 1523 if( useByteCount) 1524 currentByteCount -= getItemSize( item); 1525 currentSize = getCurrentSize(); 1526 toShrink += currentSize; 1527 itemCount--; 1528 1529 currentPosition--; 1531 1532 shrunk = true; 1533 } 1534 continue; 1535 } 1536 1537 if (item.recentlyUsed()) 1538 continue; 1539 1540 int itemSize = getItemSize( item); 1543 clean += itemSize; 1544 if (!item.getEntry().isDirty()) { 1545 1546 if (toShrink > 0) { 1547 if (SanityManager.DEBUG) { 1548 if (SanityManager.DEBUG_ON(ClockFactory.CacheTrace)) { 1549 SanityManager.DEBUG(ClockFactory.CacheTrace, name + 1550 " shrinking item " + item + " at position " + currentPosition); 1551 } 1552 } 1553 1554 toShrink -= currentSize; 1555 removeIdentity(item); 1556 holders.remove(currentPosition); 1557 if( useByteCount) 1558 currentByteCount -= getItemSize( item); 1559 currentSize = getCurrentSize(); 1560 toShrink += currentSize; 1561 itemCount--; 1562 shrunk = true; 1563 1564 currentPosition--; 1566 } 1567 continue; 1568 } 1569 1570 if (shrinkOnly) 1571 continue; 1572 1573 item.keepForClean(); 1575 break; 1576 } 1578 if (shrunk) 1579 trimToSize(); 1580 1581 if (item == null) { 1582 wokenToClean = false; 1583 needService = false; 1584 return Serviceable.DONE; 1585 } 1586 } 1588 try 1589 { 1590 if (SanityManager.DEBUG) { 1591 if (SanityManager.DEBUG_ON(DaemonService.DaemonTrace)) { 1592 SanityManager.DEBUG(DaemonService.DaemonTrace, ThreadName + " cleaning entry in " + name); 1593 } 1594 } 1595 1596 item.clean(false); 1597 if (SanityManager.DEBUG) cleaned++; 1599 1600 } catch (StandardException se) { 1601 } 1603 finally 1604 { 1605 release(item); 1606 item = null; 1607 } 1608 1609 if (SanityManager.DEBUG) { 1610 if (SanityManager.DEBUG_ON(DaemonService.DaemonTrace)) { 1611 SanityManager.DEBUG(DaemonService.DaemonTrace, ThreadName + " Found " + clean + " clean items, cleaned " + 1612 cleaned + " items in " + name ); 1613 } 1614 } 1615 1616 needService = true; 1617 return Serviceable.REQUEUE; } 1620 private int getItemSize( CachedItem item) 1621 { 1622 if( ! useByteCount) 1623 return 1; 1624 SizedCacheable entry = (SizedCacheable) item.getEntry(); 1625 if( null == entry) 1626 return 0; 1627 return entry.getSize(); 1628 } 1630 1633 public synchronized long[] getCacheStats() 1634 { 1635 stat.currentSize = getCurrentSize(); 1636 return stat.getStats(); 1637 } 1638 1639 1642 public void resetCacheStats() 1643 { 1644 stat.reset(); 1645 } 1646 1647 1650 public synchronized long getMaximumSize() 1651 { 1652 return maximumSize; 1653 } 1654 1655 1663 public void resize( long newSize) throws StandardException 1664 { 1665 boolean shrink; 1666 1667 synchronized( this) 1668 { 1669 maximumSize = newSize; 1670 stat.maxSize = maximumSize; 1671 shrink = ( shrinkSize( getCurrentSize()) > 0); 1672 } 1673 if( shrink) 1674 { 1675 performWork(true ); 1676 1680 if( shrinkSize( getCurrentSize()) > 0) 1681 { 1682 CachedItem freeItem = rotateClock( (float) 2.0); 1683 1694 if( freeItem != null) 1695 freeItem.unkeepForCreate(); 1696 } 1697 } 1698 1699 } 1701 private synchronized long getCurrentSize() 1702 { 1703 if( ! useByteCount) 1704 return holders.size(); 1705 return currentByteCount + holders.size()*ITEM_OVERHEAD; 1706 } 1707 1708 1716 public void scan( Matchable filter, Operator operator) 1717 { 1718 int itemCount = 1; 1719 Cacheable entry = null; 1720 CachedItem item = null; 1721 1722 for( int position = 0;; position++) 1727 { 1728 synchronized( this) 1729 { 1730 if( null != item) 1731 { 1732 release( item); 1733 item = null; 1734 } 1735 1736 for( ; position < holders.size(); position++) 1737 { 1738 item = (CachedItem) holders.get( position); 1739 if( null != item) 1740 { 1741 try 1742 { 1743 entry = item.use(); 1744 } 1745 catch( StandardException se) 1746 { 1747 continue; 1748 } 1749 1750 if( null != entry && (null == filter || filter.match( entry))) 1751 { 1752 item.keepForClean(); 1753 break; 1754 } 1755 } 1756 } 1757 if( position >= holders.size()) 1758 return; 1759 1760 } operator.operate( entry); 1762 } 1765 } 1767 private int trimRequests = 0; 1768 1769 1773 private void trimToSize() 1774 { 1775 int size = holders.size(); 1776 1777 trimRequests++; 1779 if( trimRequests < size/8) 1780 return; 1781 trimRequests = 0; 1782 1783 int endPosition = size - 1; 1785 1786 int invalidCount = 0; 1787 for (int i = 0; i <= endPosition; i++) 1788 { 1789 CachedItem item = (CachedItem) holders.get(i); 1790 1791 if (item.isKept()) 1792 continue; 1793 1794 if (item.isValid()) 1795 continue; 1796 1797 invalidCount++; 1798 1799 for (; endPosition > i; endPosition--) { 1802 CachedItem last = (CachedItem) holders.get(endPosition); 1803 if (last.isValid()) { 1804 holders.set(i, last); 1805 holders.set(endPosition, item); 1806 endPosition--; 1807 break; 1808 } 1809 } 1810 } 1811 if (size < 32) 1813 return; 1814 1815 int validItems = size - invalidCount; 1817 1818 if (validItems > ((3 * size) / 4)) 1820 return; 1821 1822 int newSize = validItems + (validItems / 10); 1824 1825 if (newSize >= size) 1826 return; 1827 1828 for (int r = size - 1; r > newSize; r--) { 1831 CachedItem remove = (CachedItem) holders.get(r); 1832 if (remove.isKept() || remove.isValid()) { 1833 continue; 1834 } 1835 1836 if (useByteCount) { 1837 currentByteCount -= getItemSize(remove); 1838 } 1839 1840 holders.remove(r); 1841 } 1842 1843 holders.trimToSize(); 1844 clockHand = validItems + 1; 1846 1847 } } 1849 | Popular Tags |