Java

[Java] Java 프로그램이 메모리를 사용하는 방식

림 림 2020. 10. 1. 13:57
반응형

1. 코드 실행 영역과 데이터 저장 영역

Java 프로그램이 메모리를 사용하는 방식을 알아보기 전에, 기계어를 포함한 모든 프로그래밍 언어가 공통적으로 메모리를 사용하는 방식이 있습니다. 바로 메모리를 코드 실행 영역데이터 저장 영역으로 나누어 사용하는 것입니다. 추상적인 구조는 다음과 같습니다.

코드 실행 영역에는 CPU가 실행할 코드들이 저장되어 있습니다. 데이터 저장 영역은 프로그램의 실행부터 종료까지 필요한 변수들이 저장되는 곳입니다.

 

 

2. Java 프로그램이 사용하는 메모리 영역

자바 프로그램은 데이터 저장 영역을 다시 세 영역으로 나누어 관리합니다. Static 영역, Stack 영역, Heap 영역으로 나누어집니다.

각각의 영역에는 클래스, 메소드, 인스턴스에 대한 정보가 나뉘어 저장됩니다. Static 영역에는 프로그램에서 사용할 클래스들에 대한 정보가 저장됩니다. Stack 영역에는 실행되는 메소드에 대한 정보가 저장됩니다. Heap 영역에는 생성된 인스턴스들에 대한 정보가 저장됩니다.

 

 

3. Java 프로그램 시작을 위한 전처리 과정 - Static 영역

자바 프로그램을 실행하기 위해서서는 전처리 과정이 필요합니다. 전처리 과정은 크게 JRE의 역할JVM의 역할로 나뉘어집니다. JRE는 JVM을 작동시키고 JVM은 클래스들에 대한 정보를 Static 영역에 배치합니다.

1. JRE의 사전 준비

  • 프로그램 안에 main 메소드가 있는지 확인합니다.
  • main 메소드가 있다면, 가상 기계인 JVM을 작동시키고 자바 목적 파일을 넘겨줍니다

2. JVM의 전처리

  • java.lang 패키지를 Static 영역에 배치합니다. (모든 자바 프로그램은 java.lang 패키지를 포함합니다.)
  • import된 패키지를 Static 영역에 배치합니다.
  • 프로그램 안에서 선언된 모든 클래스를 Static 영역에 배치합니다.(이 때, 인스턴스의 변수가 아닌 클래스의 static 변수들의 값을 위한 공간도 배치됩니다. 따라서 인스턴스들이 static 변수를 공유할 수 있게 됩니다.)

 

 

4. 메소드의 실행 - Stack 영역

프로그램 시작을 위한 전처리 과정을 마치면 프로그램을 시작하게 됩니다. 프로그램이 시작되면 가장 먼저 main 메소드가 실행됩니다. 그리고 main 메소드가 종료되면 프로그램도 종료됩니다. main 메소드는 프로그램의 시작이기도 하고 끝이기도 합니다.

main 메소드를 실행하기 위해서는 다음과 같은 작업이 필요합니다.

  • Stack Frame 배치: JVM은 메소드의 실행을 위해 Stack 영역에 해당 메소드에 대한 정보 를 담는 Stack Frame을 배치합니다.
  • 변수 공간 배치: Stack Frame 안에 메소드에서 선언된 변수의 값을 저장할 공간을 할당받아 배치합니다. Stack Frame 안에 저장된 지역변수들은 다른 Stack Frame에 선언된 지역변수에 접근할 수 없습니다.

💡 위에서 Stack 영역에는 실행되는 메소드의 정보가 담긴다고 했습니다. 자세히 말하면 메소드에서 사용하는 변수들의 값이 저장됩니다. 여기서 말하는 변수들에는 메소드에 전달되는 인자, 메소드 안에서 선언된 기본형(Primitive Type) 변수참조형(Reference Type) 변수가 포함됩니다. 기본형 변수의 메모리 공간에는 변수의 실제 값이 저장되고, 참조형 변수의 메모리 공간에는 Heap 영역에 생성된 인스턴스를 참조하는 주소값이 저장됩니다.

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

위와 같이 Start 클래스의 실행을 위한 전처리 과정을 모두 마치고 프로그램의 시작을 위해 main 메소드를 실행하려고 한다고 가정합시다. 그러면 JVM은 main 메소드를 위한 Stack Frame을 Stack 영역에 배치합니다. 그리고 main 메소드에서는 인자로 args를 받고 있습니다. 따라서 main 메소드의 Stack Frame 안에 변수 args를 위한 공간을 배치합니다.

그리고 main 메소드를 실행하게 되면 "Hello OOP!" 라는 문구가 출력되고 main 메소드가 종료됩니다. main 메소드가 종료되면 Stack 영역의 main 메소드의 Stack Frame이 메모리 공간을 반환하고 삭제됩니다. 

그럼 만약 main 메소드 안에서 다른 메소드를 만나면 어떻게 될까요? 마찬가지로 Stack 영역에 해당 메소드의 정보를 저장할 Stack Frame을 배치하고 변수를 위한 공간도 배치합니다. 위의 사진은 추상적인 구조이고, 실제로는 Stack 자료구조와 같이 main 스택 프레임의 윗 주소에 저장됩니다. 새로운 메소드가 실행될 때마다 Stack 영역에 Stack Frame이 쌓이고 실행이 끝나면 사라지면서 LIFO 형태로 저장됩니다.

멀티스레드(Multi Thread) 환경에서는 Stack 영역의 개수를 스레드의 개수만큼 분할해서 사용합니다. 각 스레드는 해당 스레드를 위한 Stack 영역에만 접근할 수 있습니다.

 

 

5. 클래스의 인스턴스 생성 - Heap 영역

Heap 영역에는 Stack 영역에 저장된 객체 참조 변수가 가리키는 인스턴스가 저장됩니다. 간단하게 다음과 같은 Cat 클래스가 있고, main 메소드에서 Cat 인스턴스를 생성했다고 합시다.

public class Cat {
	public String name;
	public int age;
}
public class MyClass {
	public static void main(String[] args) {
		Cat cat = new Cat();
	}
}

Stack 영역과 Heap 영역에는 다음과 같이 저장됩니다. main 메소드의 Stack Frame에는 객체 참조 변수 cat의 공간이 생기고, Heap 영역에 저장된 인스턴스의 주소값이 저장됩니다.

만약 cat 변수에 null값을 저장하게 된다면, 더 이상 cat 인스턴스를 참조하는 변수가 없어지고, 가비지 컬렉터(Garbage Collector)가 Heap 영역의 메모리를 회수하면서 메모리 낭비를 막습니다.

 

반응형

'Java' 카테고리의 다른 글

[Java] Object 클래스  (0) 2021.08.08
[Java] Oracle Java API 버그 제보하기  (0) 2021.08.08
[Java] JPMS와 Module  (0) 2021.08.08
[Java] JVM 구조  (0) 2020.10.25
[Java] Java로 HTTP GET, POST 통신하기  (1) 2020.10.22