public String getAuditReport() {
   LRUClockNode h = this.head;
   int totalNodes = 0;
   int evictedNodes = 0;
   int usedNodes = 0;
   while (h != null) {
     totalNodes++;
     if (h.testEvicted()) evictedNodes++;
     if (h.testRecentlyUsed()) usedNodes++;
     h = h.nextLRUNode();
   }
   StringBuffer result = new StringBuffer(128);
   result
       .append("LRUList Audit: listEntries = ")
       .append(totalNodes)
       .append(" evicted = ")
       .append(evictedNodes)
       .append(" used = ")
       .append(usedNodes);
   return result.toString();
 }
  /**
   * return the Entry that is considered least recently used. The entry will no longer be in the
   * pipe (unless it is the last empty marker).
   */
  public LRUClockNode getLRUEntry() {
    long numEvals = 0;

    for (; ; ) {
      LRUClockNode aNode = null;
      aNode = getHeadEntry();

      if (logger.isTraceEnabled(LogMarker.LRU_CLOCK)) {
        logger.trace(LogMarker.LRU_CLOCK, "lru considering {}", aNode);
      }

      if (aNode == null) { // hit the end of the list
        this.stats.incEvaluations(numEvals);
        return aNode;
      } // hit the end of the list

      numEvals++;

      // If this Entry is part of a transaction, skip it since
      // eviction should not cause commit conflicts
      synchronized (aNode) {
        if (aNode instanceof AbstractRegionEntry) {
          if (((AbstractRegionEntry) aNode).isInUseByTransaction()) {
            if (logger.isTraceEnabled(LogMarker.LRU_CLOCK)) {
              logger.trace(
                  LogMarker.LRU_CLOCK,
                  LocalizedMessage.create(
                      LocalizedStrings
                          .NewLRUClockHand_REMOVING_TRANSACTIONAL_ENTRY_FROM_CONSIDERATION));
            }
            continue;
          }
        }
        if (aNode.testEvicted()) {
          if (logger.isTraceEnabled(LogMarker.LRU_CLOCK)) {
            logger.trace(
                LogMarker.LRU_CLOCK,
                LocalizedMessage.create(LocalizedStrings.NewLRUClockHand_DISCARDING_EVICTED_ENTRY));
          }
          continue;
        }

        // At this point we have any acceptable entry.  Now
        // use various criteria to determine if it's good enough
        // to return, or if we need to add it back to the list.
        if (maxEntries > 0 && numEvals > maxEntries) {
          if (logger.isTraceEnabled(LogMarker.LRU_CLOCK)) {
            logger.trace(
                LogMarker.LRU_CLOCK,
                LocalizedMessage.create(
                    LocalizedStrings.NewLRUClockHand_GREEDILY_PICKING_AN_AVAILABLE_ENTRY));
          }
          this.stats.incGreedyReturns(1);
          // fall through, return this node
        } else if (aNode.testRecentlyUsed()) {
          // Throw it back, it's in the working set
          aNode.unsetRecentlyUsed();
          // aNode.setInList();
          if (logger.isTraceEnabled(LogMarker.LRU_CLOCK)) {
            logger.trace(
                LogMarker.LRU_CLOCK,
                LocalizedMessage.create(
                    LocalizedStrings.NewLRUClockHand_SKIPPING_RECENTLY_USED_ENTRY, aNode));
          }
          appendEntry(aNode);
          continue; // keep looking
        } else {
          if (logger.isTraceEnabled(LogMarker.LRU_CLOCK)) {
            logger.trace(
                LogMarker.LRU_CLOCK,
                LocalizedMessage.create(
                    LocalizedStrings.NewLRUClockHand_RETURNING_UNUSED_ENTRY, aNode));
          }
          // fall through, return this node
        }

        // Return the current node.
        this.stats.incEvaluations(numEvals);
        return aNode;
      } // synchronized
    } // for
  }