/**
   * Reduces the number of rows held in this Cache object.
   *
   * <p>Cleanup is done by checking the accessCount of the Rows and removing the rows with the
   * lowest access count.
   *
   * <p>Index operations require that up to 5 recently accessed rows remain in the cache.
   */
  private synchronized void cleanUp() throws IOException {

    int removeCount = cacheMap.size() / 2;
    int accessTarget = cacheMap.getAccessCountCeiling(removeCount, removeCount / 8);
    ObjectCacheHashMap.ObjectCacheIterator it = cacheMap.iterator();
    int savecount = 0;

    for (; it.hasNext(); ) {
      CachedObject r = (CachedObject) it.next();

      if (it.getAccessCount() <= accessTarget) {
        if (!r.isKeepInMemory()) {
          if (r.hasChanged()) {
            rowTable[savecount++] = r;
          }

          it.remove();

          cacheBytesLength -= r.getStorageSize();
        }
      }
    }

    cacheMap.setAccessCountFloor(accessTarget);
    saveRows(savecount);
  }
  /** Adds a row to the cache. */
  public synchronized void put(int key, CachedObject row) throws IOException {

    int storageSize = row.getStorageSize();

    if (cacheMap.size() >= capacity || storageSize + cacheBytesLength > bytesCapacity) {
      cleanUp();
    }

    cacheMap.put(key, row);

    cacheBytesLength += storageSize;
  }
  /** Removes an object from memory cache. Does not release the file storage. */
  public synchronized CachedObject release(int i) {

    CachedObject r = (CachedObject) cacheMap.remove(i);

    if (r == null) {
      return null;
    }

    cacheBytesLength -= r.getStorageSize();

    return r;
  }
  /** Writes out all modified cached Rows. */
  public synchronized void saveAll() throws IOException {

    Iterator it = cacheMap.iterator();
    int savecount = 0;

    for (; it.hasNext(); ) {
      CachedObject r = (CachedObject) it.next();

      if (r.hasChanged()) {
        rowTable[savecount++] = r;
      }
    }

    saveRows(savecount);
    Trace.printSystemOut(saveAllTimer.elapsedTimeToMessage("Cache.saveRow() total row save time"));
    Trace.printSystemOut("Cache.saveRow() total row save count = " + saveRowCount);
    Trace.printSystemOut(makeRowTimer.elapsedTimeToMessage("Cache.makeRow() total row load time"));
    Trace.printSystemOut("Cache.makeRow() total row load count = " + makeRowCount);
    Trace.printSystemOut(sortTimer.elapsedTimeToMessage("Cache.sort() total time"));
  }
 public int size() {
   return cacheMap.size();
 }
  /** clears out the memory cache */
  synchronized void clear() {

    cacheMap.clear();

    cacheBytesLength = 0;
  }
 /** Returns a row if in memory cache. */
 public synchronized CachedObject get(int pos) {
   return (CachedObject) cacheMap.get(pos);
 }