/**
   * 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);
  }
  /** 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"));
  }