Team Project (2021-2022)/CS Study

[7주차]Garbage Collection, 함수형 언어

꾸지새미언니 2022. 3. 21. 13:23

✅ Q1. Garbage Collection이란, 동작 방식에 대해서 설명하세요.

Java 이전의 C나 C++같은 언어는 개발자가 직접 메모리 할당과 해제를 컨트롤 해야했다. (ex: C의 malloc과 free) 이는 잦은 메모리 이슈로 이어졌고, 개발환경 개선을 위해서 Garbage collection이 등장했다. 개발자가 하던 메모리 할당과 해제를 대신 해주는 것이다. 이렇게 메모리는 알아서 관리하는 언어들을 Managed language라고 한다.

언어와 동작하는 환경마다 GC의 동작 방식이 조금씩 다르지만, 특정한 때에 특정한 방식으로 필요없는 정보(garbage)를 쓸어담아 버린다.

자바스크립트의 GC

자바스크립트는 객체가 생성되었을 때 자동으로 메모리를 할당하고, 쓸모 없어졌을 때 자동으로 해제한다. 이것이 garbage collection이다.

이러한 자동 메모리 관리는 잠재적 혼란의 원인이기도 한데, 개발자가 메모리 관리에 대해 고민할 필요가 없다는 잘못된 인상을 줄 수 있기 때문이다.

메모리 생존주기

메모리 생존주기는 프로그래밍 언어와 관계없이 비슷하다.

  1. 필요할때 할당한다.
  2. 사용한다. (읽기, 쓰기)
  3. 필요없어지면 해제한다.

두 번째 부분은 모든 언어에서 명시적으로 사용된다. 그러나 첫 번째 부분과 마지막 부분은 저수준 언어에서는 명시적이며, 자바스크립트와 같은 대부분의 고수준 언어에서는 암묵적으로 작동한다.

더 이상 필요하지 않은 메모리를 찾는건 비결정적 문제기 때문에, 가배지 컬렉터들은 이 문제에 대한 제한적인 해결책을 구현한다.

아래는 주요한 가비지 컬렉션 알고리즘들과 그 한계를 이해하는데 필요한 개념이다.

주요한 GC 알고리즘

참조

GC 알고리즘의 핵심 개념이다. A라는 메모리를 통해 (명시적이든 암시적이든) B라는 메모리에 접근할 수 있다면 "B는 A에 참조된다" 라고 한다. 예를 들어 모든 자바스크립트 오브젝트는 prototype  을 암시적으로 참조하고 그 오브젝트의 속성을 명시적으로 참조한다.

참조-세기(Reference-countring) GC

참조-세기 알고리즘은 가장 소박한 알고리즘이다. 이 알고리즘은 "더 이상 필요없는 오브젝트"를 "어떤 다른 오브젝트도 참조하지 않는 오브젝트"라고 정의한다. 이 오브젝트를 "가비지"라 부르며, 이를 참조하는 다른 오브젝트가 하나도 없는 경우, 수집이 가능하다.

한계

수동 메모리 해제

특정 메모리를 언제 해제할지 수동으로 결정하는 것이 더 나을 떄가 있다. 그리고 그렇게 하려면, 객체 메모리에 도달할 수 없도록 명시하는 기능이 필요하다. 하지만 Javascript에서는 명시적, 또는 프로그래밍 방식으로 가비지 컬렉션을 작동시킬 수 없다.

순환 참조

다음 예제에서는 두 객체가 서로 참조하는 속성으로 생성되어 순환 구조를 생성한다. 함수 호출이 완료되면 이 두 객체는 스코프를 벗어나게 될 것이며, 그 시점에서 두 객체는 불필요해지므로 할당된 메모리는 회수되어야 한다. 그러나 두 객체가 서로를 참조하고 있으므로, 참조-세기 알고리즘은 둘 다 가비지 컬렉션의 대상으로 표시하지 않는다. 이러한 순환 참조는 메모리 누수의 흔한 원인이다.

function f() {
  var x = {};
  var y = {};
  x.a = y;         // x는 y를 참조한다.
  y.a = x;         // y는 x를 참조한다.

  return "GDSC SM";
}

f();

해결책

표시하고 쓸기(Mark0and-sweep) 알고리즘

이 알고리즘은 "더 이상 필요없는 오브젝트"를 "닿을 수 없는 오브젝트"로 정의한다.

이 알고리즘은 roots 라는 오브젝트의 집합을 가지고 있다(자바스크립트에서는 전역 변수들을 의미한다). 주기적으로 가비지 콜렉터는 roots로 부터 시작하여 roots가 참조하는 오브젝트들, roots가 참조하는 오브젝트가 참조하는 오브젝트들... 을 닿을 수 있는 오브젝트라고 표시한다. 그리고 닿을 수 있는 오브젝트가 아닌 닿을 수 없는 오브젝트에 대해 가비지 콜렉션을 수행한다.

이 알고리즘은 위에서 설명한 참조-세기 알고리즘보다 효율적이다. 왜냐하면 "참조되지 않는 오브젝트"는 모두 "닿을 수 없는 오브젝트" 이지만 역은 성립하지 않기 때문이다. 위에서 반례인 순환 참조하는 오브젝트들을 설명했다.

2012년 기준으로 모든 최신 브라우저들은 가비지 콜렉션에서 표시하고-쓸기 알고리즘을 사용한다. 지난 몇 년간 연구된 자바스크립트 가비지 콜렉션 알고리즘의 개선들은 모두 이 알고리즘에 대한 것이다. 개선된 알고리즘도 여전히 "더 이상 필요없는 오브젝트"를 "닿을 수 없는 오브젝트"로 정의하고 있다.

https://developer.mozilla.org/ko/docs/Web/JavaScript/Memory_Management#reference-counting_garbage_collection

Q2. 함수형 언어에 대해서 설명하고 함수형 언어를 사용했을 때 장점을 설명하세요.

프로그래밍 패러다임

프로그래밍의 패러다임 형태로, 쉽게 이야기하면 프로그래밍 스타일을 의미한다.

프로그래밍 패러다임은 프로그래머에게 프로그래밍의 관점을 가지게 해주고, 결정하는 역할을 한다!

  • 명령형 프로그래밍: 문제를 어떻게(How) 해결해야 하는지에 대한 명령을 컴퓨터에게 알려주는 프로그래밍 방법이다. → 프로그램 = 명령의 수행이다.
    • 절차지향 프로그래밍→ 단순한 순차적 명령 수행뿐만 아니라 프로시저(루틴, 서브루틴, 함수, 메서드 등)를 이용해 작성하는 방법이다.
    • → 대표적인 절차지향 언어: C++, C
    • 객체지향 프로그래밍→ 프로그래머들이 프로그램을 상호작용하는 객체들의 집합으로 볼 수 있게 한다.
    • → 객체 단위의 개발은 코드가 직관적이며 유지보수가 쉽다는 장점이 존재한다.
    • → 대표적인 객체지향 언어 : Java, C#, C, 하스켈, 스킴
    ⇒ 명령형 프로그래밍에서는 프로그래머가 실행될 알고리즘을 명시하고 목표는 명시하지 않는다.
  • 선언형 프로그래밍: 무엇(What)을 해결해야 하는지 집중하고 그 해결 방법은 컴퓨터에게 맡기는 프로그래밍 방법이다.
    • 함수형 프로그래밍→ 프로그래머들이 프로그램을 상태값을 지니지 않는 함수값들의 연속으로 생각할 수 있게 한다.
    • → 대표적인 함수형 언어 : HTML, Python, SQL
    ⇒ 선언형 프로그래밍에서는 알고리즘을 명시하지 않고 목표를 명시한다.

⇒ 하나의 프로그래밍 언어가 하나의 프로그래밍 패러다임을 가지는 것은 아니다!

Ex) C는 절차지향이면서 객체지향적 프로그래밍 언어이며, Java의 경우 jdk 1.8(Java 8) 이전은 객체지향 프로그래밍 패러다임을 지원하는 반면 jdk 1.8이상의 자바는 람다식, 생성자 및 메서드 레퍼런스 등 함수형 프로그래밍 패러다임을 지원하기 위한 것들을 도입했다. → 비교적 최근 언어인 Switft, Go, Kotlin 등은 처음부터 여러 가지 패러다임을 지원하도록 만들어졌고, 최근에는 이렇게 처음부터 여러 패러다임을 지원하도록 프로그래밍 언어를 설계하는 시도가 늘었다고 한다.(멀티 패러다임) 스칼라도 객체 지향 프로그래밍 요소와 함수형 프로그래밍 요소가 결합된 다중패러다임 프로그래밍 언어이다.

함수형 프로그래밍

정의

💡 자료 처리를 수학적 함수의 계산으로 취급하고 상태와 가변 데이터를 멀리하는 프로그래밍 패러다임의 하나이다.

특징

  • 이론적 배경이 람다 계산식이다.
    → 1930년대 알론소 처치의 람다 계산법에서부터 시작되었다.
  • 최소 단위가 (순수) 함수이다. → 재사용성이 높다.
    • 순수 함수란? - 인자 1개 이상을 받고 데이터나 다른 함수를 반환해야 한다.
      • 동일한 입력에는 항상 같은 값을 반환해야 한다.
      • 함수의 실행은 프로그램의 실행(외부)에 영향을 미치지 않아야 한다.(Side Effects 관련 내용)
  • Side Effects를 방지한다
    • Side Effects란?
      • 컴퓨터과학에서 “함수가 결과값 이외에 다른 상태를 변경시킬 때” 부작용이 있다고 한다.
      • Ex) 함수가 전역변수, 정적변수를 수정할 때, 인자로 넘어온 것을 변경시킬 때, 화면에 데이터를 쓸 때 등...
      • 부작용은 프로그램의 동작을 이해하기 어렵게 만든다.
      → 함수형 프로그래밍은 부작용을 최소화하는 방법이다. 즉, 프로그램의 동작을 이해하기 쉽다.
  • Immutability를 지향한다.
    • Immutability란?
      • 상태를 변경하지 않는 것
      • → 상태의 변경이란?
      • 메모리에 저장된 값, 그리고 입출력에 포함되는 값을 변경하는 모든 행위를 의미함
      • call by value, call by reference를 떠올리면 쉬움!

⇒ call by value: 인자로 넘긴 변수를 복사해 새로운 지역변수를 만든다.
⇒ call by reference: 인자로 주소를 넘기기 때문에 변경이 발생한다.

대표적인 함수형 언어

  1. 리액트 → 함수형 프로그래밍 패러다임을 지향하는 라이브러리로, 불변성을 강조한다.
  2. F# → 강타입 함수형 우선 다중패러다임 언어이다.(ML 계열의 언어이다.)
  3. 클로져 → 불변값과 시간-진행 구문을 통한 프로그래밍을 강조함 ⇒ 멀티스레드 프로그램의 개발을 용이하게 하기 위해서임

장점

→ side effects를 최소화하고 immutability를 지킨다는 것 생각하기!

  • 동시성 문제를 해결할 수 있다.
    : 멀티스레드를 활용한 동시성 프로그래밍은 교착상태에 빠질 위험이 크다. 이때 이러한 문제가 발생하는 원인은 스레드 간에 공유되는 데이터나 상태 값이 변경 가능하기 때문이다. 하지만 함수형 언어는 모든 데이터가 변경 불가능하다. 따라서 여러 스레드가 동시에 하나의 공유 데이터에 접근해도 동시성 문제를 일으키지 않는다.
  • 시스템의 불규칙한 상태 문제, 그리고 버그가 발생할 가능성이 줄어든다.
    : 부작용은 버그를 발생시킬 가능성을 높일뿐만 아니라, 프로그램 상태를 변하게 하기 때문에 프로그램의 순서에 따라 다른 결과를 초래할 수 있다.
  • 프로그램의 동작을 예측하고 이해하기 쉽다.
  • 테스트와 유지보수하기 쉽다.
  • 상태의 변경을 추적하기 쉽다.
  • Thread 안전성을 보장받을 수 있다 → 병렬 처리를 동기화 없이 진행할 수 있다.

총정리

클린 코드 책 저자 Robert C.Martin은 함수형 프로그래밍을 “대입문이 없는 프로그래밍"이라고 정의했다.

 

Q3. Garbage Collection이란? 

  • 메모리 관리 기법 중 하나
  • 동적 할당 메모리 중 필요 없게 된 영역 해제하는 기능
  • 변수가 가리키지 않는 메모리 영역 탐지하여 자동으로 해제

🔵 장점

  1. 프로그래머가 동적으로 할당한 메모리 영역을 직접 관리하지 않아도 됨
  2. 버그를 줄이거나 막을 수 있음
    1. 유효하지 않은 포인터 접근 : 메모리 해제한 영역에 접근하는 버그
    2. 이중 해제 : 이미 해제했던 메모리 다시 해제하는 오류
    3. 메모리 누수 : 사용하지 않는 메모리 해제 안하고 쌓아 놓으면 메모리 누수 + 메모리 고갈

🔴 단점

  1. 해제할 메모리 결정하는 알고리즘에 의한 비용 발생
  2. 자동으로 GC가 해제 시점을 추적하기 때문에 비용 발생 (프로그래머가 아는 경우에도 탐색해야 함)
  3. 메모리가 해제되는 시점을 알 수 없음
  4. GC 행동하는 타이밍 알 수 없고 점유 시간 예측 어려워서 실시간에는 사용 어려움

언어별 Garbage Collection

☕ Java

  • Garbage Collection : JVM 의 기능 중 하나
  • Java Runtime시 Heap 영역에 저장되는 객체 중 사용하지 않는 객체를 수집하여 정리
  • Garbage Collector : GC 작업 진행
    • 역할 : 1️⃣ 메모리 할당, 2️⃣ 사용중인 메모리 인식, 3️⃣ 사용하지 않는 메모리 인식
    • 위의 작업들로 메모리 사용 가능성과 GC 진행 시점 결정

참고 : https://deveric.tistory.com/64

🐍 Python

  • Garbage Collector : GC 작업 진행
  • 기본적으로 Reference Counting을 바탕으로 GC 수행 & 메모리 관리
  • Reference Counting :
    • 객체가 참조 당할 때 reference count ⬆️
    • 참조 사라지면 reference count ⬇️
    • reference count == 0 이면 객체 메모리에서 해제

참고 : https://twinparadox.tistory.com/623

🔔 JavaScript

  • 2가지의 GC 알고리즘
    • Reference Counting : 어떤 값이 어디에서도 참조 당하고 있지 않으면 제거
      • 순환참조(circular reference)의 경우 메모리 누수 요인이 됨
    • Mark-and-sweep : 어떤 값의 도달 가능성에 초점을 둠
      • 도달 가능성(reachability) : root(global object)에서 해당 값까지 도달 가능 여부
      • 참조❌ 경우 : 도달 불가능 → 메모리 해제
      • 참조⭕ 도달❌ 경우 : 도달 불가능 → 메모리 해제

참고 : https://sustainable-dev.tistory.com/158

※ 참고 블로그들에 각 언어에 대한 예시 있음