Skip to content

Stop with integratedTerminal/externalTerminal still uses vm.exit(0), shutdown hooks do not run #1732

Open

Description

@danilaslynko

Follow-up to #1720 (fixed by microsoft/java-debug#645).

Since 0.60.2026102705, the first Stop sends terminate, keeps JDI attached, and the JVM shuts down gracefully, so breakpoints in shutdown hooks are hit. This works with "console": "internalConsole". With "console": "integratedTerminal" or "externalTerminal" the problem from #1720 is still there: shutdown hooks do not run at all.

Environment
  • Operating System: Linux Mint 22.1
  • JDK version: 21.0.7
  • Java Debugger extension version: 0.60.2026102705 or later
Steps To Reproduce
  1. Use the program from Stop detaches the debugger before shutdown hooks run, so JVM shutdown cannot be debugged #1720:
    public class App {
        public static void main(String[] args) throws Exception {
            Runtime.getRuntime().addShutdownHook(new Thread(() -> {
                System.out.println("hook start");   // breakpoint here
                System.out.println("hook end");
            }));
            Thread.sleep(Long.MAX_VALUE);
        }
    }
  2. Launch it with "console": "integratedTerminal" (or "externalTerminal").
  3. Set a breakpoint on the hook start line.
  4. Press Stop.
Current Result

The JVM is killed: the breakpoint is not hit and hook start is not printed in the terminal.

Expected Result

The same as with internalConsole: the JVM gets SIGTERM, the debugger stays attached, the shutdown-hook breakpoint is hit, and the session ends when the JVM exits.

Additional Informations

TerminateRequestHandler calls debugSession.terminate(), and DebugSession.terminate() does:

if (vm.process() != null && vm.process().isAlive()) {
    vm.process().destroy();
} else if (vm.process() == null || vm.process().isAlive()) {
    vm.exit(0);
}

In terminal mode the debuggee is started through runInTerminal and connects to a ListeningConnector, so vm.process() is null and the code falls back to vm.exit(0). JDWP VirtualMachine.Exit ends the JVM without running shutdown hooks.

DebuggeeProcessHelper.terminate() can already destroy the process by context.getProcessId(), but it is only used when there is no debug session (noDebug). Even there it does not point to the JVM: VS Code's runInTerminal response gives the PID of the terminal shell, not of the debuggee.

Activity

  1. issuelens commented on Nov 12, 2026

    @issuelens

    Thanks for the detailed follow-up and reproduction. The current Java debug-server path supports the analysis: TerminateRequestHandler delegates to DebugSession.terminate(), which uses vm.exit(0) when JDI has no associated Process. That leaves terminal-launched sessions on a different termination path from internalConsole, so this is a valid remaining bug rather than a duplicate of #1720; #1720 is the related fix that exposed this terminal-specific gap.

    As a workaround, focus the integrated/external terminal and send Ctrl+C instead of using Stop so the JVM can handle the signal and run its shutdown hooks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions