본문 바로가기

Java 길찾기/이것이 자바다

[Java] 기본 API 클래스 - System

자바 프로그램은 운영체제상에서 바로 실행되는 것이 아니라 JVM  위에서 실행된다. 따라서 운영체제의 모든 기능을 자바 코드로 직접 접근하기란 어렵다. 하지만 java.lang 패키지에 속하는 System 클래스를 이용하면 운영체제의 일부 기능을 이용할 수 있다. 즉, 프로그램 종료, 키보드로부터 입력, 모니터로 출력, 메모리 정리, 현재 시간 읽기, 시스템 프로퍼티 읽기, 환경 변수 읽기 등이 가능하다. System 클래스의 모든 필드와 메소드는 정적(static) 필드와 정적(static) 메소드로 구성되어 있다.]

 

프로그램 종료(exit())

경우에 따라서는 강제적으로 JVM을 종료시킬 때도 있다. 이때 System 클래스의 exit() 메소드를 호출하면 된다. exit() 메소드는 현재 실행하고 있는 프로세스를 강제 종료시키는 역할을 한다. exit() 메소드는 int 매개값을 지정하도록 되어 있는데, 이 값을 종료 상태값이라고 한다. 일반적으로 정상 종료일 경우 0으로 지정하고 비정상 종료일 경우 0 이외의 다른 값을 준다.

System.exit(0);

 

어떤 값을 주더라도 종료가 되는데, 만약 특정 값이 입력되었을 경우에만 종료하고 싶다면 자바의 보안 관리자를 직접 설정해서 종료 상태값을 확인하면 된다. System.exit()가 실행되면 보안 관리자의 checkExit() 메소드가 자동 호출되는데, 이 메소드에서 종료 상태값을 조사해서 특정 값이 입력되지 않으면 SecurityException을 발생시켜 System.exit()를 호출한 곳에서 예외 처리를 할 수 있도록 도와준다. checkExit()가 정상적으로 실행되면 JVM은 종료가 된다. 다음은 종료 상태값으로 5가 입력되면 JVM을 종료하도록 보안 관리자를 설정한다.

System.setSecurityManager(new Security Manager() {
    @Override
    public void checkExit(int status) {
        if(status != 5) {
            throw new SecurityException();
        }
    }
});

 

다음 예제는 종료 상태값이 5일 경우에만 프로세스를 종료한다. for문은 10번 반복되는데, i가 5가 되면 System.exit(5)가 되어 출력을 멈추고 JVM을 종료한다. 종료 상태값이 5가 아니면 보안 관리자에서 SecurityException을 발생시키고 예외 처리를 한다. 따라서 for문은 정상적으로 계속 실행 상태를 유지한다.

// ExitExample.java
public class ExitExample {
    public static void main(String[] args) {
        // 보안 관리자 설정
        System.setSecurityManager(new SecurityManager() {
            @Override
            public void checkExit(int status) {
                if(status != 5) {
                    throw new SecurityException();
                }
            }
        });
        
        for(int i=0; i<10; i++) {
            // i값 출력
            System.out.println(i);
            try {
                // JVM 종료 요청
                System.exit(i);
            }
            catch(SecurityException e) { }
        }
    }
}

 

쓰레기 수집기 실행(gc())

자바는 개발자가 메모리를 직접 코드로 관리하지 않고 JVM이 알아서 자동으로 관리한다. JVM은 메모리가 부족할 때와 CPU가 한가할 때에 쓰레기 수집기(Garbage Collector)를 실행시켜 사용하지 않는 객체를 자동 제거한다. 다음과 같이 new 연산자로 Car 객체를 생ㅅ어하고 변수 myCar에 객체 번지를 대입했다고 가정하자.

Car myCar = new Car();

만약 변수 myCar에 null을 대입하면, myCar는 객체의 번지를 잃게 된다. 객체의 번지를 모르니, 더 이상 Car 객체는 사용할 수가 없고 이제부터 쓰레기가 된다.

myCar = null;

변수 myCar가 다른 Car 객체를 참조할 경우도 마찬가지다. 이전 객체의 번지를 잃기 때문에 이전 객체는 쓰레기가 된다.

Car myCar = new Car();  // 이전 참조 객체
myCar = new Car();      // 현재 참조 객체

 

쓰레기 수집기는 개발자가 직접 코드로 실행시킬 수 없고, JVM에게 가능한한 빨리 실행해 달라고 요청할 수는 있다. 이것이 System.gc() 메소드이다. System.gc() 메소드가 호출되면 쓰레기 수집기가 바로 실행되는 것은 아니고, JVM은 빠른 시간 내에 실행시키기 위해 노력한다.

System.gc();

 

쓰레기가 생길 때마다 쓰레기 수집기가 동작한다면 정작 수행되어야 할 프로그램의 속도가 떨어지기 때문에 성능 측면에서 좋지 않다. 그리고 메모리가 충분하다면 굳이 쓰레기 수집기를 실행할 필요가 없다. gc() 메소드는 메모리가 열악하지 않은 환경이라면 거의 사용할 일이 없다. 스마트폰이 나오기 이전에도 자바로 휴대폰 게임을 개발했는데, 그때에는 휴대폰의 메모리가 작았기 때문에 쓰레기 수집기의 실행 시기가 중요할 수도 있었다 .새로운 화면을 만들기 위해 이전 화면에서 생성한 객체를 빨리 메모리에서 제거해야 했는데, 이때 System.gc()가 호출되었다. 다음은 쓰레기 수집기가 객체를 삭제하는지 확인하기 위해 이전에서 학습한 객체 소멸자(finalize)를 이용하였다. 쓰레기 수집기는 객체를 삭제하기 전에 마지막으로 객체의 소멸자를 실행한다.

// GcExample.java
public class GcExample {
    public static void main(String[] args) {
        Employee emp;
        
        emp = new Employee(1);
        emp = null;
        emp = new Employee(2);
        emp = new Employee(3);
        
        System.out.print("emp가 최종적으로 참조하는 사원번호: ");
        System.out.println(emp.eno);
        System.gc();
    }
}

class Employee {
    public int eno;
    
    public Emplyee(int eno) {
        this.eno = eno;
        System.out.println("Employee(" + eno + ") 가 메모리에 생성됨.");
    }
    
    public void finalize() {
        System.out.println("Employee(" + eno + ")  이 메모리에서 제거됨.");
    }
}

 

현재 시각 읽기(currentTimeMillis(), nanoTime())

System 클래스의 currentTimeMillis() 메소드와 nanoTime() 메소드는 컴퓨터의 시계로부터 현재 시간을 읽어서 밀리세컨드(1/1000초) 단위와 나노세컨드(1/10^9초) 단위의 long 값을 리턴한다.

long time = System.currentTimeMillis();
long time = Sysetm.nanoTime();

 

리턴 값은 주로 프로그램의 실행 소요 시간 측정에 사용된다. 프로그램 시작 시 시각을 읽고, 프로그램이 끝날 때 시각을 읽어서 차이를 구하면 프로그램 실행 소요 시간이 나온다. 다음 예제는 for문을 사용해서 1부터 1000000(백만)까지의 합을 구하는데 걸린 시간을 출력한다.

// SystemTimeExample.java
public class SystemTimeExample {
    public static void main(String[] args) {
        long time1 = System.nanoTime(); // 시작 시간 읽기
        
        int sum = 0;
        for(int i=1; i<=1000000; i++) {
            sum += 1;
        }
        
        long time2 = System.nanoTime(); // 종료 시간 읽기
        
        System.out.println("1 ~ 1000000 까지으 ㅣ합: " + sum);
        System.out.println("계산에 " + (time2 - time1) + " 나노초가 소요되었습니다.");
    }
}

 

시스템 프로퍼티 읽기(getProperty())

시스템 프로퍼티(System Property)는 JVM이 시작할 때 자동 설정되는 시스템의 속성값을 말한다. 예를 들어 운영체제의 종류 및 자바 프로그램을 실행시킨 사용자 아이디, JVM의 버전, 운영체제에서 사용되는 파일 경로 구분자 등이 여기에 속한다. 시스템 프로퍼티는 키(key)와 값(value)으로 구성되어 있다. 대표적인 키와 값에 대한 설명은 다음 표에 나와 있다.

키(key) 설명 값(value)
java.version 자바의 버전 1.8.0_20
java.home 사용하는 JRE의 파일 경로 <jdk 설치경로>\jre
os.name Operating system name Windows 10
file.separator File separator("/" on UNIX) \
user.name 사용자의 이름 사용자 계정
user.home 사용자의 홈 디렉토리 C:\Users\사용자계정
user.dir 사용자가 현재 작업 중인 디렉토리 경로 다양

 

시스템 프로퍼티를 읽어오기 위해서는 System.getProperty() 메소드를 이용하면 된다. 이 메소드는 시스템 프로퍼티의 키 이름을 매개값으로 받고 ,해당 키에 대한 값을 문자열로 리턴한다.

String value = System.getProperty(String key);

 

다음 예제에서는 운영체제 이름, 사용자 이름, 사용자 홈 디렉토리를 알아내고 출력한다. 그리고 모든 시스템 프로퍼티를 키와 값으로 출력해 보았다. 14라인의 Properties와 15라인의 Set은 나중에 다룰 것이다. 여기서는 출력 결과를 보고 어떤 키가 있는지만 이해하면 된다.

// GetPropertyExample.java
public class GetPropertyExample {
    public static void main(String[] args) {
        String osName = System.getProperty("os.name");
        String userName = System.getProperty("user.name");
        String userHome = System.getProperty("user.home");
        
        System.out.println("운영체제 이름: " + osName);  // 개별 속성 읽기
        System.out.println("사용자 이름: " + userName);
        System.out.println("사용자 홈디렉토리: " + userHome);
        
        System.out.println("--------------------");
        System.out.println(" [ key ] value");
        System.out.println("--------------------");
        Properties props = System.getProperties(); // 모든 속성의 키와 값을 출력
        Set keys = props.keySet();
        for(Object objKey : keys) {
            String key = (String) objKey;
            String value = System.getProperty(key);
            System.out.println("[ " + key + " ] " + value);
        }
    }
}

System.getProperties() 메소드는 모든(키, 값) 쌍을 저장하고 있는 Properties 객체를 리턴한다. 이 객체의 ketSet() 메소드를 호출하면 키만으로 구성된 Set 객체를 얻을 수 있다. for문은 Set객체로부터 키를 하나씩 얻어내어 문자열로 변환한 다음, System.getProperty() 메소드로 값을 얻어 키와 값을 모두 출력한다.

 

환경 변수 읽기(getenv())

대부분의 운영체제는 실행되는 프로그램에게 유용한 정보를 제공할 목적으로 환경 변수(Environment Variable)를 제공한다. 환경 변수는 프로그램상의 변수가 아니라 운영체제에서 이름(Name)과 값(Value)으로 관리되는 문자열 정보다. 환경변수는 운영체제가 설치될 때 기본적인 내용이 설정되고, 사용자가 직접 설정하거나 응용 프로그램이 설치될 때 자동적으로 추가 설정되기도 한다. 사용자가 환경 변수를 설정하는 방법은 운영체제마다 조금씩 다르다. Windows 계열의 운영체제에서는 [환경 변수] 대화상자를 이용해서 설정한다.

 

자바 프로그램에서는 환경 변수의 값이 필요한 경우 System.getenv() 메소드를 사용한다. 매개값으로 변수 이름을 주면 값을 리턴한다.

String value = System.getenv(String name);

 

다음 예제는 JAVA_HOME 환경 변수의 값을 얻어와서 출력한다.

// System.EnvExample.java
public class System.EnvExample {
    public static void main(String[] args) {
        String javaHome = System.getenv("JAVA_HOME");
        System.out.println("JAVA_HOME: " + javaHOME);
    }
}