Golang의 변수 메모리 분배와 탈출 분석

3399 단어 GoLang
  • 메모리 할당 방식
  • 탈출 분석
  • 요약
  • 1. 메모리 분배 방식
    하나의 실행 프로그램이 메모리에 분배하는 것은 모두 다섯 부분으로 나뉜다. 1. text: 저장 프로그램의 이진 명령과 일부 정적 내용 2. 데이터: 초기화된 전역 변수를 저장하고, 정적 분배 3. bss: 초기화되지 않은 전역 변수를 저장하고, 정적 분배 4,stack: 창고, 주로 함수 호출 시 국부 변수를 저장한다.메모리는 시스템이 관리하고 창고에 쌓여 있는 방식을 통해 자동으로 분배되고 창고에 나가는 방식을 통해 자동으로 방출된다.5,heap:더미, 동적 분배 메모리, 쓰레기 수거기에서 관리.
    Golang의 분배 방식은 주로 창고 분배와 더미 분배이다.창고의 분배는 값이 싸고, 무더기의 분배는 비싸다.
  • 창고: 창고를 압축하고 출고하는 두 개의 CPU 명령으로만 창고 공간의 분배와 방출을 실현할 수 있으며 정적 분배 메모리에 속한다.
  • 더미: 더미를 사용하여 동적 분배를 한 후 더미 공간을 방출할 때 쓰레기 수거기를 사용하여 더미 공간에서 더 이상 사용되지 않는 대상을 검색해야 한다.

  • 창고 분배 사용: 함수 내부의 국부 변수가 외부에서 인용되지 않았습니다. 즉 변수는 컴파일하는 동안 작용역을 확정할 수 있습니다.무더기 분배만 사용할 수 있습니다. (1) 함수 내부의 국부 변수는 외부에서 인용됩니다.(2) 변수가 신청한 메모리가 너무 크고 창고의 저장 능력을 초과할 때.
    2. 탈출 분석
    명령go build -gcflags ‘-m’ ./main.go을 통해 몇 개-m를 더 자세한 내용을 볼 수 있고-l는 내연 최적화를 금지할 수 있다.
    바늘 탈출: 한 대상의 바늘은 여러 개의 라인이나 방법에 의해 인용된다.탈출 분석: Golang 컴파일러가 포인터 대상의 동적 범위를 분석하는 방법.탈출 분석은 변수의 메모리 분배를 창고나 더미에 결정한다.
    컴파일러는 컴파일할 때 쓰레기 회수 작업을 통해 변수의 메모리 분배 방식을 선택합니다.컴파일러는 코드 블록에서 변수의 역할 영역을 추적하여 변수가 가지고 있는 검사 데이터를 통해 생명주기가 실행될 때 완전히 알 수 있는지 확인하고, 만약 그렇다면 창고에 메모리를 분배할 수 있다.그렇지 않으면 이 변수가 탈출한다고 해서 메모리를 무더기로 분배할 수밖에 없다.
    변수를 무더기로 탈출시키는 전형적인 상황:
    1. 포인터를 보내거나 포인터가 있는 값을 채널에 보냅니다.컴파일할 때, 어느 고로틴이 채널에서 데이터를 받는지 알 수가 없습니다.그래서 컴파일러는 변수가 언제 풀릴지 알 수가 없어요.2. 슬라이스에 포인터 또는 포인터가 있는 값을 저장합니다.하나의 전형적인 예는 바로 []*string이다.이것은 절단된 내용의 탈출을 초래할 수 있다.비록 그 뒤의 수조는 창고에서 분배될 수 있지만, 인용된 값은 틀림없이 더미 위에 있을 것이다.3. 슬라이스의 배후 그룹이 재분배되었다. 왜냐하면 append가 용량(cap)을 초과할 수 있기 때문이다.slice가 초기화된 곳은 컴파일할 때 알 수 있습니다. 처음에는 창고에 분배됩니다.만약 슬라이드 뒤의 저장이 운행할 때의 데이터를 바탕으로 확충되어야 한다면, 무더기에 분배될 것이다.4,인터페이스 형식에서 방법을 호출합니다.인터페이스 형식에서 호출하는 방법은 모두 동적 스케줄링입니다. 방법의 진정한 실현은 실행할 때만 알 수 있습니다.상상해봐.Reader 형식의 변수 r. r.Read(b)를 호출하면 r의 값과 슬라이드 b의 배후 저장소가 모두 도망가기 때문에 무더기에 분배됩니다.
    (1) 포인터의 경우:
    바늘이 가리키는 데이터는 모두 무더기로 분배된 것이다.전통적으로 값 복사는 지침보다 대가가 높다고 여긴다.그러나 많은 실제 상황에서 값 복사는 지침을 사용하는 것보다 훨씬 싸다.1. 컴파일러는 포인터를 해제할 때 검사를 한다.메모리 유출을 피하기 위해 바늘이 nil인 상황에서 직접 패닉 () 을 사용하는 것이 목적이다.이것은 실행할 때 더 많은 코드를 실행해야 한다.만약 데이터가 값에 따라 전달된다면, 이런 것들을 할 필요가 없다. 이것은 nil2일 수 없다. 바늘에 보통 나쁜 부분적인 인용이 있을 수 없다.하나의 함수 내부의 모든 값은 창고 공간에서 분배됩니다.국부 인용은 효율적인 코드를 작성하는 중요한 부분이다.이것은 변수 데이터가 CPU 캐시 (cpu의 1급 2급 캐시) 에서 열을 높여 명령을 추출할 때 캐시가 명중하지 않을 확률을 감소시킨다.3. Cache층에서 한 무더기의 대상을 복사하면 대략적으로 하나의 바늘을 복사하는 효율과 같다고 생각할 수 있다.CPU는 각 Cache층과 메인 메모리에서 고정된 크기의 캐치로 메모리를 이동합니다.x86 시스템은 64바이트입니다.그리고 Go는 일반 메모리 조작을 더욱 효율적으로 하기 위해 Duff's device 기술을 사용했다.
    쓰레기 수거기에서 변수를 회수하려면 변수 유형에 지침이 있는지 확인해야 한다. 만약 있다면 지침이 가리키는 메모리가 회수될 수 있는지 확인해야 이 변수가 회수될 수 있는지 결정할 수 있다.이렇게 돌아가다.회수된 변수 안에 바늘이 없으면 들어가서 다시 스캔할 필요가 없고 직접 회수하면 된다.
    (2) 슬라이스 전달
    슬라이드는 저효율 메모리 분배 행위를 일으키는 열광적인 구역이다.슬라이드의 크기를 컴파일할 때 알 수 없으면, 슬라이드 뒤에 있는 그룹 (map도 마찬가지) 이 무더기로 분배됩니다.(string은 읽기 전용[]byte)
    (3) Interface 유형
    Interface 유형에서 방법을 호출하는 것은 Struct에서 직접 호출하는 방법보다 효율이 낮습니다.인터페이스 형식에서 호출하는 방법은 동적 스케줄링입니다.
    (4)uintptr、unsafe.Pointer
    Uintptr에서 생성된 참조 및 unsafe.Pointer가 인용한 데이터는 컴파일러가 탈출할 것이라고 생각하지 않습니다.효율적이지만 안전하지 않습니다.개발자는 반드시 신중하게 써야 한다.
    3. 총결산
    1. 너무 일찍 최적화하지 말고 데이터로 우리의 최적화 작업을 구동해야 한다.2. 창고 공간 분배는 저렴한 것이고 창고 공간 분배는 비싼 것이다.3. 탈출 메커니즘을 이해하면 우리가 더욱 효율적인 코드를 쓸 수 있다.4. 포인터의 사용은 창고 분배를 더욱 불가능하게 할 수 있다.5, 저효율 코드 블록에서 분배 제어를 제공하는api를 찾습니다.(봉인되지 않은 최하층 방법)6. 빈번한 곳에서interface를 사용하지 마세요.
    참고 문서: 고성능 Go 서비스의 메모리 최적화(역)
    본고는 탈출 분석과 관련하여 참고문의 많은 내용을 발췌하여 귀납과 총결을 하였다.참고 문장 중의 상세한 예시 코드는 참고할 수 있다.

    좋은 웹페이지 즐겨찾기