【问题标题】:Is there a way to limit the amount of memory that "git gc" uses?有没有办法限制“git gc”使用的内存量?
【发布时间】:2011-03-06 23:04:27
【问题描述】:

我在共享主机上托管一个 git 存储库。我的 repo 中必然有几个非常大的文件,现在每次我尝试在 repo 上运行“git gc”时,我的进程都会因使用过多内存而被共享主机提供商杀死。有没有办法限制 git gc 可以消耗的内存量?我希望它可以用内存来换取速度,并且只需要更长的时间来完成它的工作。

【问题讨论】:

  • 是的,在 Dreamhost 上遇到了类似的问题(这个问题被标记了)。 git 被杀死的情况并不多,但 darcs(另一个 VCS)总是被杀死,所以它在 Dreamhost.com 上无法使用

标签: git memory dreamhost git-gc


【解决方案1】:

是的,请查看git config 的帮助页面并查看pack.* 选项,特别是pack.depthpack.windowpack.windowMemorypack.deltaCacheSize

这不是一个完全精确的大小,因为 git 需要将每个对象映射到内存中,因此无论窗口和增量缓存设置如何,一个非常大的对象都可能导致大量内存使用。

您可能会更幸运地在本地打包并将打包文件“手动”传输到远程端,添加 .keep 文件,这样远程 git 就不会尝试完全重新打包所有内容。

【讨论】:

    【解决方案2】:

    您可以使用关闭 delta 属性来仅为这些路径名的 blob 禁用 delta 压缩:

    foo/.git/info/attributes(或foo.git/info/attributes,如果它是一个裸存储库)(请参阅gitattributes 中的增量条目并参阅gitignore 以了解模式语法):

    /large_file_dir/* -delta
    *.psd -delta
    /data/*.iso -delta
    /some/big/file -delta
    another/file/that/is/large -delta
    

    这不会影响存储库的克隆。要影响其他存储库(即克隆),请将属性放在 .gitattributes 文件中,而不是(或附加到)info/attributes 文件中。

    【讨论】:

    • 这是迄今为止关于大文件的最有用的答案。谢谢。我有一些 PSD 的 repo,它过去需要千兆字节的内存来执行 git gc,现在它需要不到 100MB 的 RAM。酷。
    【解决方案3】:

    我使用了来自 link 的说明。与Charles Baileys 建议的想法相同。

    命令的副本在这里:

    git config --global pack.windowMemory "100m"
    git config --global pack.packSizeLimit "100m"
    git config --global pack.threads "1"
    

    这对我在具有共享主机帐户的 hostgator 上有效。

    【讨论】:

    • 谢谢!这对我有用,但我认为第二行有一个错字——没有 SizeLimit 选项;它应该是: git config --global pack.packSizeLimit "100m"
    • 这非常有效。如果一开始不起作用,请尝试对 windowMemory 和 packSizeLimit 设置下限。就我而言,25m 是最佳位置。
    • 我更改了选项名称。原始链接已损坏,不知道指向哪里。
    • 我已将损坏的链接更新为来自 Wayback Machine 的存档。
    • 似乎它对我来说可以避免致命的崩溃,但现在我得到了一个“警告:次优包 - 内存不足”(但 git 无论如何都会完成)。可能我应该尝试将大小设置为超过 100mb,看看它是否仍然完成。毕竟最初它试图用 24 个线程来做,所以限制为 1 应该已经有很大帮助了......
    【解决方案4】:

    Git repack 的内存使用是:(pack.deltaCacheSize + pack.windowMemory) × pack.threads。各自的默认值为 256MiB、无限制、nproc。

    增量缓存没有用:大部分时间都花在计算滑动窗口上的增量上,其中大部分都被丢弃了;缓存幸存者以便它们可以被重用一次(在编写时)不会改善运行时间。该缓存也不在线程之间共享。

    默认情况下,窗口内存通过pack.window (gc.aggressiveWindow) 进行限制。以这种方式限制打包是一个坏主意,因为工作集的大小和效率会有很大差异。最好将两者都提高到更高的值并依靠pack.windowMemory 来限制窗口大小。

    最后,线程化有分裂工作集的缺点。降低 pack.threads 并增加 pack.windowMemory 以使总数保持不变应该会提高运行时间。

    repack 还有其他有用的可调参数(pack.depthpack.compression、位图选项),但它们不会影响内存使用。

    【讨论】:

    【解决方案5】:

    Git 2.18(2018 年第二季度)将改善 gc 内存消耗。
    在 2.18 之前,“git pack-objects”在工作时需要分配大量的“struct object_entry”:缩小其大小有助于提高性能
    这会影响git gc

    commit f6a5576commit 3b13a5fcommit 0aca34ecommit ac77d0ccommit 27a7d06commit 660b373commit 0cb3c14commit 898eba5commit 43fa44fcommit b5c0cbd、@9876543332 @、commit fd9b1bacommit 8d6cccecommit 4c2db93(2018 年 4 月 14 日)Nguyễn Thái Ngọc Duy (pclouds)
    (由 Junio C Hamano -- gitster -- 合并于 commit ad635e8,2018 年 5 月 23 日) p>

    pack-objects:重新排序成员以缩小struct object_entry

    以前的补丁在这个结构中留下了很多洞和填充。
    此补丁重新排序成员并将结构缩小到 80 字节 (从 64 位系统上的 136 字节开始,在完成任何字段收缩之前) 有 16 位备用(在 in_pack_header_size 中还有更多位) 我们真的用完了比特)。

    这是一系列内存减少补丁中的最后一个(请参阅 “pack-objects: a bit of document about struct object_entry”为 第一个)。

    总体而言,他们减少了 linux-2.6.git 上的重新打包内存大小,从 3.747G 到 3.424G,或约 320M,下降 8.5%。
    在本系列中,重新打包的运行时间保持不变。
    Ævar 对他可以访问的大型 monorepo(大于 linux-2.6.git)进行的测试显示减少了 7.9%,因此总体预期改进应该在 8% 左右。


    使用 Git 2.20(2018 年第 4 季度),将更容易检查存在于一个分叉中的对象是否未针对未出现在同一分叉存储库中的另一个对象进行增量。

    请参阅commit fe0ac2fcommit 108f530commit f64ba53(2018 年 8 月 16 日)Christian Couder (chriscool)
    帮助者:Jeff King (peff)Duy Nguyen (pclouds)
    请参阅commit 9eb0986commit 16d75facommit 28b8a73commit c8d521f(2018 年 8 月 16 日)Jeff King (peff)
    帮助者:Jeff King (peff)Duy Nguyen (pclouds)
    (由 Junio C Hamano -- gitster -- 合并于 commit f3504ea,2018 年 9 月 17 日)

    pack-objects:将“layer”移动到“struct packing_data

    这将 'struct object_entry' 的大小从 88 字节减少到 80 字节,因此打包对象的效率更高。

    例如在具有 12M 对象的 Linux 存储库中,git pack-objects --all 需要额外的 96MB 内存,即使未使用层功能。


    请注意,Git 2.21(2019 年 2 月)修复了一个小错误:“git pack-objects”错误地使用了未初始化的互斥锁,已更正。

    commit edb673ccommit 459307b(2019 年 1 月 25 日)Patrick Hogg (``)
    帮助者:Junio C Hamano (gitster)
    (由 Junio C Hamano -- gitster -- 合并于 commit d243a32,2019 年 2 月 5 日)

    pack-objects:将读取互斥体移至packing_data struct

    ac77d0c ("pack-objects: 结构object_entry中的收缩大小字段", 2018-04-14) 在新版本中增加了 read_lock/read_unlock 的额外用法 引入oe_get_size_slow 以确保并行调用中的线程安全 try_delta().
    不幸的是oe_get_size_slow也被串行使用 代码,其中一些在第一次调用之前调用 ll_find_deltas.
    因此不保证读取的互斥锁会被初始化。

    通过将读取互斥体移动到 packing_data 并初始化来解决此问题 它在 cmd_pack_objects 中初始化的 prepare_packing_data 中。


    Git 2.21(2019 年 2 月)仍然找到另一种方法来缩小包的大小,“git pack-objects”学习另一种算法来计算 要发送的对象,交换生成的包文件以保存 有利于小推送的遍历成本。

    pack-objects:创建pack.useSparse设置

    git pack-objects”中的“--sparse”标志改变了算法 用于将对象枚举为对个人来说更快的对象 用户推动的新对象只改变了一个小圆锥 工作目录。
    不建议将稀疏算法用于服务器,因为它可能会发送出现在整个工作目录中的新对象。

    创建启用此新算法的“pack.useSparse”设置。
    这允许 'git push' 使用此算法而无需传递 '--sparse'标志一路贯穿run_command()的四个级别 来电。

    如果设置了“--no-sparse”标志,则此配置设置为 被覆盖。

    config pack documentation 现在包括:

    pack.useSparse:
    

    当为 true 时,Git 将默认使用 '--sparse' 选项 存在“--revs”选项时为“git pack-objects”。
    此算法仅遍历出现在引入新对象的路径中的树。

    在计算包以发送小额零钱时,这可以带来显着的性能优势。

    但是,如果包含的提交包含某些类型的直接重命名,则可能会将额外的对象添加到包文件中。

    具体说明请参见“git push is very slow for a huge repo”。


    注意:正如 Git 2.24 中所述,pack.useSparse 之类的设置仍处于试验阶段。

    参见commit aaf633ccommit c6cc4c5commit ad0fb65commit 31b1de6commit b068d9acommit 7211b9e(2019 年 8 月 13 日)Derrick Stolee (derrickstolee)
    (由 Junio C Hamano -- gitster -- 合并commit f4f8dfe,2019 年 9 月 9 日)

    repo-settings:创建feature.experimental设置

    feature.experimental”设置包括未承诺成为默认值但可以使用额外测试的配置选项g。

    更新以下配置设置以采用新的默认值,并 如果尚未使用 repo_settings 结构,请使用它们:

    • 'pack.useSparse=true'
    • 'fetch.negotiationAlgorithm=skipping'

    在 Git 2.26(2020 年第一季度)中,“git pack-objects”重用存储在现有包中的对象以生成其结果的方式得到了改进。

    参见commit d2ea031commit 92fb0dbcommit bb514decommit ff48302commit e704fc7commit 2f4af77commit 8ebf529commit 59b2829commit 59b2829commit 40d18ff、@987654384 和@(12 月 18 日) commit 56d9cbecommit bab28d9(2019 年 9 月 13 日)Jeff King (peff)
    (由 Junio C Hamano -- gitster -- 合并于 commit a14aebe,2020 年 2 月 14 日)

    pack-objects:改进部分包文件重用

    协助人:Jonathan Tan
    签字人:Jeff King
    签字人:Christian Couder支持>

    重用现有包文件中的增量的旧代码只是试图逐字转储包的整个片段。这比将对象实际添加到装箱单中的传统方式要快,但它并不经常启动。这段新代码确实是在寻求中间立场:为每个对象做 一些 工作,但比我们传统上做的要少。

    新代码的一般策略是从我们将包含的包文件中制作对象的位图,然后对其进行迭代,将每个对象完全按照我们的磁盘包中的内容写出,但是 不要将它添加到我们的包列表中(这会消耗内存,并增加增量的搜索空间)。

    一个复杂的问题是,如果我们省略了一些对象,我们就不能针对我们不发送的基础设置增量。所以我们必须检查try_partial_reuse() 中的每个对象,以确保我们有它的增量。

    关于性能,在最坏的情况下,我们可能会交错发送或不发送的对象,并且我们会拥有与对象一样多的块。但在实践中,我们发送大块。

    例如,在 GitHub 服务器上打包 torvalds/linux 现在重用了 650 万个对象,但只需要大约 5 万个块。


    在 Git 2.34(2021 年第四季度)中,git repack 本身(由git gc 使用)受益于内存使用量的减少。

    参见Taylor Blau (ttaylorr)commit b017334commit a9fd2f2commit a241878(2021 年 8 月 29 日)。
    (由 Junio C Hamano -- gitster -- 合并到 commit 9559de3,2021 年 9 月 10 日)

    builtin/pack-objects.c:删除重复的哈希查找

    签字人:Taylor Blau

    08cdfb1("pack-objects --keep-unreachable", 2007-09-16, Git v1.5.4-rc0 -- merge) 的原始代码中,我们将每个对象添加到装箱单中,类型为``obj->type, 其中obj 来自lookup_unknown_object()
    除非我们已经查找并解析了对象,否则它将是 OBJ_NONE
    没关系,因为oe_set_type()type_valid 位设置为“0”,我们稍后会确定真正的类型。

    所以我们在对象查找中唯一需要的是访问flags 字段,这样我们就可以用OBJECT_ADDED 标记我们已经添加了对象以避免再次添加它(我们可以传递OBJ_NONE直接而不是从对象中抓取它)。

    add_object_entry() 已经拒绝重复!这是自 7a979d9 以来的行为(“瘦包 - 创建缺少 delta 基础的包文件。”,2006-02-19,Git v1.3.0-rc1 -- merge),但 08cdfb1 没有采取优势。
    此外,要进行OBJECT_ADDED 检查,我们必须在obj_hash 中进行哈希查找。

    所以我们可以完全放弃 lookup_unknown_object() 调用, OBJECT_ADDED 标志也是如此,因为我们在这里触摸的位置是唯一检查它的位置。

    最后,我们执行相同数量的哈希查找,但额外的好处是我们不会浪费内存分配OBJ_NONE 对象(如果我们正在遍历,我们最终会需要它,但整个这段代码路径的重点是不要遍历)。

    【讨论】:

      猜你喜欢
      • 2017-06-29
      • 1970-01-01
      • 2016-04-17
      • 2014-04-26
      • 1970-01-01
      • 1970-01-01
      • 2021-06-18
      • 1970-01-01
      相关资源
      最近更新 更多