Class WindowCache

java.lang.Object
org.eclipse.jgit.internal.storage.file.WindowCache

public class WindowCache extends Object
Caches slices of a Pack in memory for faster read access.

The WindowCache serves as a Java based "buffer cache", loading segments of a PackFile into the JVM heap prior to use. As JGit often wants to do reads of only tiny slices of a file, the WindowCache tries to smooth out these tiny reads into larger block-sized IO operations.

Whenever a cache miss occurs, load(Pack, long) is invoked by exactly one thread for the given (PackFile,position) key tuple. This is ensured by an array of locks, with the tuple hashed to a lock instance.

During a miss, older entries are evicted from the cache so long as isFull() returns true.

Its too expensive during object access to be 100% accurate with a least recently used (LRU) algorithm. Strictly ordering every read is a lot of overhead that typically doesn't yield a corresponding benefit to the application.

This cache implements a loose LRU policy by randomly picking a window comprised of roughly 10% of the cache, and evicting the oldest accessed entry within that window.

Entities created by the cache are held under SoftReferences if option core.packedGitUseStrongRefs is set to false in the git config (this is the default) or by calling WindowCacheConfig.setPackedGitUseStrongRefs(boolean), permitting the Java runtime's garbage collector to evict entries when heap memory gets low. Most JREs implement a loose least recently used algorithm for this eviction. When this option is set to true strong references are used which means that Java gc cannot evict the WindowCache to reclaim memory. On the other hand this provides more predictable performance since the cache isn't flushed when used heap comes close to the maximum heap size.

The internal hash table does not expand at runtime, instead it is fixed in size at cache creation time. The internal lock table used to gate load invocations is also fixed in size.

The key tuple is passed through to methods as a pair of parameters rather than as a single Object, thus reducing the transient memory allocations of callers. It is more efficient to avoid the allocation, as we can't be 100% sure that a JIT would be able to stack-allocate a key tuple.

This cache has an implementation rule such that:

  • load(Pack, long) is invoked by at most one thread at a time for a given (PackFile,position) tuple.
  • For every load() invocation there is exactly one createRef(Pack, long, ByteWindow) invocation to wrap a SoftReference or a StrongReference around the cached entity.
  • For every Reference created by createRef() there will be exactly one call to clear(PageRef) to cleanup any resources associated with the (now expired) cached entity.

Therefore, it is safe to perform resource accounting increments during the load(Pack, long) or createRef(Pack, long, ByteWindow) methods, and matching decrements during clear(PageRef). Implementors may need to override createRef(Pack, long, ByteWindow) in order to embed additional accounting information into an implementation specific WindowCache.PageRef subclass, as the cached entity may have already been evicted by the JRE's garbage collector.

To maintain higher concurrency workloads, during eviction only one thread performs the eviction work, while other threads can continue to insert new objects in parallel. This means that the cache can be temporarily over limit, especially if the nominated eviction thread is being starved relative to the other threads.

  • Field Details

    • rng

      private static final Random rng
    • cache

      private static volatile WindowCache cache
    • streamFileThreshold

      private static volatile int streamFileThreshold
    • queue

      private final WindowCache.CleanupQueue queue
      cleanup released and/or garbage collected windows.
    • tableSize

      private final int tableSize
      Number of entries in table.
    • clock

      private final AtomicLong clock
      Access clock for loose LRU.
    • table

      Hash bucket directory; entries are chained below.
    • locks

      private final WindowCache.Lock[] locks
      Locks to prevent concurrent loads for same (PackFile,position).
    • evictLock

      private final ReentrantLock evictLock
      Lock to elect the eviction thread after a load occurs.
    • evictBatch

      private final int evictBatch
      Number of table buckets to scan for an eviction window.
    • maxFiles

      private final int maxFiles
    • maxBytes

      private final long maxBytes
    • mmap

      private final boolean mmap
    • windowSizeShift

      private final int windowSizeShift
    • windowSize

      private final int windowSize
    • statsRecorder

      private final WindowCache.StatsRecorder statsRecorder
    • mbean

      private final WindowCache.StatsRecorderImpl mbean
    • publishMBean

      private final AtomicBoolean publishMBean
    • useStrongRefs

      private boolean useStrongRefs
  • Constructor Details

  • Method Details

    • bits

      private static final int bits(int newSize)
    • reconfigure

      @Deprecated public static void reconfigure(WindowCacheConfig cfg)
      Deprecated.
      use cfg.install() to avoid internal reference.
      Modify the configuration of the window cache.

      The new configuration is applied immediately. If the new limits are smaller than what is currently cached, older entries will be purged as soon as possible to allow the cache to meet the new limit.

      Parameters:
      cfg - the new window cache configuration.
      Throws:
      IllegalArgumentException - the cache configuration contains one or more invalid settings, usually too low of a limit.
    • getStreamFileThreshold

      static int getStreamFileThreshold()
    • getInstance

      public static WindowCache getInstance()
      Returns:
      the cached instance.
    • get

      static final ByteWindow get(Pack pack, long offset) throws IOException
      Throws:
      IOException
    • purge

      static final void purge(Pack pack)
    • publishMBeanIfNeeded

      private WindowCache publishMBeanIfNeeded()
    • getStats

      public WindowCacheStats getStats()
      Returns:
      cache statistics for the WindowCache
    • resetStats

      public void resetStats()
      Reset stats. Does not reset open bytes and open files stats.
    • hash

      private int hash(int packHash, long off)
    • load

      private ByteWindow load(Pack pack, long offset) throws IOException
      Throws:
      IOException
    • createRef

      private WindowCache.PageRef<ByteWindow> createRef(Pack p, long o, ByteWindow v)
    • clear

      private void clear(WindowCache.PageRef<ByteWindow> ref)
    • close

      private void close(Pack pack)
    • isFull

      private boolean isFull()
    • toStart

      private long toStart(long offset)
    • tableSize

      private static int tableSize(WindowCacheConfig cfg)
    • lockCount

      private static int lockCount(WindowCacheConfig cfg)
    • getOrLoad

      private ByteWindow getOrLoad(Pack pack, long position) throws IOException
      Lookup a cached object, creating and loading it if it doesn't exist.
      Parameters:
      pack - the pack that "contains" the cached object.
      position - offset within pack of the object.
      Returns:
      the object reference.
      Throws:
      IOException - the object reference was not in the cache and could not be obtained by load(Pack, long).
    • scan

      private ByteWindow scan(WindowCache.Entry n, Pack pack, long position)
    • hit

      private void hit(WindowCache.PageRef r)
    • evict

      private void evict()
    • removeAll

      private void removeAll()
      Clear every entry from the cache.

      This is a last-ditch effort to clear out the cache, such as before it gets replaced by another cache that is configured differently. This method tries to force every cached entry through clear(PageRef) to ensure that resources are correctly accounted for and cleaned up by the subclass. A concurrent reader loading entries while this method is running may cause resource accounting failures.

    • removeAll

      private void removeAll(Pack pack)
      Clear all entries related to a single file.

      Typically this method is invoked during Pack.close(), when we know the pack is never going to be useful to us again (for example, it no longer exists on disk). A concurrent reader loading an entry from this same pack may cause the pack to become stuck in the cache anyway.

      Parameters:
      pack - the file to purge all entries of.
    • gc

      private void gc()
    • slot

      private int slot(Pack pack, long position)
    • lock

      private WindowCache.Lock lock(Pack pack, long position)
    • clean

      private static WindowCache.Entry clean(WindowCache.Entry top)