CS/운영체제

메모리 구조

혠꿍 2024. 9. 24. 13:12


자바 메모리 구조에 대해 학습힌다.

 

Memory Structure

 

1. 메모리 구조란?

프로그램이 실행되게 되면 운영체제는 프로그램 정보를 메인 메모리에 공간을 할당해주게 된다. 때문에 프로그램이 실행 되기 위해선 메모리에 로드되어야한다. 메모리에 올라간 데이터를 읽고 쓰면서 코드가 동작되게 된다.
 

 

1. 코드 영역 (Text Segment)

컴파일된 코드가 저장된다. CPU가 참조해 프로그램을 수행하는 영역이다. 읽기 전용이기 때문에 메모리에 올라간 순간 부터 수정이 불가능하며 모든 함수와 메소드가 저장된다.

2. 데이터 영역 (Data Segment)

프로그램이 시작될때 데이터가 저장되고 종료 될때까지 메모리를 유지한다.

  • 전연 변수
    • 프로그램 전체에서 접근이 가능하다.
  • 정적 변수
    • 한번 초기화 되면 프로그램이 종료 될때까지 값이 유지된다.

3. 힙 영역 (Heap Segment)

프로그래머가 관리해야하는 메모리 영역이다. 프로그램에 의해 동적으로 할당(new 함수를 통해 메모리를 할당 받을때 저장되는 영역)되는 메모리가 저장된다.

  • 수동으로 메모리 관리해야한다.
    • 메모리 할당, 해제를 프로그래머가 해주어야한다.
    • Java는 GC가 해제를 해주기 때문에 개발자가 해제를 하지 않아도 된다.
  • 메모리 할당 속도가 느리다.
    • 할당 되는 메모리 영역이 불연속적이기 때문에 스택 영역에 비해 느리다.
  • 메모리 누수 위험이 있다.
    • 메모리를 해제를 직접 해주지 않으면 메모리 누수가 발생한다.

4. 스택 영역 (Stack Segment)

프로그램이 실행중에 사용하는 임시 메모리 영역으로 함수 호출 시 생성되는 지역변수, 매개변수, 리턴 주소 등을 저장한다. 함수가 호출되면 메모리가 할당되고, 함수가 종료되면 메모리에서 해제된다. 

  • 자동으로 메모리를 관리한다.
    • 함수가 호출되면 할당, 종료되면 해제 과정을 스스로 수행한다.
  • 메모리 할당 속도가 빠르다.
    • 연속된 메모리에 데이터를 저장하기 때문에 빠르게 할당과 해제가 가능하다.
  • 선입후출 (LIFO : Last In, First Out)
    • 마지막에 들어온 데이터가 먼저 처리된다.

스택 영역에서 선입후출을 사용하는 이유

fun main(){
   a()
}

fun a(){ // 첫번째
   b()
}

fun b(){ // 두번째
   c()
}

fun c(){ // 세번째
   println("스택 메모리에 마지막으로 쌓이는 스택 프레임")
}

 
main 함수를 실행시키면 아래와 같은 과정이 일어난다.

  1. a가 스택에 쌓인다.
  2. b가 스택에 쌓인다.
  3. c가 스택에 쌓인다.
  4. c가 종료된다.
  5. c가 스택에서 제거된다.
  6. b가 스택에서 제거된다.
  7. a가 스택에서 제거된다.

가장 먼저 실행되어 스택에 쌓였다면 제일 마지막 실행된 스택이 제거 될때까지 메모리에 남아있기 때문에 선입후출 특징을 가지고 있다.

5. 힙 영역 & 스택 영역 경쟁 구조

프로그램이 실행되면 운영체제는 메모리 공간을 할당해준다. 할당 받은 메모리 공간은 프로그램이 종료 될때까지 변하지 않기 때문에 힙과 스택 영역이 유동적으로 메모리를 나누어 가지게 된다.
동적으로 메모리를 할당하는 경우가 많다면 힙 영역이 더 커지고, 실행 중 함수 호출과 종류가 많다면 스택 영역이 커졌다 작아졌다를 반복한다. 프로그램의 동적 메모리 사용, 함수 호출 패턴에 따라 달라진다.

6. 낮은 주소 & 높은 주소

프로그램이 실행될때 운영체제로 부여받은 메모리에 상대적인 위치이다.

  • 낮은 주소
    • 메모리 구조의 하단에 위치한다.
    • 힙이 많은 메모리를 차지하게 된다면 높은 주소 방향으로 자라나게 된다.
  • 높은 주소
    • 메모리 구조의 상단에 위치한다.
    • 스택이 많은 메모리를 차지하게 된다면 낮은 주소 방향으로 자라나게 된다.

7. 스택 영역 컴파일, 런타임

스택 메모리는 함수 호출과 관련된 정보들이 런타임에 올라가지만 크기는 컴파일 타임에 정해진다.

  • 컴파일 타임 (크기 계산)
    • 함수가 사용하는 지역변수와 매개변수의 크기를 계산해 어느정도 메모리를 사용할지 경정하게 된다.
    • 메모리에 할당되거나 해제되는 작업은 일어나지 않는다.
  • 런타임 (실제로 메모리 할당, 해제)
    • 함수가 호출된 순간 컴파일러가 계산한 스택 메모리 크기에 따라 공간을 할당한다.
    • 호출되면 메모리에 할당되었다가 종료되면 메모리에서 해제된다.

컴파일 타임에 크기가 결정되는 방식

  • 지역변수 크기 계산
  • 매개변수 크기 계산
  • 리턴주소 저장
  • 함수 호출 (재귀 호출을 하게 된다면 각각의 메모리를 차지하게 된다.)

스택 오버플로우 (Stack overflow)

스택의 크기는 컴파일 타임에 결정되어 확장되지 않는다. 프로그램 실행 중 많은 함수 호출이나 메모리를 많이 차지하는 깊은 재귀가 발생하게 되면 허용된 메모리 크기를 넘어서는 경우가 발생할 수도 있다. 이때 발생하는 에러이다.