Java 메모리 작동 방식: 책 관리 예제로 이해하는 정적, 스택, 힙 메모리
프로그램과 메모리 💾
프로그램은 CPU가 메모리에 있는 데이터를 가져와 계산하고, 처리한 결과를 다시 메모리에 저장하는 과정을 반복하며 실행됩니다. 이러한 기본 동작은 모든 프로그램이 공통적으로 수행하는 핵심 작업입니다.
하지만 하나의 컴퓨터에서 동시에 실행되는 프로그램은 매우 많고, 메모리는 한정되어 있기 때문에 운영체제(OS)는 각 프로그램이 사용하는 메모리를 제한하고 관리합니다. 자바에서도 메모리는 정적 메모리, 스택 메모리, 힙 메모리로 나뉘며, 각각의 역할과 특징이 있습니다.
1. 정적 메모리 📂
정적 메모리는 프로그램 실행 중에 변하지 않는 코드나 데이터를 저장하는 공간입니다. 자바에서는 다음과 같은 데이터가 정적 메모리에 저장됩니다:
- 정적 변수(static): 클래스에 소속된 변수로, 프로그램이 실행되는 동안 하나만 생성되어 모든 객체가 공유.
- 리터럴 값: String 리터럴이나 상수 값.
- 클래스 메서드와 코드: 클래스와 메서드 자체도 정적 메모리에 올라갑니다.
예시:
public class Example {
static int staticVariable = 42; // 정적 변수
public static void main(String[] args) {
System.out.println("Static variable: " + staticVariable);
}
}
이 예제에서 staticVariable은 프로그램 시작 시 정적 메모리에 올라가고, 프로그램 종료 시까지 유지됩니다.
2. 스택 메모리 📦
스택 메모리는 메서드 실행 시 필요한 데이터를 저장하는 공간입니다. 지역 변수와 메서드 매개변수가 여기에 저장되며, 메서드 호출 시 메모리가 할당되고, 메서드 실행이 끝나면 자동으로 해제됩니다.
스택 메모리의 특징:
- 컴파일 시 크기 결정: 변수나 매개변수의 크기를 컴파일 타임에 알 수 있어야 함.
- 자동 할당 및 해제: 메서드 호출 시 생성, 종료 시 해제.
- 빠른 속도: 스택은 고정된 크기와 간단한 구조로 매우 빠르게 동작.
예시:
public class Main {
public static void main(String[] args) {
int x = 10; // 지역 변수 (스택 메모리)
int y = 20; // 지역 변수 (스택 메모리)
System.out.println(x + y);
}
}
이 코드에서 x와 y는 메서드가 실행되는 동안 스택 메모리에 저장되고, 메서드 종료와 함께 자동으로 해제됩니다.
3. 힙 메모리 🗄️
힙 메모리는 프로그램 실행 중에 동적으로 생성된 객체가 저장되는 공간입니다. 자바에서는 new 키워드로 객체를 생성할 때 힙 메모리가 사용됩니다. 힙 메모리에 할당된 메모리는 가비지 컬렉터(GC, Garbage Collector)에 의해 자동으로 관리됩니다.
힙 메모리의 특징:
- 동적 할당: 객체는 런타임에 필요할 때 생성.
- 자동 관리: GC가 사용하지 않는 객체를 찾아 자동으로 메모리를 해제.
- 공유 가능: 여러 참조 변수가 동일한 객체를 가리킬 수 있음.
예시:
public class Main {
public static void main(String[] args) {
String str = new String("Hello, Java!"); // 힙 메모리에 저장
System.out.println(str);
}
}
위 코드에서 str 변수는 스택 메모리에 저장되지만, 실제 문자열 객체는 힙 메모리에 저장됩니다. 프로그램 실행 중 GC가 사용하지 않는 힙 메모리 객체를 정리합니다.
자바로 메모리 관리 예제 🖥️
책 정보를 저장하는 프로그램을 작성하며 스택과 힙 메모리를 이해해봅시다.
Book 클래스와 Author 클래스 작성:
public class Book {
String title;
String description;
int price;
Author author;
void print() {
System.out.println("책 제목: " + title);
System.out.println("설명: " + description);
System.out.println("가격: " + price + " 원");
author.print();
}
}
class Author {
String name;
String email;
String phone;
void print() {
System.out.println("작가명: " + name);
}
}
샘플 데이터 저장:
public class Main {
public static void main(String[] args) {
// 첫 번째 책 정보
Book b1 = new Book();
b1.title = "Good Night";
b1.description = "무서운책";
b1.price = 30000;
b1.author = new Author();
b1.author.name = "Mike";
b1.author.email = "abc@gmail.com";
b1.author.phone = "010-1111-2222";
// 두 번째 책 정보
Book b2 = new Book();
b2.title = "무서운책";
b2.description = "공포가 최고";
b2.price = 25000;
b2.author = new Author();
b2.author.name = "Mike";
b2.author.email = "abc@gmail.com";
b2.author.phone = "010-1111-2222";
// 세 번째 책 정보
Book b3 = new Book();
b3.title = "재밌는책";
b3.description = "유머가 최고";
b3.price = 20000;
b3.author = new Author();
b3.author.name = "홍길동";
b3.author.email = "hong@mail.com";
b3.author.phone = "010-3333-4444";
// 책 정보 출력
b1.print();
b2.print();
b3.print();
}
}
실행 결과:
책 제목: Good Night
설명: 무서운책
가격: 30000 원
작가명: Mike
책 제목: 무서운책
설명: 공포가 최고
가격: 25000 원
작가명: Mike
책 제목: 재밌는책
설명: 유머가 최고
가격: 20000 원
작가명: 홍길동
메모리 동작 이해:
- 스택 메모리:
- b1 변수는 스택 메모리에 저장되며, 힙에 있는 Book 객체를 참조합니다.
- b1.author는 힙에 있는 Author 객체를 참조합니다.
- 함수가 끝나면 스택 메모리는 해제됩니다.
- 힙 메모리:
- Book 객체와 Author 객체가 힙 메모리에 저장됩니다.
- 프로그램 종료 전까지 GC가 필요하지 않으면 메모리가 유지됩니다.
가비지 컬렉터(GC) 🚀
GC는 자바의 메모리 관리 시스템으로, 더 이상 사용되지 않는 객체를 자동으로 정리합니다. GC 덕분에 프로그래머는 메모리 해제에 신경 쓰지 않아도 되지만, 다음과 같은 성능 문제가 발생할 수 있습니다:
- 일시적 멈춤(Pause): 메모리가 부족하면 GC가 동작하며 프로그램이 잠시 멈출 수 있음.
- 비용: GC 작업은 CPU를 사용하므로, 프로그램 성능에 영향을 줄 수 있음.
자바에서 GC 동작 예시:
public class Main {
public static void main(String[] args) {
for (int i = 0; i < 100000; i++) {
new Book(); // 객체를 계속 생성
}
System.out.println("객체 생성 완료");
}
}
이 코드에서 생성된 Book 객체는 사용할 수 없게 되면 GC에 의해 자동으로 정리됩니다.
자바에서의 메모리 관리 요약
- 정적 메모리: 클래스와 정적 변수, 리터럴 값 등이 저장.
- 스택 메모리: 메서드 실행 시 지역 변수와 매개변수가 저장.
- 힙 메모리: 객체와 동적으로 생성된 데이터가 저장.
- GC(Garbage Collector): 힙 메모리에서 불필요한 객체를 자동으로 정리.
자바는 GC 덕분에 메모리 관리가 쉽지만, 메모리 사용을 최적화하고 GC의 성능 영향을 줄이기 위해 객체 생성을 최소화하고, 필요한 경우 참조를 명확히 해제하는 것이 중요합니다.📚💡