가상 메모리(Virtual Memory)란 무엇인가요?

김상욱·2024년 11월 30일
0

가상 메모리(Virtual Memory)란 무엇인가요?

가상 메모리(Virtual Memory)는 운영 체제가 물리적 메모리(램)의 제한을 극복하고, 프로그램 실행에 필요한 메모리를 효과적으로 관리하기 위해 사용하는 메모리 관리 기법.

주요 개념
가상 주소와 물리 주소 : 가상 메모리는 프로그램이 메모리를 가상 주소로 참조하도록 합니다. 운영 체제는 가상 주소를 실제 물리적 메모리 주소로 변환하여 프로그램을 실행.(mapping)
페이지(paging) : 가상 메모리는 일정 크기의 페이지(page) 단위로 나눔. 프로그램이 필요로 하는 데이터만 물리 메모리에 적재하여 효율적으로 사용
swapping : 물리적 메모리가 부족할 경우, 사용하지 않는 페이지를 디스크의 스왑 영역으로 이동시켜 공간을 확보. 다시 필요할 때 디스크에서 가져와 메모리에 적재.
: swapping의 과정 - 여러 프로그램이 동시에 실행되거나 프로그램이 사용하는 데이터 크기가 물리적 메모리 크기를 초과하면 메모리 부족 -> 운영 체제는 메모리 내에서 당장 사용되지 않는 페잊(Idle Pages)를 식별하여 페이지 교체 알고리즘을 사용해 교체할 페이지를 선택. -> 선택된 페이지를 디스크의 스왑 영역에 저장. 스왑 영역이란 일반적으로 운영 체제에서 따로 설정한 디스크 공간. -> 스왑된 페이지가 차지한 물리적 메모리 공간을 해제하여 다른 데이터를 적재할 수 있도록 함. -> 프로그램이 스왑된 페이지를 다시 요청하면 페이지 폴트(Page Fault)가 발생. 운영 체제는 해당 페이지를 디스크에서 물리 메모리로 다시 가져와 적재. 필요 시, 또 다른 페이지를 스왑하여 공간을 확보.
: Page Fault란 프로세스가 참조하려는 페이지가 현재 물리적 메모리에 없는 상황.

가상 메모리의 특징

  • 물리 메모리 크기에 상관없이 큰 프로그램을 실행 가능. 사용자는 물리 메모리 크기를 신경 쓰지 않아도 됨
  • 각 프로세스는 독립적인 가상 메모리 공간을 가지므로, 다른 프로세스의 메모리 접근을 방지하여 안정성을 높임.
  • 실제로 사용되는 데이터만 물리 메모리에 적재하므로 메모리 사용 효율이 높아짐

가상 메모리 작동 과정
프로그램이 가상 주소를 참조 -> 운영 체제는 mmu를 통해 가상 주소를 물리 주소로 변환하기 위해 페잊 테이블을 참조 -> 참조한 페이지가 물리 메모리에 있으면 페이지 히트하고 없으면 페이지 폴트 -> 페이지 폴트 발생시, 필요한 페이지를 디스크에서 읽어 물리 메모리에 적재. -> 물리 메모리가 가득 찼다면 사용되지 않는 페이지를 디스크로 스왑하여 공간을 만듬.

++ 물리 메모리는 RAM으로 프로그램 실행에 필요한 데이터를 저장하고 CPU가 직접 접근하여 빠르게 처리. 가상 메모리는 물리 메모리의 제한을 넘어서기 위해 사용하는 논리적 메모리 공간. 디스크는 영구적으로 저장하거나 메모리 부족시 사용되는 스왑 영역이 여기에 포함.

단, 디스크 접근(스왑)이 많아질 경우(즉, 페이지 폴트)가 많아질 경우 속도가 느려짐. 또한 페잊 테이블 관리와 변환 작업으로 추가적인 처리 비용. 페이지 폴트시에서 디스크에서 데이터를 읽어오는 데 시간이 오래걸림.


실습 1: 메모리 부족 상황 시뮬레이션

목표:

가상 메모리의 역할을 이해하기 위해 메모리 부족 상황에서 Java 프로그램이 어떻게 동작하는지 실험합니다.

방법:

  1. 대규모 배열 생성:

    • Java 프로그램에서 힙 메모리 제한을 설정하고, 이를 초과하는 대규모 데이터를 처리하려고 시도합니다.
  2. 코드 예시:

    import java.util.ArrayList;
    
    public class MemoryTest {
        public static void main(String[] args) {
            ArrayList<byte[]> memoryHog = new ArrayList<>();
            try {
                while (true) {
                    // 10MB 크기의 배열을 계속 추가
                    memoryHog.add(new byte[10 * 1024 * 1024]);
                    System.out.println("Allocated 10MB more...");
                }
            } catch (OutOfMemoryError e) {
                System.err.println("OutOfMemoryError occurred!");
            }
        }
    }
  3. 실험 환경:

    • JVM 옵션으로 힙 크기를 제한합니다. 예: -Xmx64m
    • 물리 메모리와 스왑 영역 활용이 어떻게 발생하는지 확인합니다.

학습 포인트:

  • OutOfMemoryError 발생 원인과 JVM의 메모리 관리 방식.
  • 가상 메모리를 활용해 물리 메모리를 초과하는 데이터를 처리하려는 운영 체제의 동작.

실습 2: 가비지 컬렉션 동작 이해

목표:

Java의 Garbage Collection(GC)이 메모리 효율성을 높이는 방식과 가상 메모리의 상호작용을 간접적으로 이해합니다.

방법:

  1. 메모리 사용 패턴 코드 작성:

    import java.util.ArrayList;
    
    public class GarbageCollectionTest {
        public static void main(String[] args) {
            ArrayList<byte[]> data = new ArrayList<>();
            for (int i = 0; i < 100; i++) {
                data.add(new byte[10 * 1024 * 1024]); // 10MB 배열 추가
                System.out.println("Iteration " + i);
                if (i % 10 == 0) {
                    data.clear(); // 일부 데이터 삭제
                    System.gc();  // 가비지 컬렉션 요청
                    System.out.println("Requested GC.");
                }
            }
        }
    }
  2. JVM 옵션 설정:

    • -verbose:gc 옵션을 추가하여 GC 동작을 모니터링합니다.
  3. 결과 분석:

    • GC 로그를 분석하여 메모리 해제 시점과 성능 변화를 확인합니다.

학습 포인트:

  • Java의 메모리 관리 방식과 가상 메모리의 동작 원리를 비교.
  • GC와 페이지 폴트(Page Fault)가 성능에 미치는 영향.

실습 3: Spring Boot 애플리케이션 메모리 최적화

목표:

Spring Boot 애플리케이션의 메모리 사용량을 모니터링하고, 메모리 최적화를 통해 간접적으로 가상 메모리와 메모리 관리 효율성을 체험합니다.

방법:

  1. Spring Boot 프로젝트 생성:

    • 대규모 데이터 처리 API를 구현합니다.
    @RestController
    public class MemoryController {
    
        @GetMapping("/data")
        public List<String> generateData() {
            List<String> largeData = new ArrayList<>();
            for (int i = 0; i < 10_000_000; i++) {
                largeData.add("Data " + i);
            }
            return largeData;
        }
    }
  2. JVM 메모리 모니터링:

    • JVM의 메모리 사용량을 모니터링하기 위해 Spring Actuator 및 JVisualVM 사용.
    • 스왑 사용 여부를 확인하기 위해 시스템 리소스 모니터링 도구 활용(예: top, htop).
  3. 메모리 최적화:

    • 데이터 스트리밍 방식으로 변경하여 메모리 사용량 감소.
    @GetMapping("/stream")
    public ResponseEntity<StreamingResponseBody> streamData() {
        StreamingResponseBody stream = outputStream -> {
            for (int i = 0; i < 10_000_000; i++) {
                outputStream.write(("Data " + i + "\n").getBytes());
            }
        };
        return ResponseEntity.ok().body(stream);
    }

학습 포인트:

  • 메모리 집약적인 작업이 가상 메모리와 운영 체제 성능에 미치는 영향.
  • 메모리 최적화 기법을 통한 성능 개선.

3. 추가 학습 및 실습 방향

  • OS와 JVM 관계 학습:
    • JVM이 운영 체제의 메모리 관리(가상 메모리)와 어떻게 상호작용하는지 학습.
  • 디버깅 및 프로파일링:
    • JVisualVM, Eclipse MAT 등을 사용해 메모리 사용 패턴 분석.
  • 성능 최적화 실습:
    • 데이터 처리 로직 최적화를 통해 메모리 사용량과 가상 메모리 의존성을 줄이는 실습.

결론

가상 메모리 자체는 운영 체제와 하드웨어의 영역이지만, Java와 Spring 개발자의 입장에서는 메모리 부족 상황 시뮬레이션, 가비지 컬렉션 실습, Spring 애플리케이션의 메모리 최적화 등을 통해 간접적으로 가상 메모리의 동작 원리를 이해하고, 메모리 최적화를 위한 실용적인 경험을 쌓을 수 있습니다.

0개의 댓글