1장

1. JVM, JRE, JDK

png

JVM(Java Virtual Machine)은 가상의 컴퓨터로 자바를 구동하기 위한 가상의 하드웨어를 의미한다. JRE(Java Runtime Environment)는 JVM용 운영체제로 자바 애플리케이션을 실행하기 위한 런타임 환경을 제공한다. JRE는 자바 프로그램을 실행하기 위해 필요한 라이브러리, 클래스 파일, 실행 파일, 인터프리터(java.exe) 등을 포함한다. JDK(Java Development Kit)는 JVM용 소프트웨어 개발 도구로 자바 애플리케이션을 개발하기 위한 도구와 라이브러리를 제공한다. JDK는 자바 개발에 필요한 컴파일러(javac), 디버거(jdb), 문서 생성 도구(javadoc) 등을 포함한다. 배포되는 JVM, JRE, JDK는 편의상 JRE는 JVM을 포함하고, JDK는 JRE를 포함하는 형태로 배포된다.

2. 프로그램, 프로세스, 스레드와 메모리

png

일반적인 프로그램은 실행 시 코드 실행 영역과 데이터 저장 영역으로 나누어 메모리를 사용한다. 객체지향 프로그램은 데이터 영역을 스태틱 영역, 스택 영역, 힙 영역으로 분할해서 사용한다.

png

멀티 스레드(Multi Thread)는 스택 영역을 스레드 개수만큼 분할해서 사용한다. 반면에 멀티 프로세스(Multi Process)는 자료 저장에 다수의 데이터 저장 영역을 사용한다. 멀티 프로세스는 메모리 사용량이 크다. 반면에 멀티 스레드는 하나의 메모리 영역에서 스택 영역만 분할해서 사용하므로 상대적으로 메모리를 적게 사용한다. 서블릿은 요청 당 스레드를 생성하므로 요청당 프로세스(CGI)보다 효율적이다.

3. main 메서드와 메모리

public class Start1 {
    public static void main(String[] args) {
        System.out.println("Hello OOP!");
    }
}
gif
  1. 먼저 JRE는 프로그램 안에 main 메서드가 있는지 확인한다. Start 클래스에서 main 메서드를 확인한 JRE는 JVM을 부팅한다. 부팅된 JVM은 Hello.class 목적 파일을 실행한다. JVM이 가장 먼저 하는 일은 전처리 과정이다.

  2. JVM은 가장 먼저 java.lang 패키지를 static 영역에 가져다 놓는다. java.lang 패키지가 있어야만 System.out.println() 메서드를 호출할 수 있다.

  3. JVM은 개발자가 작성한 모든 클래스와 import 패키지를 static 영역에 가져다 놓는다.

  4. 메인 메서드를 실행하기 위해 main() 스택 프레임을 스택 영역에 할당한다. 클래스를 정의하는 코드 블록을 제외한 모든 코드 블록은 스택 프레임을 생성한다.

  5. 메서드의 인자 args를 저장할 변수 공간을 스택 프레임에 할당한다.

  6. main 메서드를 실행하고 System.out.println("Hello OOP!");를 실행한다. GPU에서 "Hello OOP!"를 화면에 출력한다.

  7. main 메서드가 종료되고 스택 프레임이 소멸한다.

  8. JRE는 JVM을 종료하고 JRE도 운영체제 상의 메모리에서 사라진다.

4. 변수와 메모리

public class Start2 {
    public static void main(Stringp[] args) {
        int i;
        i = 10;
        double d = 20.0;
    }
}
gif
  1. Start1 클래스와 마찬가지로 전처리 후 main 메서드를 실행한다.

  2. main 메서드의 스택 프레임 내부 변수 공간에 int i를 위한 공간을 만든다. i는 선언하고 초기화하지 않았으므로 알 수 없는 값이 들어가 있다. 이전에 해당 공간의 메모리를 사용했던 다른 프로그램이 있다면, 청소하지 않은 값을 그대로 가진다.

  3. i를 초기화했으므로 10이 저장된다.

  4. double d 변수가 선언 및 초기화된다. 먼저 변수 공간에 d의 공간을 만든 후 20.0값을 집어넣는다.

5. 블록 구문과 메모리

public class Start3 {
    public static void main(String[] args) {
        int i = 10;
        int k = 20;

        if (i == 10) {
            int m = k + 5;
            k = m;
        } else {
            int p = k + 10;
            k = p;
        }

        // k = m + p; // 지역 변수에 접근하므로 에러
    }
}
gif
  1. 5번째 줄까지 실행한다. main 메서드의 스택 프레임 내부 변수 공간에 i와 k의 공간을 할당하고 값을 집어 넣는다.

  2. if 블록에 진입하므로 if 조건문 스택 프레임을 main 메서드의 스택 프레임에 중첩해서 생성한다.

  3. int m = k + 5;는 int m;과 m = k + 5; 두 개로 분할한다. if 조건문 스택 프레임 내부의 변수 공간에 m의 공간을 만든다.

  4. m = k + 5;를 실행하고 값을 할당한다. if 스택 프레임, 즉 내부 스택 프레임에서는 외부 스택 프레임의 값에 접근할 수 있다. 따라서 k는 외부 스택 프레임에 있는 변수지만 연산 가능하다.

  5. if 문을 종료한다. if 조건문 스택 프레임이 스택 영역에서 사라진다.

6. 메서드 호출과 메모리

public class Start4 {
    public static void main(String[] args) {
        int k = 5;
        int m;

        m = square(k);
    }

    private static int square(int k) {
        int result;
        k = 25;
        result = k;

        return result;
    }
}
gif
  1. 5번째 줄까지 실행한다. main 메서드의 스택 프레임 내부 변수 공간에 k와 m의 공간을 만들고 k에 값을 할당한다.

  2. 10번째 줄까지 실행한다. square 메서드도 if 조건문과 마찬가지로 스택 프레임을 생성한다. square 메서드의 스택 프레임의 변수 공간에 result, k, 반환값의 공간을 만든다. sqaure 메서드의 변수 k는 매개변수로 넘어온 값 5를 할당한다.

  3. sqaure 메서드의 변수 k에 새로운 값 25를 할당한다.

  4. result에 sqaure 메서드의 변수 k값을 집어넣고 result를 반환한다. 반환값은 25가 된다.

  5. square 메서드를 종료한다. square 메서드 스택 프레임이 스택 영역에서 사라진다. main 메서드의 변수 k에 반환값 25를 할당한다.

중첩되지 않은 스택 프레임은 서로 접근이 불가능함을 참고하자.

7. 전역 변수와 메모리

public class Start5 {
    static int share;

    public static void main(String[] args) {
        share = 55;

        int k = fun(5, 7);
    }

    private static int fun(int m, int p) {
        share = m + p;

        return m - p;
    }
}
gif
  1. main 메서드를 실행한다. share은 static 변수이므로 0으로 초기화된다.

  2. share에 새로운 값 55를 할당한다.

  3. 7번째 줄까지 실행한다. main 메서드의 변수 공간에 k의 공간을 만들고 fun 메서드를 호출한다. fun 메서드는 새로운 스택 프레임을 생성한다. 그리고 fun 메서드의 스택 프레임의 변수 공간에 m, p, 반환값의 공간을 만든다. m과 p에는 매개변수로 넘어온 값 5와 7을 할당한다.

  4. share에 m과 p를 더한 새로운 값 12를 할당한다.

  5. m에서 P를 뺀 -2를 반환한다.

  6. fun 메서드가 종료되었으므로 스택 영역에서 fun 메서드의 스택 프레임을 제거한다. k에 -2를 할당한다.

전역 변수는 여러 메서드에서 접근 가능하므로 가급적이면 사용을 자제하는게 좋다. 다만 전역 상수의 경우 필요에 따라 사용하는 게 바람직하다.

참고 자료

  • 스프링 입문을 위한 자바 객체 지향의 원리와 이해 - 위키북스

Last updated