macOS 应用强制退出后无法再次启动:根因分析、排查方法与无重启恢复方
记录时间2026-06-08实测环境macOS 26.5Build 25F71Apple Silicon涉及应用IntelliJ IDEA、Codex、CC Switch本次实测 IDEA 版本IntelliJ IDEA 2026.1.2本次实测 JetBrains RuntimeJBR 25.0.2本次实测 Codex 版本26.602.40724一、问题现象某些 macOS 桌面应用原本可以正常运行卡死后被强制退出随后出现以下现象再次点击 Dock、Launchpad 或“应用程序”中的图标没有任何可见反应。执行open -a 应用名称命令很快返回但应用窗口没有出现。杀掉应用相关进程后仍无法启动。重新安装应用通常不是必需的。重启 Mac 后应用又可以正常启动。问题不是固定发生在一个应用上IntelliJ IDEA、Codex、CC Switch 都曾出现过。这类现象很容易被误判为App 文件损坏应用缓存损坏应用锁文件没有删除Java 进程没有杀干净Dock 或 LaunchServices 缓存异常。本次现场排查表明实际问题至少分成两层应用为什么会先卡死强制退出后为什么新进程无法完成启动。这两层问题必须分开分析。二、结论摘要2.1 IDEA 最初卡死的直接原因IDEA 的多份 freeze thread dump 显示UI 主线程AWT-EventQueue-0一直等待Java2D DisposerJava2D Disposer又在等待 macOS Metal 渲染队列关键调用包括sun.java2d.metal.MTLRenderQueue.flushAndInvokeNow sun.java2d.Disposer FontStrikeDisposer StrikeCache.disposeStrike这说明 IDEA 最初的卡死发生在JetBrains Runtime 的 Java2D Metal 渲染路径。本次对 IDEA 增加了下面的用户级 JVM 参数-Dsun.java2d.metalfalse该参数关闭 Java2D 的 Metal 渲染后端改用其他渲染路径用来规避这类 Metal 渲染线程卡死。2.2 强制退出后无法再次启动的直接原因IDEA 被强制退出后新启动出来的 IDEA 进程并不是一个正常 JVM而只是一个很小的启动器空壳。对启动器采样后发现线程卡在JNI_CreateJavaVM LoadJavaVM dlopen dyld4::Loader::mapSegments fcntl也就是说新 IDEA 还没有创建 JVM也没有读取 IDEA 的锁文件、缓存或项目配置就已经卡在 macOS 动态库装载和系统策略校验阶段。与此同时macOS 的syspolicyd日志持续出现UNIX error exception: 24 Failed to generate SecStaticCode ... error: 100024 failed to call driver: 0x3其中 Unixerrno 24的含义是EMFILE: Too many open files进一步检查syspolicyd的文件描述符时发现该进程打开了约 2575 个文件句柄其中约 2536 个句柄都重复指向/Applications/Codex.app/Contents/MacOS/Codex因此本次“强退后再次点击没有反应”的直接原因是macOS 的系统策略服务syspolicyd出现了文件描述符泄漏或异常积压无法继续完成新可执行文件和动态库的代码签名、来源及安全策略检查。新应用虽然被 LaunchServices 创建了进程但进程卡在非常早期的加载阶段。2.3 为什么重启 Mac 一定有效重启会同时完成下面几件事结束异常的syspolicyd清空它泄漏或积压的文件描述符清理 LaunchServices、RunningBoard 中记录的半启动应用状态清除应用启动器空壳重新初始化动态链接和代码校验相关系统服务。所以重启能恢复但这并不代表 App 文件已经被重装修复。三、IDEA、Codex、CC Switch 三个问题是否有关3.1 有关联但不是“IDEA 导致其他两个应用坏了”更准确的关系是IDEA、Codex、CC Switch │ ├── 各自可能有不同的初始卡死原因 │ └── 强制退出后重新启动时都要经过相同的 macOS 启动基础设施 │ ├── LaunchServices ├── RunningBoard ├── dyld ├── amfid └── syspolicyd因此IDEA 的 Metal 渲染卡死只解释 IDEA 为什么先失去响应它不能解释 Codex 和 CC Switch 以前为什么也出现过相同症状三个应用共同出现“强退后无法再次启动、重启后恢复”说明它们很可能共享了同一类系统启动层故障本次现场已经实证syspolicyd发生文件描述符耗尽以前 Codex、CC Switch 出现问题时没有保留当时日志因此只能判断为高度可能属于同一故障类型不能百分之百断言当时也是同一个 PID、同一个泄漏目标。3.2 Codex 在本次故障中的角色本次重启syspolicyd后新的syspolicyd很快又出现大量重复打开 Codex 主程序的文件句柄。这说明当前环境中Codex 相关的可执行文件校验或其子进程来源校验是触发、放大或暴露syspolicyd文件描述符泄漏的重要条件。但是不能仅凭这个现象就断言“Codex 应用逻辑主动泄漏了文件”。原因是这些句柄属于 root 用户运行的syspolicyd不是 Codex 自己的句柄。更可能的情况是Codex 或 Codex 启动的子进程频繁触发 macOS provenance/code-signing 检查macOS 26.5 的syspolicyd在处理这些请求时没有正确关闭 Codex 主程序句柄请求积累后syspolicyd达到自己的文件描述符上限后续任何应用的代码校验或动态库装载都可能被拖死。3.3 Codex 是否被本地命令改坏本次从 OpenAI 官方地址重新下载了 Codex DMG并做了只读对照官方 DMG 校验通过官方 DMG 内的 Codex 版本也是26.602.40724官方包中的 Codex 主程序与/Applications/Codex.app主程序 SHA-256 完全一致官方包和已安装副本在当前 macOS 上执行严格codesign --verify时表现一致。所以当前/Applications/Codex.app不是被用户历史命令单独改坏的副本。重新安装完全相同的官方版本未必能解决当前syspolicyd问题。更值得关注的是macOS 26.5Codex 26.602.40724系统策略服务syspolicydCodex 及其子进程触发的 provenance/code-signing 校验这几者之间的组合行为。3.4 CC Switch 的情况历史中执行过codesign--force--deep--sign- /Applications/CC\Switch.app xattr-cr/Applications/CC\Switch.app它们的影响是codesign --sign -会把 CC Switch 改成 ad-hoc 签名--deep会递归处理应用包中的嵌套组件xattr -cr会递归移除隔离、来源和其他扩展属性这些修改只直接作用于 CC Switch不会直接修改 IDEA 或 Codex。因此它们不能解释 IDEA 和 Codex 也出现同类问题它们可能让 CC Switch 自己的签名、更新和系统来源校验变得更复杂后续不建议把“重新签名整个 App”作为常规修复手段。四、历史命令与本次故障的关系历史操作是否是本次根因说明git commit、git remote、git push否与 macOS 应用启动链路无关设置或取消HTTP_PROXY、HTTPS_PROXY基本无关只影响网络请求和 Homebrew 下载不会造成本地 JVM 卡在dlopen/fcntlHomebrew 下载 CC Switch 失败否只是代理端口配置错误npm run dev否与 IDEA 主程序装载无直接关系pkill -f IntelliJ IDEA是触发动作不是根因强制终止了应用但没有修复已经异常的系统策略服务pkill -f fsnotifier不是根因只影响 JetBrains 文件监控子进程open -a IntelliJ IDEA不是根因只是提交启动请求系统服务异常时它可能无错误返回对 CC Switch 执行codesign --force --deep --sign -只影响 CC Switch可能带来 CC Switch 自身签名和更新风险但不会直接改坏 IDEA 或 Codex对 CC Switch 执行xattr -cr只影响 CC Switch移除该 App 的扩展属性不是全局修复五、为什么open -a没报错但应用就是不出来open -a的主要职责是把启动请求交给 LaunchServices。它不是在当前终端中同步等待应用完成所有初始化。本次系统日志显示RunningBoard 接受了 IDEA 启动请求系统成功创建了 IDEA PIDRunningBoard 把它标记为running-active但 IDEA 启动器在创建 JVM 时卡住因为系统已经认为 Bundle ID 对应的应用正在运行后续点击图标可能只会尝试激活这个半启动进程所以用户看到的效果就是“点击完全没反应”。这类进程通常具有以下特点PID 存在CPU 很低RSS 内存只有几 MB没有完整 JVM 线程没有打开 IDEA 的配置、缓存、项目文件和本地端口sample显示卡在dlopen、dyld、fcntl、codesign或安全策略相关调用。六、下次遇到问题时的 5 分钟无重启恢复流程以下流程优先恢复系统不删除应用数据也不重装应用。第 0 步保存其他应用中的工作重启syspolicyd通常不会关闭普通应用但执行任何系统级恢复前都建议先保存正在编辑的文件。如果 Codex 正在运行并且不是用 Codex 自己排查问题建议先正常退出 Codex避免它继续创建子进程并触发新的校验请求。第 1 步确认目标应用有没有留下空壳进程IDEAps-axopid,ppid,%cpu,%mem,rss,state,lstart,command\|grep-E/Applications/IntelliJ IDEA.app/Contents/MacOS/idea|fsnotifier\|grep-vgrepCodexps-axopid,ppid,%cpu,%mem,rss,state,lstart,command\|grep-E/Applications/Codex.app/Contents/MacOS/Codex\|grep-vgrepCC Switchps-axopid,ppid,%cpu,%mem,rss,state,lstart,command\|grep-EiCC Switch|cc-switch|ccswitch\|grep-vgrep如果目标应用的主进程存在但内存很小、长时间没有窗口可以先记录 PID。第 2 步确认syspolicyd是否异常查看 CPUps-axopid,ppid,user,%cpu,%mem,state,lstart,command\|grep[s]yspolicyd正常情况下syspolicyd不应该长时间占用很高 CPU。检查最近错误log show--last10m--stylecompact\--predicateprocess syspolicyd\|grep-EUNIX error exception: 24|Failed to generate SecStaticCode|failed to call driver如果反复看到UNIX error exception: 24基本可以确认syspolicyd已达到文件描述符上限。检查打开文件总数sudolsof-nP-p$(pgrep-xsyspolicyd)|wc-l检查是否大量重复打开某个 Appsudolsof-nP-p$(pgrep-xsyspolicyd)\|awkNR 1 { name $9 for (i 10; i NF; i) name name $i count[name] } END { for (name in count) print count[name], name }\|sort-nr\|head-n20如果前几名出现数百或数千次相同的 App 主程序说明已经抓到非常强的文件描述符泄漏证据。第 3 步结束目标应用的半启动空壳先使用普通TERMkill目标应用PID等待两秒后确认ps-p目标应用PID只有进程仍然存在时才使用kill-9目标应用PID注意不要对不确定的 PID 直接执行kill -9如果应用已经完整启动并可能有未保存数据应先正常退出本次 IDEA PID21412已确认没有创建 JVM因此结束它不会丢失项目编辑状态。第 4 步只重启syspolicyd在终端执行sudokillallsyspolicyd输入 Mac 管理员密码。syspolicyd是 launchd 管理的系统服务。终止旧进程后macOS 会自动启动一个新的实例。检查 PID 是否变化pgrep-lfsyspolicyd本次实测中旧 PID 为495重启后变成91960再次重启后又变成87102。PID 变化说明系统服务已经被重新拉起。不要使用下面这条命令作为首选sudolaunchctl kickstart-ksystem/com.apple.security.syspolicy本次在开启 SIP 的正常系统中该命令返回Operation not permitted while System Integrity Protection is engaged没有必要为了这件事关闭 SIP。第 5 步重新启动应用IDEAopen-naIntelliJ IDEACodexopen-naCodexCC Switchopen-naCC Switch如果应用本来就支持单实例也可以使用open-a应用名称在已经清理空壳进程的前提下-n可以明确要求 LaunchServices 创建新实例。第 6 步验证不是“又启动了一个空壳”以 IDEA 为例ps-axopid,ppid,%cpu,%mem,rss,state,lstart,command\|grep/Applications/IntelliJ IDEA.app/Contents/MacOS/idea\|grep-vgrep完整 IDEA JVM 通常会占用数百 MB 到数 GB 内存而不是几 MB。继续确认ps-axopid,ppid,command\|grep-Efsnotifier|jetbrainsd\|grep-vgrep查看最新 IDEA 日志tail-n100$HOME/Library/Logs/JetBrains/IntelliJIdea2026.1/idea.log看到插件加载、项目加载、索引等日志说明 IDEA 已经越过启动器和 JVM 创建阶段。七、IDEA 专项修复关闭 Java2D Metal7.1 推荐方法IDEA 能启动时在菜单中打开Help - Edit Custom VM Options加入-Dsun.java2d.metalfalse然后完全退出并重新启动 IDEA。7.2 IDEA 无法启动时手动修改本次 IDEA 2026.1 的用户级 VM 参数文件是~/Library/Application Support/JetBrains/IntelliJIdea2026.1/idea.vmoptions查看文件cat$HOME/Library/Application Support/JetBrains/IntelliJIdea2026.1/idea.vmoptions避免重复添加VM_OPTIONS$HOME/Library/Application Support/JetBrains/IntelliJIdea2026.1/idea.vmoptionsgrep-qxF---Dsun.java2d.metalfalse$VM_OPTIONS\||printf%s\n-Dsun.java2d.metalfalse$VM_OPTIONS本次最终文件内容为# custom IntelliJ IDEA VM options (expand/override bin/idea.vmoptions) -Dsun.java2d.metalfalse7.3 如何确认参数已经生效执行grep-F---Dsun.java2d.metalfalse\$HOME/Library/Logs/JetBrains/IntelliJIdea2026.1/idea.log\|tail本次新启动日志的JVM options同时出现-Dsun.java2d.metaltrue -Dsun.java2d.metalfalse后出现的用户自定义参数会覆盖前面的默认值因此最终使用false。7.4 不要修改 App 包里的默认 VM 参数不要直接编辑/Applications/IntelliJ IDEA.app/Contents/bin/idea.vmoptions原因更新 IDEA 时可能被覆盖修改 App Bundle 内容可能破坏代码签名JetBrains 提供了用户级 Custom VM Options 机制应该优先使用它。JetBrains 官方说明Tuning the IDE八、完整深度排查流程当快速恢复流程无效时按以下顺序定位。8.1 先判断应用处在哪个启动阶段情况 A完全没有 PID可能原因LaunchServices 没有真正创建进程App Bundle 注册异常路径或 Bundle ID 不正确系统安全策略直接拒绝启动。检查open-a应用名称echo$?然后查看log show--last5m--stylecompact\--predicateprocess launchservicesd OR process runningboardd OR process amfid情况 BPID 存在但内存只有几 MB这通常是启动器空壳。执行采样samplePID51-file$HOME/Desktop/app-launch.sample.txt重点搜索grep-EJNI_CreateJavaVM|dlopen|dyld|fcntl|codesign|syspolicy\$HOME/Desktop/app-launch.sample.txt如果卡在dlopen/dyld/fcntl优先排查syspolicyd不要先删除应用缓存。情况 C完整 JVM 已经启动但没有窗口可能原因窗口状态恢复异常插件卡死项目加载卡死单实例锁或本地端口异常UI 线程死锁图形渲染问题。此时再检查jcmdIDEA_PIDThread.print-l如果jcmd自己也长时间卡住要重新考虑底层系统服务是否异常。情况 D完整 JVM 已启动而且 CPU 或内存异常重点分析IDEA freeze thread dumpGC插件索引文件系统监听Java2D、Metal 或 Skia 渲染外部构建进程。8.2 检查 IDEA freeze dump目录~/Library/Logs/JetBrains/IntelliJIdea2026.1/threadDumps-freeze-*查找 Metal 渲染调用grep-R-n-EMTLRenderQueue|Java2D Disposer|StrikeCache|FontStrikeDisposer\$HOME/Library/Logs/JetBrains/IntelliJIdea2026.1/threadDumps-freeze-*\|head-n100本次的关键关系是AWT-EventQueue-0 waits for Java2D Disposer Java2D Disposer waits for MTLRenderQueue这不是普通的项目索引慢而是 UI 渲染链路阻塞。8.3 检查 IDEA 自带 JBR 是否能独立启动/Applications/IntelliJ IDEA.app/Contents/jbr/Contents/Home/bin/java-version正常时应该很快输出版本。本次在syspolicyd异常时该命令超过 12 秒仍无输出重启syspolicyd后立刻输出openjdk version 25.0.2 OpenJDK Runtime Environment JBR-25.0.2 OpenJDK 64-Bit Server VM JBR-25.0.2这个对照可以证明JBR 文件本身并未损坏卡住的是系统装载和校验链路重装 IDEA 不是当时最有效的动作。8.4 检查锁文件但不要把锁文件当成默认根因IDEA 常见文件~/Library/Application Support/JetBrains/IntelliJIdea2026.1/.lock ~/Library/Caches/JetBrains/IntelliJIdea2026.1/.port只有满足下面条件时才考虑删除已确认没有任何 IDEA 主进程已确认没有 JetBrains 后台组件仍在使用日志明确指向单实例锁或端口备份或记录了原文件。本次 IDEA 启动器在打开这些文件之前就已经卡住因此删除.lock或.port不能解决本次根因。8.5 检查系统内存压力memory_pressure vm_stat本次 Mac 有 64GB 内存空闲充足没有 swap、压缩和 pageout 压力因此不是内存不足。8.6 检查 App 签名时要谨慎解释结果查看签名codesign-d--verbose4/Applications/应用名称.app验证codesign--verify--deep--strict--verbose4/Applications/应用名称.app查看 Gatekeeperspctl--statusspctl--assess--typeexecute--verbose4/Applications/应用名称.app注意codesign --verify失败不一定能单独证明本地文件被篡改应与官方包做哈希和签名对照本次官方 Codex DMG 与已安装 Codex 的主程序哈希相同当前系统中spctl --status显示 assessments disabled这不是本次文件描述符泄漏的直接原因但会让签名问题更难判断不要为了绕过提示长期关闭 Gatekeeper更不要为了本问题关闭 SIP。九、Codex 的长期处理建议9.1 当前版本不建议单纯重复安装本次官方 DMG 与已安装副本版本和主程序哈希一致。因此在仍然是26.602.40724的情况下简单地“删除后重新安装同一个包”未必能改变syspolicyd的表现。9.2 优先升级到后续 Codex 版本当 OpenAI 发布新版本后正常退出 Codex安装新的官方版本不要自行对 Codex 执行codesign --force --deep --sign -不要对 Codex 执行递归xattr -cr作为常规操作升级后观察syspolicyd是否还会大量重复打开 Codex 主程序。查看当前版本/usr/libexec/PlistBuddy\-cPrint :CFBundleShortVersionString\/Applications/Codex.app/Contents/Info.plistOpenAI 官方 Codex App 文档Codex AppApple Silicon 官方安装包Codex.dmg9.3 如果问题复现先退出 Codex 再重启syspolicyd如果不是在 Codex 中排查可先正常退出 Codex然后执行sudokillallsyspolicyd再启动目标应用。这样可以避免重启后的syspolicyd立即继续接收大量来自 Codex 子进程的校验请求。9.4 保存可提交给 OpenAI 或 Apple 的证据复现后立即执行mkdir-p$HOME/Desktop/app-launch-diagnosisps-axopid,ppid,user,%cpu,%mem,rss,state,lstart,command\$HOME/Desktop/app-launch-diagnosis/processes.txtlog show--last15m--stylecompact\--predicateprocess syspolicyd OR process amfid OR process launchservicesd OR process runningboardd\$HOME/Desktop/app-launch-diagnosis/system-launch.logsudolsof-nP-p$(pgrep-xsyspolicyd)\$HOME/Desktop/app-launch-diagnosis/syspolicyd-lsof.txt如果目标应用留下空壳sample目标应用PID101\-file$HOME/Desktop/app-launch-diagnosis/app-launch.sample.txt提交日志前应先检查其中是否包含用户名项目路径本地文件名网络地址Token其他隐私信息。十、CC Switch 的长期处理建议10.1 不再把 ad-hoc 重签作为常规方案下面这类命令只适合明确知道签名后果时使用codesign--force--deep--sign-/Applications/CC Switch.app风险包括改变原开发者签名破坏自动更新校验嵌套组件签名状态变化与系统 provenance、Gatekeeper 记录不一致后续难以判断到底是官方包问题还是本地修改问题。10.2 出现同类现象时先检查系统层如果 CC Switch 再次出现点击无反应open -a CC Switch无明显报错重启 Mac 后恢复应先检查log show--last10m--stylecompact\--predicateprocess syspolicyd\|grep-EUNIX error exception: 24|Failed to generate SecStaticCode不要立刻再次重签整个 App。十一、不要优先做的操作11.1 不要反复点击图标或循环执行open -a它不会修复系统策略服务还可能产生更多半启动请求和日志噪声。11.2 不要一上来就删除整个应用配置目录例如不要直接删除~/Library/Application Support/JetBrains ~/Library/Caches/JetBrains ~/Library/Application Support/Codex本次根因发生在应用读取这些目录之前删除配置只会丢失设置不能修复syspolicyd。11.3 不要默认删除.lock和.port只有日志和进程状态证明是单实例锁问题时才处理。11.4 不要随意重建 LaunchServices 数据库LaunchServices 只是把进程创建出来。本次真正阻塞发生在进程创建后的 JVM/动态库装载阶段。11.5 不要关闭 SIP本问题不需要关闭 SIP。11.6 不要长期关闭 Gatekeeper本次系统显示 Gatekeeper assessments disabled。虽然这不是syspolicyd泄漏的直接原因但长期关闭会降低下载应用的安全校验能力也会使签名排查失去重要参照。11.7 不要把kill -9当作第一选择先尝试正常退出或kill。只有进程不响应并且已确认无未保存状态时才使用kill -9。十二、本次实际执行的恢复步骤本次没有重启 Mac实际完成了以下操作确认 macOS 版本为 26.5系统内存充足找到 IDEA 空壳进程 PID21412使用lsof证明该进程没有打开 IDEA 配置、缓存和锁文件使用sample证明它卡在JNI_CreateJavaVM - dlopen - dyld - fcntl使用 IDEA freeze dump 证明原始 UI 卡死发生在 Java2D Metal 渲染链路从syspolicyd日志中确认errno 24发现syspolicyd大量重复打开 Codex 主程序使用管理员权限终止旧syspolicyd由 launchd 自动拉起新实例验证 IDEA 自带 JBR 的java -version恢复正常在用户级idea.vmoptions中加入-Dsun.java2d.metalfalse结束 IDEA 空壳进程再次重启syspolicyd执行open-naIntelliJ IDEAIDEA 成功启动为完整 JVM新 IDEA PID 为18337RSS 约 1.7GBfsnotifier和jetbrainsd均正常启动IDEA 日志进入项目加载和索引阶段IDEA 日志确认-Dsun.java2d.metalfalse已生效。十三、下次处理时的最短命令清单确认目标应用和系统服务ps-axopid,ppid,user,%cpu,%mem,rss,state,lstart,command\|grep-E[s]yspolicyd|IntelliJ IDEA|Codex|CC Switch确认syspolicyd是否报文件句柄耗尽log show--last10m--stylecompact\--predicateprocess syspolicyd\|grep-EUNIX error exception: 24|Failed to generate SecStaticCode检查句柄总数和重复目标sudolsof-nP-p$(pgrep-xsyspolicyd)|wc-lsudolsof-nP-p$(pgrep-xsyspolicyd)\|grep/Applications/\|awk{print $9}\|sort\|uniq-c\|sort-nr\|head结束已经确认的应用空壳kill空壳PID重启系统策略服务sudokillallsyspolicyd重新启动 IDEAopen-naIntelliJ IDEA确认 IDEA 完整启动ps-axopid,ppid,%cpu,%mem,rss,state,lstart,command\|grep/Applications/IntelliJ IDEA.app/Contents/MacOS/idea\|grep-vgreptail-n100$HOME/Library/Logs/JetBrains/IntelliJIdea2026.1/idea.log十四、最终判断本次问题不是一个单点故障而是两层问题叠加第一层 IDEA 的 Java2D Metal 渲染线程卡死 ↓ 用户强制退出 IDEA 第二层 macOS syspolicyd 已发生文件描述符泄漏/耗尽 ↓ 新 IDEA 在 JVM 和动态库装载阶段卡住 ↓ LaunchServices 认为应用已经启动 ↓ 再次点击图标或 open -a 看起来完全没反应因此最终解决方案也分为两部分恢复启动能力结束应用空壳并重启syspolicyd降低 IDEA 再次卡死概率加入-Dsun.java2d.metalfalse。对于 Codex 和 CC Switch它们与 IDEA 不是同一个应用级故障但很可能共享同一个 macOS 系统启动层故障本次 Codex 明确与syspolicyd的重复文件句柄现象相关以前两次由于没有保存现场日志只能归类为高度相似不能做百分之百的历史追溯。如果以后再次发生优先执行本文的“5 分钟无重启恢复流程”不需要一开始就重启整台 Mac、重装应用、删除配置或重新签名 App。