Introduce core.packedIndexGitUseStrongRefs config key Introduce a core.packedIndexGitUseStrongRefs configuration key, which defaults to true so that the current behavior does not change. However, setting it to false allows soft references to be used for Pack indices instead of strong references so that they can be garbage collected when there is memory pressure. Pack objects can be large when associated with pack files with large object counts, and this memory is not really accounted for or tracked by the WindowCache and it can be very substantial at times, especially with many large object count projects. A particularly problematic use case is Gerrit's ls-projects command which loads very little data in the WindowCache via ByteWindows, but ends up loading and holding many entire indices in memory, sometimes even after the ByteWindows for their Pack objects have already been garbage collected since they won't get cleared until after a new ByteWindow is loaded. By using SoftReferences, single use indices can get cleared when there is memory pressure and OOMs can be easily avoided, drastically reducing the amount of memory required to perform an ls-projects on large sites with many projects and large object counts. On one of our test sites, an ls-projects command with strong index references requires more than 66GB of heap to complete successfully, with soft index references it requires less than 23GB. Change-Id: I3cb3df52f4ce1b8c554d378807218f199077d80b Signed-off-by: Martin Fick <quic_mfick@quicinc.com> Signed-off-by: Matthias Sohn <matthias.sohn@sap.com>
diff --git a/Documentation/config-options.md b/Documentation/config-options.md index 3495813..5651001 100644 --- a/Documentation/config-options.md +++ b/Documentation/config-options.md
@@ -38,6 +38,7 @@ | `core.packedGitMmap` | `false` | ✅ | Whether to use Java NIO virtual memory mapping for JGit buffer cache. When set to `true` enables use of Java NIO virtual memory mapping for cache windows, `false` reads entire window into a `byte[]` with standard read calls. `true` is experimental and may cause instabilities and crashes since Java doesn't support explicit unmapping of file regions mapped to virtual memory. | | `core.packedGitOpenFiles` | `128` | ⃞ | Maximum number of streams to open at a time. Open packs count against the process limits. | | `core.packedGitUseStrongRefs` | `false` | ⃞ | Whether the window cache should use strong references (`true`) or SoftReferences (`false`). When `false` the JVM will drop data cached in the JGit block cache when heap usage comes close to the maximum heap size. | +| `core.packedIndexGitUseStrongRefs` | `true` | ⃞ | Whether pack indices should use strong references (`true`) or SoftReferences (`false`). When `false` the JVM will drop data cached in the JGit pack indices when heap usage comes close to the maximum heap size. | | `core.packedGitWindowSize` | `8 kiB` | ✅ | Number of bytes of a pack file to load into memory in a single read operation. This is the "page size" of the JGit buffer cache, used for all pack access operations. All disk IO occurs as single window reads. Setting this too large may cause the process to load more data than is required; setting this too small may increase the frequency of read() system calls. | | `core.precomposeUnicode` | `true` on Mac OS | ✅ | MacOS only. When `true`, JGit reverts the unicode decomposition of filenames done by Mac OS. | | `core.quotePath` | `true` | ✅ | Commands that output paths (e.g. ls-files, diff), will quote "unusual" characters in the pathname by enclosing the pathname in double-quotes and escaping those characters with backslashes in the same way C escapes control characters (e.g. `\t` for TAB, `\n` for LF, `\\` for backslash) or bytes with values larger than `0x80` (e.g. octal `\302\265` for "micro" in UTF-8). |
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackTest.java index a359654..e150945 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackTest.java
@@ -261,7 +261,7 @@ public void testDelta_FailsOver2GiB() throws Exception { new PackIndexWriterV1(f).write(list, footer); } - Pack pack = new Pack(packName, null); + Pack pack = new Pack(repo.getConfig(), packName, null); try { pack.get(wc, b); fail("expected LargeObjectException.ExceedsByteArrayLimit");
diff --git a/org.eclipse.jgit/META-INF/MANIFEST.MF b/org.eclipse.jgit/META-INF/MANIFEST.MF index 84b2987..0b5e8bf 100644 --- a/org.eclipse.jgit/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit/META-INF/MANIFEST.MF
@@ -124,6 +124,7 @@ x-friends:="org.eclipse.jgit.ssh.apache, org.eclipse.jgit.ssh.jsch, org.eclipse.jgit.test", + org.eclipse.jgit.internal.util;version="6.7.0";x-internal:=true, org.eclipse.jgit.lib;version="6.7.0"; uses:="org.eclipse.jgit.transport, org.eclipse.jgit.util.sha1,
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectory.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectory.java index 0ef38db..579f931 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectory.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectory.java
@@ -259,7 +259,7 @@ public Pack openPack(File pack) throws IOException { } PackFile bitmapIdx = pf.create(BITMAP_INDEX); - Pack res = new Pack(pack, bitmapIdx.exists() ? bitmapIdx : null); + Pack res = new Pack(config, pack, bitmapIdx.exists() ? bitmapIdx : null); packed.insert(res); return res; }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/Pack.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/Pack.java index 2b5586a..90f9811 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/Pack.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/Pack.java
@@ -15,6 +15,8 @@ import static org.eclipse.jgit.internal.storage.pack.PackExt.INDEX; import static org.eclipse.jgit.internal.storage.pack.PackExt.KEEP; import static org.eclipse.jgit.internal.storage.pack.PackExt.REVERSE_INDEX; +import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_CORE_SECTION; +import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_PACKED_INDEX_GIT_USE_STRONGREFS; import java.io.EOFException; import java.io.File; @@ -32,6 +34,7 @@ import java.util.Collections; import java.util.Comparator; import java.util.Iterator; +import java.util.Optional; import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; import java.util.zip.CRC32; @@ -53,8 +56,10 @@ import org.eclipse.jgit.internal.storage.pack.BinaryDelta; import org.eclipse.jgit.internal.storage.pack.PackExt; import org.eclipse.jgit.internal.storage.pack.PackOutputStream; +import org.eclipse.jgit.internal.util.Optionally; import org.eclipse.jgit.lib.AbbreviatedObjectId; import org.eclipse.jgit.lib.AnyObjectId; +import org.eclipse.jgit.lib.Config; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectLoader; @@ -79,6 +84,8 @@ public class Pack implements Iterable<PackIndex.MutableEntry> { public static final Comparator<Pack> SORT = (a, b) -> b.packLastModified .compareTo(a.packLastModified); + private boolean useStrongRefs; + private final PackFile packFile; private PackFile keepFile; @@ -111,11 +118,11 @@ public class Pack implements Iterable<PackIndex.MutableEntry> { private byte[] packChecksum; - private volatile PackIndex loadedIdx; + private volatile Optionally<PackIndex> loadedIdx = Optionally.empty(); - private PackReverseIndex reverseIdx; + private Optionally<PackReverseIndex> reverseIdx = Optionally.empty(); - private PackBitmapIndex bitmapIdx; + private Optionally<PackBitmapIndex> bitmapIdx = Optionally.empty(); /** * Objects we have tried to read, and discovered to be corrupt. @@ -129,12 +136,16 @@ public class Pack implements Iterable<PackIndex.MutableEntry> { /** * Construct a reader for an existing, pre-indexed packfile. * + * @param cfg + * configuration this directory consults for write settings. * @param packFile * path of the <code>.pack</code> file holding the data. * @param bitmapIdxFile * existing bitmap index file with the same base as the pack */ - public Pack(File packFile, @Nullable PackFile bitmapIdxFile) { + public Pack(Config cfg, File packFile, @Nullable PackFile bitmapIdxFile) { + useStrongRefs = cfg.getBoolean(CONFIG_CORE_SECTION, + CONFIG_KEY_PACKED_INDEX_GIT_USE_STRONGREFS, WindowCache.getInstance().isPackedIndexGitUseStrongRefs()); this.packFile = new PackFile(packFile); this.fileSnapshot = PackFileSnapshot.save(packFile); this.packLastModified = fileSnapshot.lastModifiedInstant(); @@ -148,57 +159,58 @@ public Pack(File packFile, @Nullable PackFile bitmapIdxFile) { } private PackIndex idx() throws IOException { - PackIndex idx = loadedIdx; - if (idx == null) { - synchronized (this) { - idx = loadedIdx; - if (idx == null) { - if (invalid) { - throw new PackInvalidException(packFile, - invalidatingCause); - } - try { - long start = System.currentTimeMillis(); - PackFile idxFile = packFile.create(INDEX); - idx = PackIndex.open(idxFile); - if (LOG.isDebugEnabled()) { - LOG.debug(String.format( - "Opening pack index %s, size %.3f MB took %d ms", //$NON-NLS-1$ - idxFile.getAbsolutePath(), - Float.valueOf(idxFile.length() - / (1024f * 1024)), - Long.valueOf(System.currentTimeMillis() - - start))); - } - - if (packChecksum == null) { - packChecksum = idx.packChecksum; - fileSnapshot.setChecksum( - ObjectId.fromRaw(packChecksum)); - } else if (!Arrays.equals(packChecksum, - idx.packChecksum)) { - throw new PackMismatchException(MessageFormat - .format(JGitText.get().packChecksumMismatch, - packFile.getPath(), - PackExt.PACK.getExtension(), - Hex.toHexString(packChecksum), - PackExt.INDEX.getExtension(), - Hex.toHexString(idx.packChecksum))); - } - loadedIdx = idx; - } catch (InterruptedIOException e) { - // don't invalidate the pack, we are interrupted from - // another thread - throw e; - } catch (IOException e) { - invalid = true; - invalidatingCause = e; - throw e; - } + Optional<PackIndex> optional = loadedIdx.getOptional(); + if (optional.isPresent()) { + return optional.get(); + } + synchronized (this) { + optional = loadedIdx.getOptional(); + if (optional.isPresent()) { + return optional.get(); + } + if (invalid) { + throw new PackInvalidException(packFile, invalidatingCause); + } + try { + long start = System.currentTimeMillis(); + PackFile idxFile = packFile.create(INDEX); + PackIndex idx = PackIndex.open(idxFile); + if (LOG.isDebugEnabled()) { + LOG.debug(String.format( + "Opening pack index %s, size %.3f MB took %d ms", //$NON-NLS-1$ + idxFile.getAbsolutePath(), + Float.valueOf(idxFile.length() + / (1024f * 1024)), + Long.valueOf(System.currentTimeMillis() + - start))); } + + if (packChecksum == null) { + packChecksum = idx.packChecksum; + fileSnapshot.setChecksum( + ObjectId.fromRaw(packChecksum)); + } else if (!Arrays.equals(packChecksum, + idx.packChecksum)) { + throw new PackMismatchException(MessageFormat + .format(JGitText.get().packChecksumMismatch, + packFile.getPath(), + PackExt.PACK.getExtension(), + Hex.toHexString(packChecksum), + PackExt.INDEX.getExtension(), + Hex.toHexString(idx.packChecksum))); + } + loadedIdx = optionally(idx); + return idx; + } catch (InterruptedIOException e) { + // don't invalidate the pack, we are interrupted from + // another thread + throw e; + } catch (IOException e) { + invalid = true; + invalidatingCause = e; + throw e; } } - return idx; } /** * Get the File object which locates this pack on disk. @@ -288,8 +300,9 @@ void resolve(Set<ObjectId> matches, AbbreviatedObjectId id, int matchLimit) public void close() { WindowCache.purge(this); synchronized (this) { - loadedIdx = null; - reverseIdx = null; + loadedIdx.clear(); + reverseIdx.clear(); + bitmapIdx.clear(); } } @@ -1127,40 +1140,41 @@ synchronized PackBitmapIndex getBitmapIndex() throws IOException { if (invalid || bitmapIdxFile == null) { return null; } - if (bitmapIdx == null) { - final PackBitmapIndex idx; - try { - idx = PackBitmapIndex.open(bitmapIdxFile, idx(), - getReverseIdx()); - } catch (FileNotFoundException e) { - // Once upon a time this bitmap file existed. Now it - // has been removed. Most likely an external gc has - // removed this packfile and the bitmap - bitmapIdxFile = null; - return null; - } - + Optional<PackBitmapIndex> optional = bitmapIdx.getOptional(); + if (optional.isPresent()) { + return optional.get(); + } + try { + PackBitmapIndex idx = PackBitmapIndex.open(bitmapIdxFile, idx(), + getReverseIdx()); // At this point, idx() will have set packChecksum. if (Arrays.equals(packChecksum, idx.packChecksum)) { - bitmapIdx = idx; - } else { - bitmapIdxFile = null; + bitmapIdx = optionally(idx); + return idx; } + } catch (FileNotFoundException e) { + // Once upon a time this bitmap file existed. Now it + // has been removed. Most likely an external gc has + // removed this packfile and the bitmap } - return bitmapIdx; + bitmapIdxFile = null; + return null; } private synchronized PackReverseIndex getReverseIdx() throws IOException { if (invalid) { throw new PackInvalidException(packFile, invalidatingCause); } - if (reverseIdx == null) { - PackFile reverseIndexFile = packFile.create(REVERSE_INDEX); - reverseIdx = PackReverseIndexFactory.openOrCompute(reverseIndexFile, - getObjectCount(), () -> getIndex()); - reverseIdx.verifyPackChecksum(getPackFile().getPath()); + Optional<PackReverseIndex> optional = reverseIdx.getOptional(); + if (optional.isPresent()) { + return optional.get(); } - return reverseIdx; + PackFile reverseIndexFile = packFile.create(REVERSE_INDEX); + PackReverseIndex revIdx = PackReverseIndexFactory.openOrCompute(reverseIndexFile, + getObjectCount(), () -> getIndex()); + revIdx.verifyPackChecksum(getPackFile().getPath()); + reverseIdx = optionally(revIdx); + return revIdx; } private boolean isCorrupt(long offset) { @@ -1195,4 +1209,8 @@ public String toString() { + packFile.length() + ", packChecksum=" + ObjectId.fromRaw(packChecksum).name() + "]"; } + + private <T> Optionally<T> optionally(T element) { + return useStrongRefs ? new Optionally.Hard<>(element) : new Optionally.Soft<>(element); + } }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackDirectory.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackDirectory.java index 85b2d34..28f6250 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackDirectory.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackDirectory.java
@@ -65,6 +65,8 @@ class PackDirectory { private static final PackList NO_PACKS = new PackList(FileSnapshot.DIRTY, new Pack[0]); + private final Config config; + private final File directory; private final AtomicReference<PackList> packList; @@ -80,6 +82,7 @@ class PackDirectory { * the location of the {@code pack} directory. */ PackDirectory(Config config, File directory) { + this.config = config; this.directory = directory; packList = new AtomicReference<>(NO_PACKS); @@ -457,7 +460,7 @@ private PackList scanPacksImpl(PackList old) { continue; } - list.add(new Pack(packFile, packFilesByExt.get(BITMAP_INDEX))); + list.add(new Pack(config, packFile, packFilesByExt.get(BITMAP_INDEX))); foundNew = true; }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/WindowCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/WindowCache.java index 25653b3..81537dd 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/WindowCache.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/WindowCache.java
@@ -435,7 +435,9 @@ static final void purge(Pack pack) { private final AtomicBoolean publishMBean = new AtomicBoolean(); - private boolean useStrongRefs; + private final boolean useStrongRefs; + + private final boolean useStrongIndexRefs; private WindowCache(WindowCacheConfig cfg) { tableSize = tableSize(cfg); @@ -467,6 +469,7 @@ else if (eb < 4) windowSizeShift = bits(cfg.getPackedGitWindowSize()); windowSize = 1 << windowSizeShift; useStrongRefs = cfg.isPackedGitUseStrongRefs(); + useStrongIndexRefs = cfg.isPackedIndexGitUseStrongRefs(); queue = useStrongRefs ? new StrongCleanupQueue(this) : new SoftCleanupQueue(this); @@ -751,6 +754,10 @@ private static Entry clean(Entry top) { return n == top.next ? top : new Entry(n, top.ref); } + boolean isPackedIndexGitUseStrongRefs() { + return useStrongIndexRefs; + } + private static class Entry { /** Next entry in the hash table's chain list. */ final Entry next;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/util/Optionally.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/util/Optionally.java new file mode 100644 index 0000000..987584f --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/util/Optionally.java
@@ -0,0 +1,126 @@ +/* + * Copyright (c) 2023 Qualcomm Innovation Center, Inc. + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Distribution License v. 1.0 which is available at + * https://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +package org.eclipse.jgit.internal.util; + +import java.lang.ref.SoftReference; +import java.util.Optional; + +/** + * Interface representing a reference to a potentially mutable optional object. + * + * @param <T> + * type of the mutable optional object + * + * @since 6.7 + */ +public interface Optionally<T> { + /** + * A permanently empty Optionally + * + * @param <T> + * type of the mutable optional object + * + */ + public class Empty<T> implements Optionally<T> { + @Override + public void clear() { + // empty + } + + @Override + public Optional<T> getOptional() { + return Optional.empty(); + } + } + + /** + * A permanent(hard) reference to an object + * + * @param <T> + * type of the mutable optional object + * + */ + public class Hard<T> implements Optionally<T> { + /** + * The mutable optional object + */ + protected T element; + + /** + * @param element + * the mutable optional object + */ + public Hard(T element) { + this.element = element; + } + + @Override + public void clear() { + element = null; + } + + @Override + public Optional<T> getOptional() { + return Optional.ofNullable(element); + } + } + + /** + * A SoftReference Optionally + * + * @param <T> + * type of the mutable optional object + * + */ + public class Soft<T> extends SoftReference<T> implements Optionally<T> { + /** + * @param t + * the mutable optional object + */ + public Soft(T t) { + super(t); + } + + @Override + public Optional<T> getOptional() { + return Optional.ofNullable(get()); + } + } + + /** + * The empty Optionally + */ + public static final Optionally<?> EMPTY = new Empty<>(); + + /** + * @param <T> + * type of the empty Optionally + * @return the empty Optionally + */ + @SuppressWarnings("unchecked") + public static <T> Optionally<T> empty() { + return (Optionally<T>) EMPTY; + } + + /** + * Clear the object + * + */ + void clear(); + + /** + * Get an Optional representing the current state of the object + * + * @return the mutable optional object + */ + Optional<T> getOptional(); +}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java index 7776b00..d812eac 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java
@@ -364,6 +364,12 @@ public final class ConfigConstants { */ public static final String CONFIG_KEY_PACKED_GIT_USE_STRONGREFS = "packedgitusestrongrefs"; + /** + * The "packedIndexGitUseStrongRefs" key + * @since 6.7 + */ + public static final String CONFIG_KEY_PACKED_INDEX_GIT_USE_STRONGREFS = "packedindexgitusestrongrefs"; + /** The "remote" key */ public static final String CONFIG_KEY_REMOTE = "remote";
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WindowCacheConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WindowCacheConfig.java index a12f652..27795ab 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WindowCacheConfig.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WindowCacheConfig.java
@@ -18,6 +18,7 @@ import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_PACKED_GIT_WINDOWSIZE; import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_STREAM_FILE_TRESHOLD; import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_PACKED_GIT_USE_STRONGREFS; +import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_PACKED_INDEX_GIT_USE_STRONGREFS; import org.eclipse.jgit.internal.storage.file.WindowCache; import org.eclipse.jgit.lib.Config; @@ -39,6 +40,8 @@ public class WindowCacheConfig { private boolean useStrongRefs; + private boolean useStrongIndexRefs; + private int packedGitWindowSize; private boolean packedGitMMAP; @@ -56,6 +59,7 @@ public WindowCacheConfig() { packedGitOpenFiles = 128; packedGitLimit = 10 * MB; useStrongRefs = false; + useStrongIndexRefs = true; packedGitWindowSize = 8 * KB; packedGitMMAP = false; deltaBaseCacheLimit = 10 * MB; @@ -133,6 +137,31 @@ public void setPackedGitUseStrongRefs(boolean useStrongRefs) { } /** + * Get whether the Pack indices cache should use strong references or + * SoftReferences + * + * @return {@code true} if the cached Pack indices should use strong references, + * otherwise it will use {@link java.lang.ref.SoftReference}s + * @since 6.7 + */ + public boolean isPackedIndexGitUseStrongRefs() { + return useStrongIndexRefs; + } + + /** + * Set if the Pack indices cache should use strong refs or soft refs + * + * @param useStrongRefs + * if @{code true} the Pack strongly references cached indices + * otherwise it uses {@link java.lang.ref.SoftReference}s which + * can be evicted by the Java gc if heap is almost full + * @since 6.7 + */ + public void setPackedIndexGitUseStrongRefs(boolean useStrongRefs) { + this.useStrongIndexRefs = useStrongRefs; + } + + /** * Get size in bytes of a single window mapped or read in from the pack * file. * @@ -270,6 +299,8 @@ public WindowCacheConfig fromConfig(Config rc) { setPackedGitUseStrongRefs(rc.getBoolean(CONFIG_CORE_SECTION, CONFIG_KEY_PACKED_GIT_USE_STRONGREFS, isPackedGitUseStrongRefs())); + setPackedIndexGitUseStrongRefs(rc.getBoolean(CONFIG_CORE_SECTION, + CONFIG_KEY_PACKED_INDEX_GIT_USE_STRONGREFS, isPackedIndexGitUseStrongRefs())); setPackedGitOpenFiles(rc.getInt(CONFIG_CORE_SECTION, null, CONFIG_KEY_PACKED_GIT_OPENFILES, getPackedGitOpenFiles())); setPackedGitLimit(rc.getLong(CONFIG_CORE_SECTION, null,