자바 어플리케이션의 성능을 확인하고 개선하기 위해선, 자바 메모리 구조에 대한 이해가 필요합니다.
자바의 메모리 관리를 잘 하기만 하더라도 어플리케이션이 더욱 효율적으로 실행 될 수 있죠!
자바의 메모리 구조
자바의 메모리 구조는 크게 3가지로 나눌 수 있습니다.
- 메서드 영역: 이 영역은 모든 스레드가 공유하는 메모리 영역으로, 클래스 수준의 정보(클래스 로더에 의해 로드된 클래스와 인터페이스의 메타데이터), 정적 변수를 저장하는 static 영역, 상수를 저장하는 Constant Pool 로 이루어져 있습니다. 이 영역은 어플리케이션이 실행되는 동안 잘 변하지 않는 정보를 저장하는데 사용합니다.
- 스택 영역: 자바 어플리케이션 실행 시, 어플리케이션 내의 스레드마다 각 스레드의 스택이 존재하게 되는데, 해당 스레드 안에서 메서드가 호출될 때 마다, 스텍 프레임이 생성되어 로컬 변수와 메서드 호출에 필요한 정보를 저장합니다. 이후 메서드가 종료되면 해당 스택 프레임은 스택에서 제거됩니다.
- 힙 영역: 자바 어플리케이션의 메모리 영역 중 가장 큰 영역을 차지합니다. 이 영역은 객체의 인스턴스 및 배열을 저장하는데 사용됩니다. 이 안에서 가비지 컬렉터가 GC를 통해 해당 객체들의 생명주기를 관리합니다.
그 외에도, 스레드가 실행 중인 JVM의 주소를 저장하는 프로그램 카운터 영역 / 자바가 아닌 다른 언어로 작성된 코드를 실행하기 위한 네이티브 메서드 스택 영역 등이 있습니다.
메서드 영역과 힙 영역의 관계
여기서 주로 혼동되는 개념이 바로 메서드 영역과 힙 영역의 관계에 대한 개념입니다.
예시를 들어보겠습니다.
class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public void sayHello() {
System.out.println("안녕 내 이름은 " + name + "이고, 나이는 " + age + "살이야");
}
}
public class Main {
public static void main(String[] args) {
Person person1 = new Person("chulsoo", 30);
Person person2 = new Person("yeonghee", 25);
person1.sayHello();
person2.sayHello();
}
}
위 예시에서, Person 클래스는 해당 객체의 속성을 나타내는 변수 name과 age를 가지고 있고, sayHello라는 메서드를 포함하고 있ㅆ브니다.
그리고 main 메서드에서 Person의 인스턴스 두개를 생성하고 각각에 대해 sayHello를 호출합니다.
그 경우, 힙 영역에 Person의 인스턴스인 person1과 person2가 생성되는데, 이때 해당 인스턴스에 메서드까지 가지고 있지 않고,
전체적인 Person 클래스의 내용을 메타데이터의 형태로 메서드 영역에 저장후, 이 정보를 참조해서 메서드를 실행하게 됩니다.
즉, Person 클래스가 처음 JVM에 로드될 때, sayHello의 메서드 코드는 메서드 영역에 한번만 배치되며 모든 Person의 인스턴스가 해당 메서드를 공유하여 사용할 수 있습니다.
스택 영역의 동작
public class StackExample {
public static void main(String[] args) {
System.out.println("Start main");
firstMethod();
System.out.println("End main");
}
public static void firstMethod() {
System.out.println("Inside firstMethod");
secondMethod();
System.out.println("End of firstMethod");
}
public static void secondMethod() {
System.out.println("Inside secondMethod");
}
}
위 코드를 실행시키는 자바 어플리케이션이 있다고 가정해봅시다.
이 코드는 main 메서드에서 시작해서 firstMethod를 호출하고, 그 다음 firstMethod 내에서 secondMethod를 호출합니다.
이 코드가 실행될 때 스택 영역의 동작을 살펴봅시다.
- 프로그램이 실행되고 main 메서드를 위한 첫번째 스택 프레임이 생성됩니다. 이 프레임은 main메서드의 지역 변수와 호출 정보를 담고 있습니다.
- 이후 main 메서드 내에서 firstMethod가 호출되어 새로운 스택 프레임이 main 프레임 위에 추가됩니다. 이 프레임은 firstMethod의 지역변수와 호출 정보를 담고 있습니다.
- firstMethod내에서 secondMethod도 호출되면서 해당 스택프레임이 firstMethod위에 생성됩니다. 마찬가지로 해당 메서드의 지역변수와 호출 정보를 담고 있습니다.
- 이후 secondMethod가 종료되면 스택 프레임에서 해당 스택이 제거됩니다.
- 그 다음 firstMethod도 종료되어 해당 스택프레임이 제거됩니다.
- 마지막으로 main 메서드 까지 실행이 종료되면 마지막 남은 스택 프레임이 제거되고 프로그램이 종료됩니다.
이러한 방식으로 메서드 호출 시 해당 실행 컨텍스트를 저장하고 있어서, 메서드 호출과 실행흐름을 관리하기 용이한 구조를 제공합니다.
스택 영역과 힙 영역의 동작
class Book {
String title;
int page;
public Book(String title, int page) {
this.title = title;
this.page = page;
}
public void printInfo() {
System.out.println(title + "은 " + page + "페이지로 구성되어있다");
}
}
public class Library {
public static void main(String[] args) {
Book book1 = new Book("Do it 자바 프로그래밍", 350);
Book book2 = new Book("헤드퍼스트 자바", 500);
book1.printInfo();
book2.printInfo();
}
}
위 코드를 실행시키는 자바 어플리케이션이 있다고 가정했을 때,
해당 코드가 실행되면서 힙 영역과 스택 영역이 어떻게 상호작용할까요?
- main 메서드가 호출되면서 이에 대한 스택 프레임이 스택 영역에 생성됩니다.
- Book 객체들이 힙 영역에 생성됩니다.
- book1.printInfo() / book2.printInfo() 메서드 호출 시 각 메서드에 대한 스택 프레임이 스택 영역에 생성됩니다.
이렇게 스택 영역과 힙 영역, 메서드 영역에 필요한 정보들이 저장되어 호출되는 것이죠!
'Language > Java ☕️' 카테고리의 다른 글
자바의 static (0) | 2024.05.22 |
---|---|
자바의 가비지 컬렉션 (0) | 2024.02.02 |
String vs StringBuffer vs StringBuilder (0) | 2024.02.02 |
Double과 Float를 사용하면 안되는 이유 (0) | 2024.02.01 |
JDK vs JRE vs JVM (0) | 2024.02.01 |
안녕하세요, 저는 주니어 개발자 박석희 입니다. 언제든 하단 연락처로 연락주세요 😆