根本原因是JVM以低权限用户运行时对默认临时目录(如/tmp)无写权限,或因noexec/nosuid挂载、SELinux/AppArmor策略限制所致;应创建专用目录(如/var/lib/myapp/tmp),设属主为运行用户并chmod 700,再通过-Djava.io.tmpdir显式指定。

OpenJDK服务启动时提示 java.io.IOException: Permission denied 写入 /tmp 或 $JAVA_HOME/tmp
根本原因不是 JDK 本身出错,而是 JVM 启动后尝试在临时目录创建文件(如 JIT 编译缓存、java.io.tmpdir 指向路径下的 socket 文件、JFR 日志等),但当前运行用户对该目录无写权限。常见于用低权限用户(如 tomcat、appuser)运行服务,而 /tmp 虽全局可写,但某些加固系统启用了 noexec 或 nosuid 挂载选项,或使用了 systemd-tmpfiles 隔离机制。
验证方式:ls -ld /tmp 看是否含 sticky 位(通常为 drwxrwxrwt),再用运行用户测试:sudo -u appuser touch /tmp/test.$$ && rm /tmp/test.$$。失败即确认权限问题。
如何安全指定并授权自定义临时目录(非 /tmp)
不建议直接 chmod 777 /tmp 或对系统 /tmp 做任何修改 —— 这会破坏沙箱隔离,且 systemd 可能每小时自动清理或重挂载。正确做法是为服务单独配置一个受控临时路径:
- 创建专用目录:
sudo mkdir -p /var/lib/myapp/tmp - 设属主和基础权限:
sudo chown appuser:appuser /var/lib/myapp/tmp;sudo chmod 700 /var/lib/myapp/tmp(仅属主读写执行) - 在 JVM 启动参数中显式指定:
-Djava.io.tmpdir=/var/lib/myapp/tmp - 若服务由 systemd 托管,确保
RuntimeDirectory=tmp不与该路径冲突;更稳妥是在 service 文件中加Environment="JAVA_OPTS=-Djava.io.tmpdir=/var/lib/myapp/tmp"
注意:该目录无需递归 chmod,JVM 自身不会在其中建深层子目录;但需确保父路径(如 /var/lib/myapp)对 appuser 至少有执行(x)权限,否则无法进入。
为什么 chmod -R 755 在 JDK 安装目录下无效甚至危险
有人误以为给 $JAVA_HOME/jre/tmp 赋权就能解决,这是典型误区:
-
$JAVA_HOME/jre/tmp并非 JVM 默认使用的临时目录 ——java.io.tmpdir默认是系统/tmp,JDK 目录下这个tmp子目录实际未被任何标准组件引用 - 对
$JAVA_HOME目录递归设755或777会暴露lib/下的 native 库、bin/java可执行文件等,若该目录被 web 服务意外映射为静态资源路径,可能引发远程代码泄露 - JDK 安装目录应保持只读(
755对目录、644对 jar、755对 bin 下二进制),仅bin/中的可执行文件需要x位
真正要授权的是「JVM 运行时写入的目标路径」,不是 JDK 的安装路径。
Tomcat + OpenJDK 场景下临时目录权限的连带影响
当 Tomcat 使用 OpenJDK 运行时,除 JVM 自身 tmpdir 外,还需关注 Tomcat 自己的临时工作区:
-
java.io.tmpdir影响 JSP 编译缓存、SSL 临时密钥生成等 -
CATALINA_BASE/work是 Tomcat 编译 JSP 的默认目录,必须对运行用户(如tomcat)可写,且不能放在 NFS 或无exec权限的挂载点上 - 若同时设置
-Djava.io.tmpdir=/var/lib/tomcat9/tmp,则需同步确保/var/lib/tomcat9/tmp和/var/lib/tomcat9/work都由tomcat用户拥有,且权限为700 - 不要把
work和tmp设为同一目录 —— Tomcat 的work会频繁创建/删除大量小文件,混用易导致 JVM 缓存污染或 inode 耗尽
最易忽略的一点:SELinux 或 AppArmor 启用时,即使文件权限全对,也可能因策略限制阻止 JVM 写入自定义 tmp 目录 —— 此时需检查 audit.log 并用 setsebool 或调整 profile。


















