Interview 18장 - 컴퓨터 공학 (3)
포스트
취소

Interview 18장 - 컴퓨터 공학 (3)

Interview

문자열

유니코드

  • 전 세계의 모든 문자를 컴퓨터에서 일관되게 표현하고 다룰 수 있도록 설계된 산업 표준이다.
  • 인코딩을 통해 문자나 기호를 컴퓨터가 이해할 수 있는 신호로 만들고, 이 신호를 해석하는 디코딩 과정을 통해 처리된다.

ASCLL

  • 영문 알파벳을 사용하는 대표적인 문자 인코딩으로 7 비트로 모든 영어 알파벳을 표현할 수 있다.
  • 52개의 영문 알파벳 대소문자와, 10개의 숫자, 32개의 특수 문자, 그리고 하나의 공백 문자를 포함한다.

UTF-8과 UTF-16의 차이점

  • UTF-8과 UTF-16은 인코딩 방식의 차이를 의미합니다. UTF-8은 Universal Coded Character Set + Transformation Format – 8-bit의 약자로, UTF- 뒤에 등장하는 숫자는 비트(bit)이다.

UTF-8 특징: 가변 길이 인코딩

  • UTF-8은 유니코드 한 문자를 나타내기 위해 1 byte(= 8 bits)에서 4 bytes까지 사용한다.
  • 예를 들어, 코 라는 문자의 유니코드는 U+CF54 (16진수, HEX)로 표현됩니다. 이 문자를 이진법(binary number)으로 표시하면, 1100-1111-0101-0100 이 됩니다. 이 문자를 UTF-8로 표현하면, 다음과 같이 3byte의 결과로 표현된다.

    1
    2
    3
    4
    5
    6
    7
    8
    
    1110xxxx 10xxxxxx 10xxxxxx // # x 안에 순서대로 값을 채워넣습니다.
    11101100 10111101 10010100
    
    let encoder = new TextEncoder(); // 기본 인코딩은 'utf-8'
    encoder.encode('') // Uint8Array(3) [236, 189, 148]
    (236).toString(2) // "11101100"
    (189).toString(2) // "10111101"
    (148).toString(2) // "10010100"
    
  • ASCII 코드는 7비트로 표현되고, UTF-8에서는 다음과 같이 1 byte의 결과로 만들 수 있다.
    1
    2
    3
    4
    
    0xxxxxxx
    01100010
    encoder.encode('b') // Uint8Array [98]
    (98).toString(2) // "1100010"
    

UTF-8 특징 : 바이트 순서 고정

  • UTF-16에 비해 바이트 순서를 따지지 않고, 순서가 정해져 있다.

UTF-16 특징 : 코드 그대로 바이트로 표현 가능, 바이트 순서의 다양성

  • UTF-16은 유니코드 코드 대부분(U+0000부터 U+FFFF; BMP) 을 16 bits로 표현한다.
  • 대부분에 속하지 않는 기타 문자는 32 bit(4 bytes)로 표현하므로 UTF-16도 가변 길이라고 할 수 있으나, 대부분은 2 바이트로 표현한다.
  • U+ABCD라는 16진수를 있는 그대로 이진법으로 변환하면 1010-1011-1100-1101 이다. 이 이진법으로 표현된 문자를 16 bits(2 bytes)로 그대로 사용하며, 바이트 순서(엔디언)에 따라 UTF-16의 종류도 달라진다.
  • UTF-8에서는 한글은 3 바이트, UTF-16에서는 2 바이트를 차지한다.

가비지 컬렉션

  • 가비지 컬렉션은 프로그램에서 더 이상 사용하지 않는 메모리를 자동으로 정리하는 것이다.
  • 이 기능을 가진 언어(혹은 엔진)는 자바, C#, 자바스크립트 등이 있다.
  • C 언어 같은 저수준 언어에서는 메모리 관리를 위해 malloc()과 free()를 사용해 개발자가 스스로 메모리를 할당하고 해제해야 한다.
  • 그러나 JavaScript는 C언어와는 반대로 고수준 언어로서, 객체가 생성되었을 때 자동으로 메모리를 할당하고 필요하지 않다면 자동으로 해제하는 가비지 컬렉션이 내장되어 있다.

메모리 생존주기

  • 필요할 때 개발자가 할당한다.
  • 할당된 메모리를 사용한다.
  • 메모리가 더이상 필요하지 않으면 해제한다.

메모리 할당

  • JavaScript는 프로그래머 대신, 값을 선언할 때 자동으로 메모리를 할당해준다.
  • 변수를 선언하고 배열을 할당하여 안에 요소를 집어넣을 때, 그 배열을 담을 메모리의 크기를 고려하지 않는데, 이 부분을 JavaScript가 배열과 배열에 담긴 값들을 위한 메모리 크기 할당을 알아서 진행했기 때문이다.
  • 정수, 문자열, 함수, 객체 모든 부분에서 자동적으로 일어난다.

할당된 메모리 사용

  • 기본적으로 할당된 메모리를 읽고 쓰는 것을 의미한다.
  • 변수나 객체 속성의 값을 읽고 쓰거나, 함수 호출 시에 함수에 인수를 전달하여 수행하는 방식으로 일어난다.

메모리 해제

  • 할당된 메모리가 더이상 필요 없다면 해제를 해야 앱의 성능을 저하시키지 않는다.
  • 저수준 언어는 개발자가 직접 결정하고 해제하는 방식을 사용하며, 개발자가 직접 관여하기 때문에 개발자의 제어 정도가 굉장히 높은 편이다.
  • 가비지 컬렉션의 목적은 메모리 할당을 추적하고, 할당된 메모리 블록이 더이상 필요하지 않게 되었는지를 “스스로” 판단하여 필요하지 않다고 판단이 된다면 해당 메모리를 해제한다.

대표적인 가비지 컬렉션의 방법

  • 참조(reference)
    • 명시적이든, 암묵적이든 관계없이 메모리 관리 관점에서 어떤 객체가 다른 객체에 접근할 수 있다면 다른 객체를 참조한다고 말한다.
    • avaScript 객체는 자신의 프로토타입(prototype)에 대해 암묵적인 참조를 갖고 있고, 자신의 속성(property) 값에 대한 명시적 참조도 가지고 있다.
  • 렉시컬 스코핑(lexical scoping)
    • 변수 이름이 중첩된 함수에서 해석되는 방식을 정의하는 것으로, 중첩되어 있는 더 안쪽의 함수는 부모 함수가 값을 반환한 다음에도 부모 함수의 스코프를 포함하고 있다.

레퍼런스 카운팅(참조 횟수 계산)

  • 한 객체를 참조하는 변수의 수를 추적하는 방법으로 가장 단순한 형태의 가비지 컬렉션 알고리즘이다.
  • 객체를 참조하는 변수는 처음에는 특정 메모리에 대해 레퍼런스가 하나뿐이지만, 변수의 레퍼런스가 복사될 때마다 레퍼런스 카운트가 늘어난다.
  • 객체를 참조하고 있던 변수의 값이 바뀌거나, 변수 스코프를 벗어나면 레퍼런스 카운트는 줄어든다.
  • 레퍼런스 카운트가 0이 되면, 그 객체와 관련한 메모리는 비울 수 있다.
  • 레퍼런스 카운트가 0이 된다는 말은 아무도 그 객체에 대한 레퍼런스를 가지고 있지 않다는 말과 같다.

    1
    2
    3
    4
    5
    6
    7
    8
    
    function reference() {
      var obj1 = {};
      var obj2 = {};
      obj1.p = obj2;
      obj2.p = obj1;
    }
    
    reference();
    
    • 두 객체가 생성되고 서로를 참조하고 있는 형태이기 때문에 순환 참조가 발생한다.
    • 레퍼런스 카운팅 알고리즘에서는 두 객체가 적어도 한 번은 참조한 것으로 간주되기 때문에 둘 다 가비지컬렉션이 될 수 없게 된다.

트레이싱

  • 한 객체에 flag를 두고, 가비지 컬렉션 사이클마다 flag에 표시 후 삭제하는 mark and sweep 방법이다.
  • 객체에 in-use flag를 두고, 사이클마다 메모리 관리자가 모든 객체를 추적해서 사용 중인지 아닌지를 표시(mark)한다.
  • 표시되지 않은 객체를 삭제(sweep)하는 단계를 통해 메모리를 해제한다.
  • 객체가 필요한지 결정하기 위해 해당 객체에 닿을 수 있는지 (reachable)을 판단하며, 3단계를 거친다.
    • 루트
      • 일반적으로 루트는 코드에서 참조되는 전역 변수이다.
      • 자바스크립트에서 루트로 동작할 수 있는 전역 변수는 window 객체이다.
      • 가비지컬렉터는 모든 루트의 완전한 목록을 만들어낸다.
      • 그런 다음 모든 루트와 그 자식들을 검사해서 활성화 여부를 표시한다.
      • 루트가 닿을 수 없는 것들은 가비지로 표시된다.
      • 마지막으로 가비지컬렉터는 활성으로 표시되지 않은 모든 메모리를 OS에 반환한다.

메모리 누수

  • 예상치 못한 참조는 개발자는 더 이상 사용되지 않을 것이라 생각했지만, 어떠한 이유로 활성화 상태인 루트 트리 안에 존재하는 메모리 조각들이다.
  • 더이상 사용되지 않지만 코드 상 어딘가에 유지되어 해제되지 못한 변수들이다.
    • 우발적으로 생성된 전역변수
    • DOM 외부에서의 참조
    • 클로저의 잘못된 사용
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.