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

如何在Java中记录调用System.exit()的栈追踪信息?

替代SecurityManager捕获System.exit()调用栈的方案

方案1:Java Agent + 字节码增强(推荐)

通过Java Agent在类加载阶段修改System.exit(int)方法的字节码,在调用原方法前自动记录当前栈追踪。这种方式无需修改业务代码,也不用引入SecurityManager,属于无侵入式实现。

示例思路(用ByteBuddy简化开发):

public class ExitMonitorAgent {
    public static void premain(String args, Instrumentation inst) {
        new ByteBuddy()
            .redefine(System.class)
            .method(named("exit").and(takesArguments(int.class)))
            .intercept(MethodDelegation.to(ExitInterceptor.class))
            .make()
            .load(System.class.getClassLoader(), inst);
    }

    public static class ExitInterceptor {
        public static void exit(int status) throws Exception {
            // 记录调用栈信息
            System.err.println("System.exit(" + status + ") 被调用,调用栈:");
            new RuntimeException().printStackTrace();
            // 调用原exit方法
            Method original = System.class.getDeclaredMethod("exit", int.class);
            original.setAccessible(true);
            original.invoke(null, status);
        }
    }
}

将代码打包为Java Agent Jar,启动应用时通过-javaagent:agent.jar参数加载,即可自动捕获所有System.exit()的调用栈。

方案2:反射替换Runtime实例

通过反射替换Runtime.getRuntime()返回的实例,重写exit(int)方法来记录栈追踪:

public class TrackedRuntime extends Runtime {
    private final Runtime originalRuntime;

    public TrackedRuntime(Runtime original) {
        this.originalRuntime = original;
    }

    @Override
    public void exit(int status) {
        // 输出调用栈
        System.err.println("Runtime.exit(" + status + ") 被调用,调用栈:");
        new RuntimeException().printStackTrace();
        originalRuntime.exit(status);
    }

    // 代理其他Runtime核心方法,避免功能缺失
    @Override
    public void addShutdownHook(Thread hook) {
        originalRuntime.addShutdownHook(hook);
    }

    @Override
    public int availableProcessors() {
        return originalRuntime.availableProcessors();
    }
    // 按需代理其他Runtime方法,如exec()、freeMemory()等
}

// 应用启动早期执行替换逻辑
public static void setupRuntimeTracking() throws Exception {
    Field runtimeField = Runtime.class.getDeclaredField("currentRuntime");
    runtimeField.setAccessible(true);
    runtimeField.set(null, new TrackedRuntime(Runtime.getRuntime()));
}

这种方式需要在应用启动初期执行替换,且要确保代理所有Runtime核心方法,避免破坏原有功能,适合小型应用或测试场景。

方案3:Shutdown Hook补充(局限性大)

JVM的Shutdown Hook会在System.exit()触发时执行,但Hook的调用栈是JVM内部逻辑,无法直接获取调用exit()的业务代码栈。可结合ThreadLocal提前记录栈信息,但需要业务代码主动配合:

private static final ThreadLocal<StackTraceElement[]> LAST_RECORDED_STACK = new ThreadLocal<>();

// 业务代码中主动调用该方法记录当前栈
public static void recordCurrentStack() {
    LAST_RECORDED_STACK.set(Thread.currentThread().getStackTrace());
}

// 注册Shutdown Hook
static {
    Runtime.getRuntime().addShutdownHook(new Thread(() -> {
        StackTraceElement[] stack = LAST_RECORDED_STACK.get();
        if (stack != null) {
            System.err.println("疑似触发exit的栈追踪:");
            for (StackTraceElement elem : stack) {
                System.err.println("\tat " + elem);
            }
        }
    }));
}

该方案无法自动捕获所有exit()调用,仅适合有明确埋点场景的补充方案。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 21:42:52