티스토리 뷰

반응형

JDK VS JRE VS JVM

 

배포되는 자바 프로그램의 구조

JDK 자바 개발 도구(Java Development Kit) JVM을 위한 소프트웨어 개발 도구
JRE 자바 실행 환경(Java Runtime Evironment) JVM을 위한 OS
JVM 자바 가상 기계(Java Virtual Machine) 가상의 컴퓨터

 

  자바 프로그램은 JDK를 이용해 개발되고, JRE에 의해 JVM에서 실행된다. JDK에 의해 개발된 자바 프로그램을 JRE가 포함하고, 이런 JRE는 JVM에 포함되는 형태로 배포된다.

 

  JVM은 자바 코드 컴파일러인 javac.exe를 포함하여 자바 소스 파일인 .java파일을 자바 목적 파일인 .class로 컴파일해준다. JRE에는 자바 프로그램 실행기인 java.exe를 포함하고 있다. 이런 구조로 자바는 플랫폼(하드웨어 + OS)에 필요한 설치 파일이 필요하지 않고, 플랫폼에 있는 JVM이 중재자로 프로그램을 실행해준다. 이런 구조 덕분에 자바는 플랫폼 독립적(Write Once Run AnyWhere)인 특성을 가질 수 있다.

 

 

객체 지향 프로그램의 메모리 사용 방식

프로그램의 메모리 사용 방식

  프로그램이 샐행될 때 프로그램은 메모리를 코드 실행영역, 데이터 저장 영역 이렇게 구분하여 사용한다.

 

객체 지향 프로그램의 메모리 사용 방식

  객체 지향 프로그램은 데이터 저장 영역을 static, 스택, 힙 이렇게 3가지 영역으로 구분해서 사용한다.

 

데이터 저장 영역의 사용 대상

  객체 지향 프로그램의 메모리는 static, 스택, 힙영역 이렇게 사용한다고 했는데 static은 클래스, 스택은 메서드, 힙은 객체를 위한 영역이다. 자바 프로그램이 실행될 때 각자 구분된 데이터 저장 영역에 맞게 사용을 한다.

 

 

main() 메서드 실행시 발생하는 일들

public class Main {
    public static void main(String[] args) {
    	System.out.println("Hello World!");
    }
}

    위의 간단한 코드를 실행할 때 어떤 일이 발생하는지 알아보자.

  main() 메서드는 프로그램이 실행되는 시작점이다. JRE는 프로그램에 main() 메서드가 있는지 확인한다. main 메서드가 확인되면 JRE는 프로그램 실행을 위한 준비를 한다. 이 때 목적 파일을 위한 JVM이 실행되고 JVM은 전처리를 한다.

 

java.lang 패키지가 static 영역에 배치

  JVM은 먼저 java.lang 패캐지를 static 영역에 배치한다. 그래서 모든 자바 프로그램은 java.lang 패키지를 포함하게 된다.

 

   JVM은 java.lang 패키지를 로드한 다음, 작성한 클래스와 import한 패키지도 가져온다. 

 

main() 메서드 스택 할당

  main()또한 메서드이기 때문에 main 메서드를 위한 스택 프레임이 스택 영역에 할당된다. main 메서드는 public static void main(String[] args) {...}라고 정의되는데 main 메서드의 인자 args를 위한 공간도 할당된다. 

 

  앞에서의 과정이 끝나면 main 메서드 안의 명령어들이 실행된다. 그리고 실행이 끝나고 닫힌 중괄호를 만나고 스택 프레임이 소멸된다.

 

 

변수와 메모리

int a = 10;
int b = 20;

  main 메서드에 위의 코드가 추가되었을 때, 변수를 위한 공간은 어디에 있는지 알아보자.

 

변수가 할당 되었을 때

  변수 a와 b는 main 메서드 안에 존재한다. 따라서 main 스택 프레임에는 차례대로 a, b (a 변수에 값이 먼저 할당 되기 때문에) 순서대로 값이 쌓인다.

 

int a = 10;
int b = 20;

if(a != b) {
    int c = a + b;
}

  위의 코드에서 if문 안의 변수 c는 어디에 할당이 되는지 알아보자.

 

  main의 스택 프레임 안에 if 스택프레임이 생성되고, c 변수를 위한 공간이 할당된다. 그리고 if 문의 닫힌 괄호를 만나면 if 스택프레임도 종료된다.

 

int a = 10;
int b = 20;

if(a != b) {
    int c = a + b;
}

  위의 코드에서 if문이 끝난 다음 a가 있는 영역에서는 c를 사용할 수 없다. 왜냐하면 c가 존재하는 if의 스택 프레임이 존재하지 않기 때문이다. 하지만 c가 존재하는 괄호 안에서는 a를 참조할 수 있다. 즉, 외부 스택 프레임에서는 내부 스택 프레임의 변수에 접근이 불가능하지만 역은 가능하다는 결론이 나온다.

 

public static void main(String[] args) {
    int a = 10;
    int b = 20;
    
    int sum = add(a, b);
}

public static int add(int a, int b) {
    int sum = a + b;
    return sum;
}

  add 메서드는 인자로 받은 두 정수값을 더한 다음 계산한 결과 값을 리턴하는 함수이다. main은 이 add 함수를 호출하고 있다.

 

 

add 스택 프레임의 공간이 부족한 관계로 a, b 인자는 합침

  add 메서드의 스택 프레임의 return은 반환해줄 값을 가지는 가상의 변수이다. 자바는 call-by-value로 동작을 하기 때문에 main() 스택 프레임의 a, b 변수와 add() 스택 프레임의 a, b 변수는 서로 다른 공간에 존재한다. 두 스택 프레임은 별도의 공간을 할당 받기 때문에 서로가 가지고 있는 변수의 값에 접근이 불가능하다. main() 메서드가 add() 메서드의 반환된 값을 받는 방식으로 값이 전달된다.

 

static int global;

public static void main(String[] args) {
    global = 10;
    add(1);
}

private static void addN(int n) {
    global += n;
}

global 변수의 위치

    global 변수에는 static 키워드가 있기 때문에 static 영역에 할당된다.

 

addN 호출시 메모리 공간

  global 변수는 전역변수이기 때문에 addN 메서드를 호출할 때 위와 같이 할당이 된다. 이렇게 전역 변수를 사용하면은 공유가 쉬워진다는 장점이 있다. 하지만, 전역변수에서 변경이 일어날 때 추적을 하기가 어렵다는 단점이 있다.

  이런 전역 변수는 되도록 사용하지 않는 것이 좋지만, 읽기 전용으로 값을 공유하는 경우에는 전역 상수를 사용하는 것이 좋다. Java에도 Math.PI라는 원주율에 대한 전역 상수가 정의되어 있다.

 

 

멀티 스레드와 멀티 프로세스

멀티 스레드
멀티 프로세스

  멀티 스레드의 경우 각자 스택 영역 만을 할당받는다. 멀티 프로세스의 경우 각자 static, 스택, 힙 영역을 할당받는다. 따라서 멀티 스레드는 멀티 프로세스보다 메모리를 적게 사용한다. 멀티 스레드에서 스택 영역만 각자 할당을 받기 때문에 힙, static 영역은 공유하기 때문에 참조가 멀티 프로세스보다 쉽다.

  자바의 servlet도 요청을 받을 때 프로세스가 아니라 스레드를 생성한다. static, 힙, 스택 영역을 생성하는 것이 아닌 새로운 스택 영역만 생성하기 때문에 더 효율적이기 때문이다.

  하지만, 멀티스레드에서 쓰기 가능한 전역 변수를 사용할 때 thread-safe하지 않게 된다. thread-safe를 위해 락을 할 수는 있지만 멀티 스레드의 장점을 활용하지 못하는 해결 방법이다.

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/11   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
글 보관함