🏔️ 왜 컴퓨터 메모리는 한 종류가 아닐까?
— 메모리 계층 구조(Memory Hierarchy) 완전 정복
핵심 요약: 빠른 메모리는 비싸고 작고, 느린 메모리는 싸고 크다.
이 트레이드오프를 해결하기 위해 여러 계층의 메모리를 피라미드 구조로 쌓았다.
1️⃣ 메모리 계층 구조란?
이상적인 메모리는 빠르고 + 크고 + 저렴해야 한다.
하지만 현실에서 이 세 가지를 동시에 만족하는 메모리는 존재하지 않는다.
빠름 + 작음 + 비쌈 ←──── 트레이드오프 ────▶ 느림 + 큼 + 저렴
SRAM (캐시) DRAM (RAM)
이 한계를 극복하기 위해 역할에 따라 계층을 나눠 쌓는 설계 방식이 탄생했다.
2️⃣ 메모리 계층 피라미드
3️⃣ 계층별 특성 비교
| 계층 | 속도 | 용량 | 가격 | 휘발성 | 메모리 종류 |
|---|---|---|---|---|---|
| 레지스터 | < 1 ns | 수십 Byte | 가장 비쌈 | 휘발성 | — |
| L1 캐시 | ~1 ns | 수십~수백 KB | 매우 비쌈 | 휘발성 | SRAM |
| L2 캐시 | ~5 ns | 수백 KB~수 MB | 비쌈 | 휘발성 | SRAM |
| L3 캐시 | ~20 ns | 수~수십 MB | 비쌈 | 휘발성 | SRAM |
| 메인 메모리 | ~100 ns | 수~수십 GB | 보통 | 휘발성 | DRAM |
| SSD | ~0.1 ms | 수백 GB~수 TB | 저렴 | 비휘발성 | NAND Flash |
| HDD | ~10 ms | 수 TB | 매우 저렴 | 비휘발성 | 자기 디스크 |
💡 휘발성 vs 비휘발성
- 휘발성(Volatile): 전원이 꺼지면 데이터 사라짐 → 레지스터, 캐시, RAM
- 비휘발성(Non-volatile): 전원이 꺼져도 데이터 유지 → SSD, HDD
4️⃣ 왜 이 구조가 효과적일까? — 지역성(Locality)
메모리 계층 구조가 실제로 효과를 발휘하는 이유는 프로그램의 데이터 접근 패턴에 규칙성이 있기 때문이다.
시간 지역성 (Temporal Locality)
한 번 접근한 데이터는 곧 다시 접근할 가능성이 높다.
int sum = 0;
for (int i = 0; i < 1000; i++) {
sum += i; // sum은 매 반복마다 접근 → 캐시에 유지됨
}
공간 지역성 (Spatial Locality)
접근한 데이터 주변도 곧 접근할 가능성이 높다.
int[] arr = {1, 2, 3, 4, 5, ...};
for (int x : arr) {
process(x); // 순서대로 접근 → 캐시 라인(64byte) 단위로 미리 적재
}
두 지역성이 계층 구조를 만나면
CPU가 arr[0] 요청
→ 캐시 미스 → RAM에서 캐시 라인(64byte) 통째로 캐시에 적재
→ arr[0] ~ arr[15] (int 기준 16개)가 한번에 캐시로 올라옴
→ arr[1], arr[2] ... 접근 시 캐시 히트 ✅
→ RAM 접근 최소화 → 성능 향상
5️⃣ 성능의 핵심 — 하위 계층 접근을 줄여라
성능 최적화의 본질은 "CPU가 하위 계층까지 내려가는 횟수를 최소화" 하는 것이다.
⚡ RAM 한 번 접근할 시간에 레지스터는 200번 연산할 수 있다.
6️⃣ Java 개발자 관점 — 계층 구조를 의식한 코드
📌 JVM 메모리 영역과 계층 구조의 연결
하드웨어 계층 JVM 메모리 영역
──────────────────────────────────────
레지스터 / L1 ←→ 지역 변수, 연산 중 임시값
L2 / L3 캐시 ←→ 자주 접근하는 객체 필드
RAM (DRAM) ←→ Heap (객체), Method Area (클래스 정보)
SSD / HDD ←→ 클래스 파일(.class), 로그, DB
✅ 계층 구조를 고려한 코드 패턴
// ❌ 계층 구조 비친화적 — 무작위 메모리 접근
Map<Long, Order> orderMap = new HashMap<>();
for (long id : idList) {
process(orderMap.get(id)); // Heap 곳곳의 객체 무작위 접근 → 캐시 미스 빈번
}
// ✅ 계층 구조 친화적 — 연속 메모리 접근
long[] orderIds = new long[10000]; // 연속 배열
for (long id : orderIds) {
process(id); // 공간 지역성 활용 → 캐시 라인 단위로 효율적 적재
}
JVM GC와 메모리 계층의 관계
Minor GC: Young Generation만 스캔 → 소량의 RAM 접근 → 빠름 ✅
Major GC: Old Generation 전체 스캔 → 대량의 RAM 접근 → 느림 ⚠️
Full GC: 전체 Heap 스캔 → RAM 대규모 접근 → Stop-The-World ❌
GC 튜닝의 핵심 목표 중 하나가 Full GC 발생 빈도를 줄여 RAM 대규모 접근을 최소화하는 것이다.
7️⃣ 핵심 정리
빠름·비쌈·소용량 ←──────────────────▶ 느림·저렴·대용량
레지스터 → L1 → L2 → L3 → RAM → SSD/HDD
| 개념 | 한 줄 요약 |
|---|---|
| 메모리 계층 구조 | 속도·비용·용량 트레이드오프를 계층으로 분산해 해결 |
| 시간 지역성 | 최근 접근 데이터는 곧 다시 쓴다 → 캐시에 유지 |
| 공간 지역성 | 주변 데이터도 곧 쓴다 → 캐시 라인 단위 선적재 |
| 성능 최적화 | 하위 계층 접근 횟수를 최소화하는 것이 핵심 |
| Java 관점 | Heap 무작위 접근 → 캐시 미스 → 성능 저하 / 연속 배열 → 캐시 히트 |
| GC 튜닝 | Full GC를 줄여 대규모 RAM 접근 빈도를 낮추는 것이 목표 |