You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用JGit批量克隆Gerrit仓库时触发Too many open files错误

批量克隆Gerrit仓库时出现"Too many open files"错误

使用JGit循环克隆Gerrit上的2000+代码仓库,克隆到约第1000个时失败,报错创建锁文件失败,提示Too many open files。

克隆实现代码

override fun downloadProject(moduleName: String): Path {
    val tempDirectory = Files.createTempDirectory(moduleName)
    val cloneCommand = Git.cloneRepository()
    moduleInfoDao.getModuleByName(moduleName).codeRepository?.let {
        cloneCommand.setURI(makeSshUri(it))
    } ?: throw ModuleWithoutRepositoryException()
    cloneCommand.setTransportConfigCallback { transport ->
        val sshTransport = transport as SshTransport
        sshTransport.sshSessionFactory = sshSessionFactory
    }
    cloneCommand.setDirectory(tempDirectory.toFile())
    cloneCommand.setBare(false)
    val cloneResult = cloneCommand.call()

    cloneResult.close()
    cloneCommand?.repository?.close()

    return tempDirectory
}

异常堆栈信息

> 2022-09-24 19:26:15.941 [ool-request-processor-thread-1] ERROR Creating lock file /var/folders/6_/h619j3tx5fscmn4q2wr0vqth0000gp/T/module-agents-offline15007549319743888409/.git/HEAD.lock failed - <o.e.j.internal.storage.file.LockFile> - org.eclipse.jgit.internal.storage.file.LockFile.lock(LockFile.java:144) 
java.nio.file.FileSystemException: /var/folders/6_/h619j3tx5fscmn4q2wr0vqth0000gp/T/module-agents-offline15007549319743888409/.git/HEAD.lock: Too many open files
    at java.base/sun.nio.fs.UnixException.translateToIOException(UnixException.java:100)
    at java.base/sun.nio.fs.UnixException.rethrowAsIOException(UnixException.java:106)
    at java.base/sun.nio.fs.UnixException.rethrowAsIOException(UnixException.java:111)
    at java.base/sun.nio.fs.UnixFileSystemProvider.newByteChannel(UnixFileSystemProvider.java:218)
    at java.base/java.nio.file.Files.newByteChannel(Files.java:375)
    at java.base/java.nio.file.Files.createFile(Files.java:652)
    at org.eclipse.jgit.util.FS_POSIX.createNewFileAtomic(FS_POSIX.java:428)
    at org.eclipse.jgit.internal.storage.file.LockFile.lock(LockFile.java:142)
    at org.eclipse.jgit.internal.storage.file.RefDirectoryUpdate.tryLock(RefDirectoryUpdate.java:56)
    at org.eclipse.jgit.lib.RefUpdate.link(RefUpdate.java:678)
    at org.eclipse.jgit.internal.storage.file.FileRepository.create(FileRepository.java:246)
    at org.eclipse.jgit.api.InitCommand.call(InitCommand.java:103)
    at org.eclipse.jgit.api.CloneCommand.init(CloneCommand.java:275)
    at org.eclipse.jgit.api.CloneCommand.call(CloneCommand.java:173)
    at ru.myorganization.payin.modules.quality.service.GitDownloaderService.downloadProject(GitDownloaderService.kt:48)
    at ru.myorganization.payin.modules.quality.service.ProjectDownloadingAbstractnessService.getAbstractnessFactor(ProjectDownloadingAbstractnessService.kt:15)
    at ru.myorganization.payin.modules.quality.service.StableAbstractionsPrincipleService.getWarnings(StableAbstractionsPrincipleService.kt:25)
    at ru.myorganization.payin.modules.quality.service.StableAbstractionsPrincipleService$$FastClassBySpringCGLIB$$787d1f0a.invoke(<generated>)
    at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:783)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:753)
    at org.springframework.cache.interceptor.CacheInterceptor.lambda$invoke$0(CacheInterceptor.java:54)
    at org.springframework.cache.interceptor.CacheAspectSupport.invokeOperation(CacheAspectSupport.java:366)
    at org.springframework.cache.interceptor.CacheAspectSupport.execute(CacheAspectSupport.java:421)
    at org.springframework.cache.interceptor.CacheAspectSupport.execute(CacheAspectSupport.java:345)
    at org.springframework.cache.interceptor.CacheInterceptor.invoke(CacheInterceptor.java:64)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:753)
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:698)
    at ru.myorganization.payin.modules.quality.service.StableAbstractionsPrincipleService$$EnhancerBySpringCGLIB$$42c48c58.getWarnings(<generated>)
    at ru.myorganization.payin.modules.quality.service.WarningsCompositeService.getWarnings(WarningsCompositeService.kt:19)
    at ru.myorganization.payin.modules.quality.controllers.scheduler.FindModuleWarningsScheduler.setWarnings(FindModuleWarningsScheduler.kt:43)
    at ru.myorganization.payin.modules.quality.controllers.scheduler.FindModuleWarningsScheduler.analyzeAllModules(FindModuleWarningsScheduler.kt:25)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:64)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:564)
    at org.springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:84)
    at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54)
    at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
    at java.base/java.util.concurrent.FutureTask.runAndReset$$$capture(FutureTask.java:305)
    at java.base/java.util.concurrent.FutureTask.runAndReset(FutureTask.java)
    at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:305)
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1130)
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:630)
    at java.base/java.lang.Thread.run(Thread.java:832)

问题原因及解决办法

原因分析

Too many open files错误是因为进程打开的文件句柄数超过系统限制。现有代码虽调用了关闭方法,但存在资源释放不彻底的情况:

  • JGit克隆过程中会打开多个底层文件流,单纯关闭Git实例和Repository可能遗漏部分句柄
  • 循环克隆时资源释放不及时,导致句柄持续累积

解决步骤

  1. 用use函数确保资源自动释放
    Kotlin的use函数可自动管理资源生命周期,确保所有关联文件句柄被关闭:

    override fun downloadProject(moduleName: String): Path {
        val tempDirectory = Files.createTempDirectory(moduleName)
        val cloneCommand = Git.cloneRepository()
        moduleInfoDao.getModuleByName(moduleName).codeRepository?.let {
            cloneCommand.setURI(makeSshUri(it))
        } ?: throw ModuleWithoutRepositoryException()
        cloneCommand.setTransportConfigCallback { transport ->
            val sshTransport = transport as SshTransport
            sshTransport.sshSessionFactory = sshSessionFactory
        }
        cloneCommand.setDirectory(tempDirectory.toFile())
        cloneCommand.setBare(false)
        
        // use块结束后自动关闭Git实例及关联资源
        cloneCommand.call().use { /* 克隆完成后的操作 */ }
        
        cloneCommand.repository?.close()
        return tempDirectory
    }
    
  2. 及时清理临时目录(可选)
    如果克隆完成后不需要保留临时目录,可在方法末尾添加清理逻辑,释放目录关联的句柄:

    // 仅当后续无需使用临时目录时执行
    // Files.walk(tempDirectory).sorted(Comparator.reverseOrder()).forEach(Files::delete)
    
  3. 调高系统文件句柄限制
    若代码优化后仍报错,可临时调高进程的文件句柄限制:

    • Linux临时生效:执行ulimit -n 4096
    • Linux永久生效:编辑/etc/security/limits.conf,添加:
      soft nofile 4096
      hard nofile 8192
      
  4. 限制并发克隆数
    如果是并发克隆导致句柄激增,控制并发线程数,避免同时打开过多资源。


内容的提问来源于stack exchange,提问作者алексей лашнев

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 19:05:39