public DirectBufferWrapper get(long pageId, boolean read, boolean checkAccess) throws IOException {
    DirectBufferWrapper wrapper;
    //fast path: buffer is in .segments
    //FIXME RC: only read lock is needed here -- could improve scalability for fast path
    //          ...But LinkedHashMap.get() is not a read operation, since with .accessOrder .get() will
    //          reorder entries, hence it is a _write_ operation, that is why exclusive lock
    //          is needed
    pagesAccessLock.lock();
    try {
      wrapper = pagesByPageId.get(pageId);
      if (wrapper != null) {
        myHits++;
        return wrapper;
      }
    }
    finally {
      pagesAccessLock.unlock();
    }

    //maybe buffer is scheduled for remove, but not yet removed? Return it from trash when:
    pagesAllocationLock.lock();
    try {
      DirectBufferWrapper notYetRemoved = pagesToRemoveByPageId.remove(pageId);
      if (notYetRemoved != null) {
        pagesAccessLock.lock();
        try {
          DirectBufferWrapper previous = pagesByPageId.put(pageId, notYetRemoved);
          assert previous == null;
        }
        finally {
          pagesAccessLock.unlock();
        }

        disposeRemovedSegments(null);
        myHits++;
        return notYetRemoved;
      }

      //Double-check: maybe somebody already loads our segment after we've checked first time:
      pagesAccessLock.lock();
      try {
        wrapper = pagesByPageId.get(pageId);
        if (wrapper != null) return wrapper;
      }
      finally {
        pagesAccessLock.unlock();
      }

      //Slow path: allocate new buffer and load its content from fileStorage:

      final long startedAtNs = COLLECT_PAGE_LOADING_TIMES ? System.nanoTime() : 0;

      final PagedFileStorage fileStorage = getRegisteredPagedFileStorageByIndex(pageId);
      disposeRemovedSegments(null);

      final long disposeFinishedAtNs = COLLECT_PAGE_LOADING_TIMES ? System.nanoTime() : 0;

      wrapper = allocateAndLoadPage(pageId, read, fileStorage, checkAccess);

      if (COLLECT_PAGE_LOADING_TIMES) {
        final long finishedAtNs = System.nanoTime();
        myLoadedPages++;
        myPageLoadUs += NANOSECONDS.toMicros(finishedAtNs - disposeFinishedAtNs);
        myPageDisposalUs += NANOSECONDS.toMicros(disposeFinishedAtNs - startedAtNs);
      }

      pagesAccessLock.lock();
      try {
        if (totalSizeCached + fileStorage.getPageSize() < cachedSizeLimit) {
          myRegularPageLoads++;
        }
        else {
          myPageLoadsAboveSizeThreshold++;
        }
        pagesByPageId.put(pageId, wrapper);
      }
      finally {
        pagesAccessLock.unlock();
      }

      ensureSize(cachedSizeLimit);

      return wrapper;
    }
    finally {
      pagesAllocationLock.unlock();
    }
  }
public DirectBufferWrapper get(long pageId, boolean read, boolean checkAccess)