Java 메모리 구조
Java 메모리는 3가지 영역으로 구성되어 있다
- 메서드 영역 Method Area
프로그램을 실행하는데 필요한 공통 데이터를 관리한다
그러므로 프로그램의 모든 영역에서 공유
안에 있는 3가지는 각각
클래스 정보 : 클래스 실행 코드, 필드, 메서드, 생성자등 모든 실행코드
static 영역 : static 변수 보관
런타임 상수 풀 : 공통 리터럴 상수
- 스택 영역 Stack Area
스레드 별 하나의 실행 스택이 생성된다 (스레드 수 만큼 스택이 생성된다)
네모칸 하나가 스택 프레임이고
지역 변수, 중간 연산 결과, 메서드 호출 정보를 포함한다
메서드를 호출할 때 마다 하나의 스택 프레임이 쌓이고, 메서드가 종료되면 해당 스택 프레임이 제거된다.
- 힙 영역 Heap Area
객체(인스턴스), 배열이 생성되는 영역
GC가 이루어지는 주요 영역
더이상 참조되지 않는 객체는 GC에 의해 제거됨
스레드 생성
스레드를 직접 만들때
Thread 클래스를 상속받는 법, Runnable 인터페이스를 구현하는 방법이 있다.
Thread 상속
상속하고 run 메서드를 override해주면 된다
주의할 점은 오버라이드한 run 메서드를 호출하는 것이 아니라
start 메서드를 실행해야한다
- 이유는 뒤에서
실행할때마다
우리가 만든 스레드 내부에서 실행되는 Thread-0 출력 순서가 달라질 수 있다
- 이 이유도 뒤에서
해석해보면
프로세스가 실행되기 위해 최소 하나의 스레드가 필요하니
main 스레드가 시작되고
main 스택 영역이 할당된다
그후 HelloThread 인스턴스를 생성한다
그후 start() 메서드를 호출할때
자바가 스레드를 위한 스택 영역을 할당한다
그러면서 Thread-0 스레드가 run() 메서드의 스택 프레임을 스택에 올리고 run()메서드를 시작한다
더 자세히 보면
주의할 점은
run() 메서드를 실행하는 것은 Thread-0 라는 점
main 스레드는
start()메서드로 Thread-0 스레드에게 시작하라고 명령을 하고
그 후 일을 진행하는 것이다.
--> 이러면 Thread-0에서 실행되는 출력 순서가 달라지는 이유가 이해된다.
main 스레드 입장에서는 start로 Thread-0를 부르고 이후, 기다리지 않고 start 메서드를 빠져 나와 출력을 찍은 것이고
동시에 Thread-0 내부에서는 출력을 수행하니 순서가 달라질 수 있는 것
즉 main 스레드와 Thread-0가 동시에 실행되는 것
--> 그리고 이로 인해 main 스레드에서 Thread-0 run 메서드가 아닌 start 메서드를 호출해야하는 이유도 이해가 된다.
run을 호출하게 되면 Thread-0에서 run 메서드를 실행하는게 아니라
main 스레드에서 run 메서드를 실행하게 되는 것
즉, 스택 영역이 하나만 할당되는 것이다.
코드를 보면
그림으로 보면
사용자 스레드, 데몬 스레드 (user thread, daemon thread)
스레드는 두가지 종류가 있다
- 사용자 스레드 (non-daemon thread)
프로그램 주요 작업을 수행
작업이 완료 될때까지 실행된다
모든 user thread가 종료되면 JVM이 종료된다
- 데몬 스레드 (daemon thread)
백그라운드 작업을 실행
모든 user thread가 종료되면 daemon thread는 자동으로 종료된다
예를 들어 사용하지 않는 파일이나 메모리를 정리하는 작업을 수행
JVM이 종료되는 시점에 대해 추가하면
모든 사용자 스레드가 종료될 때까지 기다려야 하므로
모든 스택 영역이 빌때까지 기다리고
모두 비었다면 그때 종료된다
반면에 데몬 스레드가 돌아가고 있어도 사용자 스레드가 모두 종료된다면 JVM이 종료된다.
daemon thread 예시를 보면
데몬 스레드 내부에서 10초 후에 출력을 하도록 설정했는데
daemon thread 내부 run 메서드 출력이 나오지 않고 프로그램이 종료된다
Runnable 인터페이스 구현
앞에서는 Thread 클래스를 상속받아 스레드를 생성했는데
이번에는 Runnable 인터페이스를 구현해서 스레드를 생성해보자
실무에서는 보통 이 방법을 사용한다고 한다.
별로 다른점은 없고
Thread 객체를 바로 생성하는게 아니라
runnable 객체를 만들고 thread 객체 생성자 파라미터로 전달한다는게 좀 다르다
이 부분때문에 실무에서 주로 사용한다고 한다.
정리하면 두가지 이유로 Runnable 인터페이스 구현 방식을 사용하라고 한다.
1. 스레드와 실행할 작업을 명확히 분리할 수 있다
2. 인터페이스를 사용하는 것이 상속보다 더 유연하고 유지보수 하기 쉽다
'SUMMARY > JAVA' 카테고리의 다른 글
[JAVA] 1. 프로세스와 스레드 (3) | 2025.08.11 |
---|