[CS] 메모리 구조 - Code Segment / Data Segment / Heap / Stack

김민아·2024년 12월 29일

CS

목록 보기
4/8
post-thumbnail

1. 정의

  • 메모리 : 데이터를 저장하고 처리하는 공간입니다. 주로 프로그램이 실행될 때 데이터를 빠르게 처리할 수 있도록 관리합니다.
  • 메모리 구조 : 메모리를 어떻게 구성하고 관리하는지를 설명하는 개념입니다. 컴퓨터의 메모리는 다양한 목적과 성격에 따라 여러 구역으로 나뉘며, 각 구역은 프로그램의 데이터와 명령어를 저장하고 처리하기 위해 사용됩니다.

2. 구조

3. 코드 영역 (Code Segment)

  • 역할 : 실행할 프로그램의 명령어(코드)를 저장.
  • 특징 :
    • 프로그램의 실행 파일에서 읽혀져 메모리에 적재됨.
    • 보통 읽기 전용(Read-Only)으로 설정됨.
    • 예시 : 함수, 루프, 조건문 등 실행 코드.

4. 데이터 영역 (Data Segment)

  • 역할 : 프로그램에서 사용하는 전역 변수와 정적 변수를 저장.
  • 특징 :
    • 프로그램 실행 시 이미 고정 크기로 할당되며, 프로그램이 종료될 때까지 메모리에 남아 있습니다.
  • 구분 :
    • 초기화된 데이터 영역(Initialized Data) : 초기값이 있는 전역 변수와 정적 변수가 저장되는 영역입니다.
      int global_var = 10; // 초기화된 전역 변수
      static int static_var = 20; // 초기화된 정적 변수
    • 초기화되지 않은 데이터 영역(BSS(Block Started by Symbol) Segment) : 초기값이 없는 전역 변수와 정적 변수가 저장되는 영역입니다. 이후 데이터가 초기화되더라도 런타임 동안 다른 메모리 공간으로 이동하지 않습니다.
      int global_var_uninitialized; // 초기화되지 않은 전역 변수
      static int static_var_uninitialized; // 초기화되지 않은 정적 변수

5. 힙 영역 (Heap)

  • 역할 : 동적으로 할당된 메모리를 저장.
  • 특징 :
    • 객체를 생성할 때 사용됩니다.
      PriorityQueue<Integer> pq = new PriorityQueue<>(); // 힙 자료구조를 사용
      String s = new String("hello"); // 힙 메모리를 사용
      int[] numbers = new int[10]; // 배열도 힙 메모리에 저장
      List<Integer> list = new ArrayList<>(); // 리스트 객체가 힙에 저장
      class Car {
      	String model;
      }
      Car myCar = new Car(); // myCar 객체가 힙 메모리에 생성
    • 메모리를 동적으로 요청하고 반환(해제)하는 공간.
    • 동적 메모리를 요청하지 않으면 생성되지 않습니다.
      • 할당과 해제를 개발자가 명시적으로 수행.
      • 메모리 누수(Memory Leak)가 발생할 수 있음.

6. 스택 영역 (Stack Segment)

  • 역할 : 함수 호출 시 생성되는 지역 변수와 함수 호출 정보를 저장.
  • 특징 :
    • 함수가 호출될 때마다 자동으로 생성됩니다.
    • 예를 들어, 함수의 지역 변수를 선언하거나 함수 호출 시 스택이 사용됩니다.
    • LIFO(Last In, First Out) 방식으로 늦게 들어온 게 먼저 나갑니다.
      • 함수가 종료되면 스택 메모리가 자동으로 해제됩니다.
      • 빠른 접근 속도를 가집니다.
  • 구성 요소 :
    • 지역 변수, 매개변수
    • 함수 호출 정보 (복귀 주소 등).
  • 스택 영역의 크기 비교(반복 호출 vs 재귀 호출) :
    • 반복문 내에서 함수 호출(예를 들어, for문에서 함수 A를 3번 호출하는 경우)
      for (int i = 0; i < 3; i++) {
          A();  // A 함수가 반복적으로 호출됨
      }
      • 스택은 함수 호출이 일어날 때마다 커집니다.
      • 함수 A는 스택에서 단일 프레임을 차지하며, 반복문 내에서 같은 함수 A를 호출해도 매번 같은 스택 공간을 차지합니다.
      • 결과적으로 스택 공간은 함수 A를 3번 호출해도, 함수 호출이 끝나면 해당 프레임이 빠져 나가고 다시 반복문에서 새로 호출되는 형태이므로, 스택 크기는 변하지 않습니다.
    • 함수 안에 함수가 호출되는 경우(예를 들어, 재귀적으로 함수 A에서 함수 B를 호출하고, 함수 B에서 함수 C를 호출하는 경우)
        void A() { B(); }
        void B() { C(); }
        void C() { /* 작업 */ }
      • 이 경우 스택은 함수 호출이 중첩될 때마다 커집니다.
      • 함수 A 호출 → 함수 B 호출 → 함수 C 호출 → 함수 C 종료 → 함수 B 종료 → 함수 A 종료 순으로 스택이 하나씩 차고 빠집니다.
      • 재귀 함수는 각 함수 호출마다 스택을 확장시키고, 끝나면 스택이 줄어들므로 스택 크기가 커집니다.
    • 결론 : 반복문에서는 같은 함수 호출로 인해 스택이 증가하지 않지만, 재귀 호출이나 함수 호출이 중첩될 때 스택 크기가 커지게 됩니다.

7. 특징

  • 메모리 주소 값에 높낮이가 있는 이유 :
    • 힙과 스택은 서로 반대 방향으로 성장하므로, 최대한 충돌하지 않게 설계합니다.
    • 구조적인 배치로 각 영역을 쉽고 효율적으로 관리합니다.
  • 메모리 접근 속도 : 스택은 힙보다 빠른 속도를 가지며, 힙은 동적 할당/해제로 인해 더 복잡합니다.
  • 메모리 누수 : 힙 영역에서 동적 할당된 메모리를 해제하지 않으면 메모리 누수가 발생합니다.
  • 힙과 스택의 충돌 :
    • 힙과 스택은 서로 반대 방향으로 성장하며, 최악의 경우 충돌할 수 있습니다. 충돌이 발생하면 StackOverflowError나 OutOfMemoryError가 발생합니다.
    • 운영체제는 프로세스별로 메모리를 제한적으로 할당하여 충돌을 방지합니다. 예를 들어, 프로그램이 4GB 메모리를 사용할 수 있다면, 힙과 스택이 각각 2GB를 넘지 못하도록 제한합니다.
    • 스택 오버플로(Stack Overflow) : 스택 크기를 초과하는 재귀 호출이나 큰 데이터를 저장하면 발생합니다.
      • 충돌이 발생하면 프로그램이 강제 종료됩니다.
  • 메모리 제한 : 각 영역의 크기는 운영체제와 프로그램 설정에 따라 제한됩니다.
profile
안녕하세요.

0개의 댓글