모종의 이유로 다시 내용을 훑고있는데, 모든 내용이 다 … 유기적으로 연관이 되어있다는 걸 알고 흐름에 따라 정리해보기로 함.
JVM 메모리 구조 → 멀티스레드와 공유 자원 → 경쟁 상태 문제 발생 → 동기화(뮤텍스, 세마포어)로 문제 해결 → GC와 static의 관계
JVM
자바 바이트코드(.claass 파일)를 운영체제가 이해할 수 있는 언어로 번역하고 실행하는 소프트웨어입니다. 특정 운영체제에 종속되지 않고 자바 프로그램을 실행할 수 있게 해주는 가상 컴퓨터입니다.
- 플랫폼 독립성, 메모리 관리, 런타임 환경 제공
JVM 메모리 구조
자바 프로그램을 실행하면 하나의 프로세스가 생성되고, 그 안에 JVM이 동작합니다.
JVM은 프로그램을 실행하는 데 필요한 메모리를 여러 영역으로 나누어 관리하는데, 크게 스레드가 공유하는 영역과 스레드마다 독립적인 영역으로 나뉩니다.
- 스레드가 공유하는 영역
- JVM 내의 모든 스레드가 함께 사용합니다. 여러 스레드가 동시 접근이 가능하기 때문에 동기화 문제 (Race Condition)가 발생할 수 있습니다.
- 메서드 영역: 클래스 파일의 바이트 코드, 정적(static) 변수, 상수, 필드 정보, 메서드 정보 등이 저장됩니다. JVM이 프로그램을 시작할 때 로드되며, 프로그램이 종료될 때까지 남아 있습니다.
- 힙 영역: new 연산자로 생성된 객체와 배열이 저장되는 공간입니다. 대부분의 프로그램 데이터가 이 곳에 저장되며, GC의 주요 관리 대상입니다.
- 스레드마다 독립적인 영역
- 스레드가 자신만의 고유한 공간을 가지며, 다른 스레드와 공유하지 않으므로 동기화 문제가 발생하지 않습니다.
- 스택 영역: 메서드 호출 시 사용되는 지역 변수, 매개변수, 반호나 값 등이 임시로 저장됩니다. 메서드 호출이 끝나면 자동으로 소멸됩니다.
- PC(Program Counter) 레지스터: 스레드가 실행할 다음 명령어의 주소를 저장합니다. 스레드마다 실행 순서가 다르므로 각각 독립적인 PC 레지스터를 가집니다.
프로세스와 스레드
JVM과 메모리 구조를 이해하려면? → 둘의 관계를 명확히 알아야 합니다.
- 프로세스
- 운영체제로부터 메모리, CPU 등 자원을 할당받아 실행되는 프로그램의 단위
- 각 프로세스는 독립적인 메모리 공간을 가지며 하나의 JVM이 하나의 프로세스입니다.
- 스레드
- 한 프로세스 내에서 실행되는 작업의 흐름(실행 단위)
- 한 프로세스는 여러 스레드를 가질 수 있으며, 이 스레드들은 프로세스의 메모리 공간을 공유합니다.
- 자바의 멀티스레드 프로그래밍은 이 스레드들이 공유하는 힙과 메서드 영역의 데이터를 안전하게 다루는 것이 핵심입니다.
멀티스레드와 공유 자원
JVM 프로세스는 여러 개의 스레드를 생성하여 작업을 병렬로 처리할 수 있습니다. 각 스레드는 독립적인 실행 흐름을 가지지만, 힙과 메서드 영역을 공유합니다.
이러한 공유 자원에 여러 스레드가 동시에 접근하여 값을 읽거나 수정할 때 문제가 발생합니다.
(예를 들어 → 여러 스레드가 동시에 static 변수의 값을 증가시키는 작업을 한다고 가정.)
- 각 스레드는 변수의 현재 값을 읽고, 1을 더한 다음 다시 쓰는 과정을 거친다.
- 만약 한 스레드가 값을 읽은 직후 다른 스레드가 먼저 값을 수정해 버리면? → 첫 번째 스레드는 낡은 값으로 계산하게 되어 최종 결과가 예상과 달라진다.
이처럼 여러 스레드가 공유 자원에 동시에 접근하면서 발생하는 데이터 불일치 문제를 경쟁 상태(Race Condition)이라고 합니다.
경쟁 상태 문제 발생
방금 설명했듯, JVM의 힙과 메서드 영역은 모든 스레드가 공유하는 공간입니다.
여러 스레드가 이러한 공유 자원에 동시에 접근하여 값을 읽거나 수정할 때 경쟁 상태라는 문제가 발생합니다.
경쟁 상태는 여러 스레드의 실행 순서가 예측 불가능하여, 그 결과가 달라지는 상황을 말합니다.
이는 데이터 불일치나 논리적 오류를 초래합니다.
(간단한 예시 → static 변수 counter를 두 스레드가 1씩 증가시키는 상황)
- 스레드 A가 counter의 현재 값(0)을 읽습니다.
- 동시에 스레드 B도 counter의 현재 값(0)을 읽습니다.
- 스레드 A는 읽은 값에 1을 더해(0 + 1 = 1) counter에 다시 씁니다.
- 스레드 B는 자신이 읽었던 값에 1을 더해 (0 + 1 = 1) counter에 다시 씁니다.
결과 적으로 counter의 최종 값은 2가 되어야 하지만, 잘못된 순서로 인해 1이 됩니다.
이처럼 공유 자원에 접근하는 코드 영역을 임계 구역(Critical Section)이라고 부르며, 이 구역에 대한 접근은 반드시 제어해야 합니다.
동기화
경쟁 상태 문제를 해결하기 위한 방법이 바로 동기화입니다.
동기화는 여러 스레드가 공유 자원(임계 구역)에 동시에 접근하는 것을 막고, 한 번에 한 스레드만 접근하도록 순서를 제어하는 것을 말합니다.
이러한 동기화를 구현하는 대표적인 도구에 뮤텍스와 세마포어가 있습니다.
- 뮤텍스(Mutex)
- 상호 배제(Mutuql Exclusion)의 약자로 단 하나의 스레드만 임계 구역에 접근할 수 있도록 하는 잠금(Lock)메커니즘입니다.
- 잠금(Lock): 공유 자원을 사용하기 전에 먼저 잠금을 걸어 다른 스레드가 접근하지 못하도록 합니다.
- 해제(Unlock): 작업을 마친 후에는 잠금을 풀어 다른 스레드가 사용할 수 있게 합니다.
- 소유권: 잠금을 건 스레드만 잠금을 해제할 수 있습니다.
뮤텍스는 한 명의 사용자만 사용할 수 있는 화장실에 비유할 수 있습니다. 사용자가 화장실에 들어가면 문을 잠그고, 나오면서 문을 열어줍니다. 다른 사람은 문이 열릴 때까지 기다려야 합니다.
- 세마포어(Semaphore)
- 공유 자원에 대한 접근을 제어하는 동구로 정수형 변수 하나와 P연산 V연산으로 구성됩니다. 뮤텍스와 달리 여러 개의 스레드가 동시에 접근할 수 있습니다.
- 카운팅(Counting): 세마포어의 정수 값은 공유 자원에 접근 가능한 스레드 수를 의미합니다.
- P연산(Waiting): 자원을 사용하려는 스레드는 P연산을 수행하여 세마포어 값을 감소시킵니다. 만약 값이 음수가 되면 해당 스레드는 대기 상태로 전환됩니다.
- V연산(Signal): 자원 사용을 마친 스레드는 V연산을 수행하며 세마포어 값을 증가시킵니다. 대기 중인 스레드가 있다면 하나를 깨워 자원을 사용하게 합니다.
세마포어는 여러 개의 좌석이 있는 영화관에 비유할 수 있습니다. 좌석이 남아있다면(세마포어 값 > 0) 여러 명이 동시에 입장 가능하고, 좌석이 모두 차면(세마포어 값 <= 0) 다음 손님은 좌석이 생길 때까지 기다려야 합니다.
뮤텍스는 세마포어의 특수한 형태로 볼 수 있습니다. 세마포어의 값이 1인 이진 세마포어(Binary Semaphore)는 뮤텍스와 동일한 역할을 합니다. 하지만 일반적으로는 단일 자원에는 뮤텍스, 여러 개의 자원에는 세마포어를 사용합니다.
GC와 static 변수의 관계
static 변수는 JVM의 메서드 영역에 할당됩니다. 이 영역은 프로그램이 시작될 때 메모리에 로드되어 프로그램이 종료될 때까지 메모리에 남아있습니다. 이러한 static 변수는 애플리케이션의 생명주기와 같기 때문에, GC의 관리 대상이 아닙니다.
반면 new키워드로 힙 영역에 생성된 객체는 해당 객체에 대한 유효한 참조가 사라지면 GC가 이를 감지하여 메모리를 회수합니다.
이러한 특성 때문에 static 변수를 남발하면 다음과 같은 문제가 발생할 수 있습니다.
- 메모리 누수
- static 변수가 힙 영역의 객체를 참조하고 있는 한, 해당 객체는 애플리케이션이 종료될 때까지 GC의 대상이 되지 않습니다. 만약 이 객체가 불필요한 데이터를 계속 축적하면 메모리가 점진적으로 부족해지는 메모리 누수가 발생할 수 있습니다.
- 공유 자원 문제
- static 변수는 모든 스레드가 공유하는 자원으로, 멀티스레드 환경에서 동기화 처리를 제대로 하지 않으면 경쟁 상태에 빠질 수 있습니다.
따라서 static 변수는 프로그램 전반에 걸쳐 공유되어야 하는 상수나 단일 객체(싱글턴)를 정의할 때만 신중하게 사용하는 것이 좋습니다.
'CS 및 면접복기' 카테고리의 다른 글
| 20250829 (2) | 2025.08.29 |
|---|---|
| 20250820 - 디자인패턴(2) (2) | 2025.08.20 |
| 20250818 - 디자인 패턴 (2) | 2025.08.18 |
| 20250811 - Network (2) | 2025.08.11 |
| 20250808 (3) | 2025.08.08 |