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

cron定时启动Java程序报无法创建本地线程OOM错误排查

问题描述

我有一个Java程序,在UNIX web服务器上通过普通shell命令(java -jar xxx.jar)启动时运行完全正常。
但我尝试通过cron配置定时运行该程序时,收到如下错误:

[0.075s][warning][os,thread] Failed to start thread - pthread_create failed (EAGAIN) for attributes: stacksize: 1024k, guardsize: 0k, detached.
Exception in thread "main" java.lang.OutOfMemoryError: unable to create native thread: possibly out of memory or process/resource limits reached
    at java.base/java.lang.Thread.start0(Native Method)
    at java.base/java.lang.Thread.start(Thread.java:801)

经初步判断,该问题并非Java编码错误,因为通过shell启动时程序运行无任何异常;也并非cron服务本身故障,因为其他Java程序可通过cron正常执行。
我了解到cron运行环境存在系统资源配额限制,因此首先检查了系统的用户资源限制配置:

$ ulimit -a
core file size          (blocks, -c) unlimited
data seg size           (kbytes, -d) unlimited
scheduling priority             (-e) 0
file size               (blocks, -f) unlimited
pending signals                 (-i) 1545091
max locked memory       (kbytes, -l) 65536
max memory size         (kbytes, -m) unlimited
open files                      (-n) 1024
pipe size            (512 bytes, -p) 8
POSIX message queues     (bytes, -q) 819200
real-time priority              (-r) 0
stack size              (kbytes, -s) unlimited
cpu time               (seconds, -t) unlimited
max user processes              (-u) 62987
virtual memory          (kbytes, -v) unlimited
file locks                      (-x) unlimited

随后我调整了cron服务对应的systemd配置,执行命令如下:

$ systemctl stop cron
$ sudo systemctl edit cron
$ systemctl daemon-reload
$ systemctl start cron

在systemctl edit cron打开的配置文件中,我参考ulimit与systemd参数的映射规则,配置了与ulimit一致的资源限制值:

TasksMax=unlimited
LimitCORE=unlimited
LimitDATA=unlimited
LimitFSIZE=unlimited
LimitSIGPENDING=1545091
LimitMEMLOCK=65536
LimitRSS=unlimited
LimitNOFILE=1024
LimitSTACK=unlimited
LimitCPU=unlimited
LimitNPROC=62987
LimitAS=unlimited
LimitLOCKS=unlimited

但上述调整并未解决问题,cron启动该程序时仍然抛出相同错误。


解决方案

你修改cron服务的systemd全局限制不生效的核心原因是:cron执行任务时默认会通过PAM模块加载独立的资源限制,不会直接继承cron服务本身的systemd limit配置,按以下顺序排查修复即可:

  • 检查cron的PAM限制配置
    打开/etc/security/limits.conf以及/etc/security/limits.d/目录下的所有配置文件,确认运行Java程序的对应用户,有没有单独配置nproc(最大进程/线程数)、stack(栈大小)、as(虚拟内存)的硬限制。多数发行版默认会给普通用户设置远低于全局ulimit的nproc硬限制,你在交互式shell中看到的ulimit值是登录会话加载的配置,和cron走PAM的加载路径完全不一致。
    配置参考(将your_username替换为实际运行程序的用户名):
    your_username soft nproc 65535
    your_username hard nproc 65535
    your_username soft as unlimited
    your_username hard as unlimited
    your_username soft stack unlimited
    your_username hard stack unlimited
    
    配置完成后无需重启cron服务,下次任务触发时会自动加载新规则。
  • 校验cron环境实际生效的资源限制
    交互式shell会自动加载/etc/profile、~/.bashrc等环境配置,cron默认环境变量极简,不会加载上述配置,很可能出现资源限制、JVM参数和你手动执行时不一致的问题。你可以先在cron任务中增加打印当前资源限制的逻辑,确认实际生效的配置:
    * * * * * ulimit -a > /tmp/cron_ulimit.log 2>&1; /usr/bin/java -jar /path/to/xxx.jar > /tmp/app_run.log 2>&1
    
    等任务触发后查看/tmp/cron_ulimit.log,重点核对max user processes、virtual memory、stack size三个值是否和手动执行shell时的ulimit结果一致,如果存在差异,说明资源限制没有正确加载。
  • 检查systemd用户切片默认限制
    即使正确配置了cron服务的Limit参数,systemd给每个用户分配的user.slice默认存在TasksMax限制,默认值通常为系统总可用PID数的1/4左右,cgroup层面的限制会覆盖ulimit配置,触发pthread_create的EAGAIN错误。
    执行sudo systemctl edit user.slice,填入以下配置:
    [Slice]
    TasksMax=infinity
    LimitNPROC=infinity
    
    保存后执行systemctl daemon-reload即可生效。
  • 临时绕过方案
    如果以上配置调整后仍不生效,可以直接在cron任务中通过systemd-run启动Java程序,手动指定资源限制,完全绕开cron的默认限制规则:
    * * * * * /usr/bin/systemd-run --user --scope -p TasksMax=infinity -p LimitNPROC=infinity /usr/bin/java -jar /path/to/xxx.jar
    

额外排查点:如果服务器开启了SELinux,也可能拦截cron环境下的进程资源申请,可以临时执行setenforce 0测试,如果问题消失再针对性调整SELinux规则即可,不建议长期关闭SELinux。


内容的提问来源于stack exchange,提问作者Ingo

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.03 04:27:25