Computer >> 컴퓨터 >  >> 프로그래밍 >> Java

Java 9 Process API로 프로세스 종료하기 - ProcessHandle의 destroy() 메서드 활용법

Java 9부터 새롭게 개선된 Process API를 통해 실행 중인 프로세스에 대한 다양한 정보를 손쉽게 조회하고 제어할 수 있게 되었습니다. 이번 글에서는 프로세스를 식별하고 종료하는 핵심 인터페이스인 ProcessHandle과 함께, 실제 코드 예제를 통해 프로세스를 종료(destroy)하는 방법을 자세히 알아보겠습니다.

ProcessHandle과 ProcessHandle.Info 인터페이스란?

ProcessHandle 인터페이스는 운영체제 수준의 네이티브 프로세스를 식별하고 제어할 수 있는 기능을 제공합니다. 이 인터페이스를 사용하면 다음과 같은 작업이 가능합니다.

  • 프로세스 ID(PID) 확인 및 네이티브 프로세스 제어
  • isAlive() 메서드를 통한 프로세스 생존 여부 확인
  • destroy() 메서드를 통한 프로세스 정상 종료

또한 ProcessHandle.Info 인터페이스는 특정 시점의 프로세스 정보 스냅샷을 제공합니다. 명령어(command), 인자(arguments), 시작 시간(startTime), 총 CPU 사용 시간(totalCpuDuration), 실행 사용자(user) 등의 정보를 Optional 형태로 안전하게 조회할 수 있습니다.

프로세스 종료 예제 코드

아래 예제에서는 ProcessBuilder로 자식 프로세스를 생성한 뒤, toHandle() 메서드로 ProcessHandle 객체를 얻고, destroy() 메서드를 호출하여 해당 프로세스를 종료하는 과정을 보여줍니다.

import java.io.File;
import java.io.IOException;
import java.util.Objects;

public class DestroyProcessTest {
    public static void main(String[] args) throws InterruptedException {
        System.out.println("---------------------------");
        System.out.println("Destroy Process:");

        final String javaCmd = getJavaCmdFromParent();
        final String classpath = getClassPathFromParent();

        try {
            final Process process = new ProcessBuilder(javaCmd, "-cp", classpath,
                DestroyProcessTest.class.getName()).start();
            ProcessHandle processHandle = process.toHandle();
            printInfo(processHandle);
            destroyProcess(processHandle);

            Thread.sleep(1000);
            System.out.println("---------------------------");
            System.out.println("After destroying the process:");
            printInfo(processHandle);

        } catch(IOException e) {
            e.printStackTrace();
        }
    }
    private static String getClassPathFromParent() {
        return System.getProperty("java.class.path", "./*");
    }
    private static String getJavaCmdFromParent() {
        return Objects.isNull(System.getProperty("java.home")) ? "java"
: String.format("%s%sbin%sjava", System.getProperty("java.home"), File.separator, File.separator);
    }
    private static void destroyProcess(ProcessHandle processHandle) throws IllegalStateException {
        System.out.println("Ready to destroy Process with id: " + processHandle.pid());
        processHandle.destroy();
    }
    private static void printInfo(ProcessHandle processHandle) {
        System.out.println("---------");
        System.out.println("Id: " + processHandle.pid());
        System.out.println("isAlive(): " + processHandle.isAlive());
        System.out.println("isSupportsNormalTermination(): " + processHandle.supportsNormalTermination());

        ProcessHandle.Info processInfo = processHandle.info();
        System.out.println("Info: " + processInfo.toString());
        System.out.println("Info arguments().isPresent(): " + processInfo.arguments().isPresent());
        System.out.println("Info command().isPresent(): " + processInfo.command().isPresent());
        System.out.println("Info totalCpuDuration().isPresent(): " + processInfo.totalCpuDuration().isPresent());
        System.out.println("Info user().isPresent(): " + processInfo.user().isPresent());
    }
}

실행 결과

위 코드를 실행하면 프로세스 종료 전후의 상태 변화를 확인할 수 있습니다. 종료 전에는 isAlive()true였지만, destroy() 호출 후에는 false로 바뀌는 것을 볼 수 있습니다.

---------------------------
Destroy Process:
---------
Id: 4384
isAlive(): true
isSupportsNormalTermination(): false
Info: [user: Optional[Tutorialspoint\User], cmd: C:\Program Files\Java\jdk-9.0.4\bin\java.exe, startTime: Optional[2020-03-06T10:58:53.210Z], totalTime: Optional[PT0.046875S]]
Info arguments().isPresent(): false
Info command().isPresent(): true
Info totalCpuDuration().isPresent(): true
Info user().isPresent(): true
Ready to destroy Process with id: 4384
---------------------------
After destroying the process:
---------
Id: 4384
isAlive(): false
isSupportsNormalTermination(): false
Info: [user: Optional[Tutorialspoint\User], startTime: Optional[2020-03-06T10:58:53.210Z], totalTime: Optional[PT0.109375S]]
Info arguments().isPresent(): false
Info command().isPresent(): false
Info totalCpuDuration().isPresent(): true
Info user().isPresent(): true

핵심 포인트 정리

  • toHandle() 메서드: Process 객체에서 ProcessHandle 객체로 변환할 때 사용합니다.
  • destroy() vs destroyForcibly(): destroy()는 정상 종료를 요청하며, 강제 종료가 필요한 경우 destroyForcibly()를 사용합니다.
  • supportsNormalTermination(): 해당 프로세스가 정상 종료를 지원하는지 여부를 반환합니다. Windows 환경에서는 일반적으로 false가 출력됩니다.
  • 프로세스 생존 확인: 종료 후 isAlive() 값이 false로 변경되어 프로세스가 실제로 종료되었음을 확인할 수 있습니다.

이처럼 Java 9의 Process API는 외부 프로세스 관리 작업을 훨씬 직관적이고 안전하게 수행할 수 있도록 도와줍니다. 배치 작업 모니터링, 외부 프로그램 제어 등 다양한 시나리오에서 유용하게 활용해 보시기 바랍니다.