Index: src/java/org/apache/solr/common/util/ConcurrentLRUCache.java
===================================================================
--- src/java/org/apache/solr/common/util/ConcurrentLRUCache.java	(revision 709219)
+++ src/java/org/apache/solr/common/util/ConcurrentLRUCache.java	(working copy)
@@ -1,8 +1,22 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 package org.apache.solr.common.util;
 
-import java.util.LinkedHashMap;
-import java.util.Map;
-import java.util.TreeSet;
+import java.util.*;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicLong;
@@ -31,6 +45,8 @@
   private volatile boolean islive = true;
   private final Stats stats = new Stats();
   private final int acceptableWaterMark;
+  //oldest known item in the cache
+  private long oldestItem = 0;
 
   public ConcurrentLRUCache(int upperWaterMark, final int lowerWaterMark, int acceptableWatermark, int initialSize, boolean runCleanupThread, boolean runNewThreadForCleanup, final int delay) {
     if (upperWaterMark < 1) throw new IllegalArgumentException("upperWaterMark must be > 0");
@@ -126,26 +142,39 @@
    */
   public void markAndSweep() {
     if (!markAndSweepLock.tryLock()) return;
+    //take a copy and keep modifying that
+    long oldestItem = this.oldestItem;
     try {
       isCleaning = true;
       int size = stats.size.get();
-      long currentLatestAccessed = stats.accessCounter.get();
       int itemsToBeRemoved = size - lowerWaterMark;
       int itemsRemoved = 0;
       if (itemsToBeRemoved < 1) return;
-      // currentLatestAccessed is the counter value of the item accessed most recently
-      // therefore remove all items whose last accessed counter is less than (currentLatestAccessed - lowerWaterMark)
-      long removeOlderThan = currentLatestAccessed - lowerWaterMark;
-      for (Map.Entry<Object, CacheEntry> entry : map.entrySet()) {
-        if (entry.getValue().lastAccessed <= removeOlderThan && itemsRemoved < itemsToBeRemoved) {
-          evictEntry(entry.getKey());
+      // iteration : 1
+      // let us remove all the entries from the lowest to the no:of items to be removed
+      // this may remove less than the desired no:of items . but it will never remove an item which
+      // must not be removed
+      long removeOlderThan = oldestItem + itemsToBeRemoved;
+      // keep the non removes entries in an array , so that the second iteration
+      // is more efficient
+      CacheEntry[] entryArr = new CacheEntry[size];
+      int entryArrIdx =0;
+      // if the oldest entry is known then it is safe to remove any item which is less than oldestItem + itemsToBeRemoved
+      for (CacheEntry entry : map.values()) {
+        entry.lastAccessedCopy = entry.lastAccessed;
+        if (entry. lastAccessedCopy <= removeOlderThan && itemsRemoved < itemsToBeRemoved) {
+          evictEntry(entry.key);
+          if (entry.lastAccessedCopy > oldestItem) {
+            oldestItem = entry.lastAccessedCopy;
+          }
+        } else {
+          // if the item is not removed , copy them into an array
+          entryArr[entryArrIdx++] = entry;
         }
       }
 
-      // Since the removal of items in the above loop depends on the value of the lastAccessed variable,
-      // between the time we recorded the number of items to be removed and the actual removal process,
-      // some items may graduate above the removeOlderThan value and escape eviction.
-      // Therefore, we again check if the size less than acceptableWaterMark, if not we remove items forcefully
+      // Since the removal of items in the above loop does not guarantee that the size will reach a desired number
+      // we again check if the size less than acceptableWaterMark, if not we remove items forcefully
       // using a method which does not depend on the value of lastAccessed but can be more costly to run
 
       size = stats.size.get();
@@ -159,8 +188,9 @@
       // bucket of fixed size and remove them even if they have become newer in the meantime
       // The caveat is that this may lead to more cache misses because we may have removed
       // an item which was used very recently (against the philosophy of LRU)
-      for (Map.Entry<Object, CacheEntry> entry : map.entrySet()) {
-        CacheEntry v = entry.getValue();
+      for (int i=0;i < entryArrIdx;i++) {
+        CacheEntry v = entryArr[i];
+        //copy the value again. it would have changed since we lat copied it
         v.lastAccessedCopy = v.lastAccessed;
         if (tree.size() < itemsToBeRemoved) {
           tree.add(v);
@@ -171,11 +201,17 @@
           }
         }
       }
-      for (CacheEntry sortCacheEntry : tree)
-        evictEntry(sortCacheEntry.key);
+      for (CacheEntry e : tree){
+        evictEntry(e.key);
+        if(e.lastAccessedCopy > oldestItem ){
+          oldestItem = e.lastAccessedCopy;
+        }
+      }
     } finally {
       isCleaning = false;
       markAndSweepLock.unlock();
+      //copy the value to the instance variable
+      this.oldestItem = oldestItem;
     }
   }
 
