페이징 스토리지
페이징 스토리지 관리는 운영 체제의 중요한 부분이며, 그 핵심 아이디어는 프로세스의 주소 공간을 여러 개의 고정된 크기의 페이지로 나누는 것으로, 각 페이지를 "페이지 프레임" 또는 "페이지 프레임", "메모리 블록", "물리적 블록"이라고 합니다. "메모리 블록", "물리적 블록". 각 페이지 프레임에는 "페이지 프레임 번호"라는 숫자가 있습니다. 동시에 가상 및 실제 메모리 공간은 동일한 크기의 페이지와 프레임으로 나뉘며, 물리적 주소와 논리적 주소를 변환할 수 있도록 매핑 체계가 설정됩니다. 이러한 유형의 스토리지는 메모리 스왑 효율성을 개선하고 메모리 조각화를 줄입니다. 그러나 페이지 테이블의 저장 공간과 데이터 복제 비용이 증가하며 지터가 발생할 수 있습니다.
가상 주소
컴퓨터 시스템에서 가상 주소 공간은 운영 체제에서 관리합니다. 사용자 프로세스가 생성되면 운영 체제는 0부터 시작하여 연속적이고 독립적인 가상 주소 공간을 할당하고 프로세스는 서로 격리됩니다. 사용자 프로세스는 연속된 가상 주소에 액세스하여 물리적 메모리의 서로 다른 데이터와 명령어에 액세스할 수 있습니다.
메모리 페이징
운영 체제는 메모리 페이징 기술을 사용합니다. 메모리는 페이지라고 하는 동일한 크기의 블록으로 나뉩니다. 각 프로세스의 가상 주소 공간도 가상 페이지라고 하는 동일한 크기의 블록으로 나뉩니다. 운영 체제는 페이지 테이블을 사용하여 가상 페이지를 실제 페이지에 매핑합니다. 각 프로세스의 페이지 테이블은 독립적이므로 서로 다른 프로세스의 가상 페이지를 서로 다른 물리적 페이지에 매핑하여 메모리 격리를 달성할 수 있습니다.
메모리 보호
프로세스가 서로 간섭하고 서로의 데이터에 액세스하는 것을 방지하기 위해 운영 체제는 메모리 보호 메커니즘을 통해 각 프로세스의 메모리 액세스를 제한합니다. 페이지 테이블에서 읽기 전용, 쓰기 가능, 실행 가능 등과 같은 페이지의 액세스 권한을 설정할 수 있습니다. 운영 체제는 프로세스의 액세스 권한에 따라 페이지 테이블 항목을 설정하여 프로세스가 자신이 소유한 메모리 영역에만 액세스할 수 있고 다른 프로세스의 메모리에 액세스할 수 없도록 합니다.
프로세스 전환
다중 채널 프로그램 환경에서는 CPU가 여러 프로세스를 전환하여 동시 실행을 가능하게 합니다. 프로세스가 실행 예약되면 운영 체제는 프로세스의 페이지 테이블을 메모리 관리 장치(MMU)에 로드하여 프로세스의 가상 주소가 실제 주소에 올바르게 매핑될 수 있도록 합니다. 프로세스가 전환되면 MMU는 다음 프로세스의 페이지 테이블로 전환하여 프로세스 간 메모리 격리를 달성합니다.
메모리 할당
이번 메모리 할당에 대한 논의는 주로 힙 메모리 할당, 골랑 메모리 사전 할당 전략, 힙의 기본 단위인 페이지 mheap, 각 페이지 크기 8k, 엠스팬을 구성하는 페이지 수, 엠스팬이 메모리 사용률 향상을 위해 메모리 조각화를 줄이는 문제를 해결하기 위해 총 다양한 사양으로 나뉘는 이유에 대해 설명합니다.
// class bytes/obj bytes/span objects tail waste max waste min align
// 1 8 8192 1024 0 87.50% 8
// 2 16 8192 512 0 43.75% 16
// 3 24 8192 341 8 29.24% 8
// 4 32 8192 256 0 21.88% 32
// 5 48 8192 170 32 31.52% 16
// 6 64 8192 128 0 23.44% 64
// 7 80 8192 102 32 19.07% 16
// 8 96 8192 85 32 15.95% 32
// 9 112 8192 73 16 13.56% 16
// 10 128 8192 64 0 11.72% 128
// 11 144 8192 56 128 11.82% 16
// 12 160 8192 51 32 9.73% 32
// 13 176 8192 46 96 9.59% 16
// 14 192 8192 42 128 9.25% 64
// 15 208 8192 39 80 8.12% 16
// 16 224 8192 36 128 8.15% 32
// 17 240 8192 34 32 6.62% 16
// 18 256 8192 32 0 5.86% 256
// 19 288 8192 28 128 12.16% 32
// 20 320 8192 25 192 11.80% 64
// 21 352 8192 23 96 9.88% 32
// 22 384 8192 21 128 9.51% 128
// 23 416 8192 19 288 10.71% 32
// 24 448 8192 18 128 8.37% 64
// 25 480 8192 17 32 6.82% 32
// 26 512 8192 16 0 6.05% 512
// 27 576 8192 14 128 12.33% 64
// 28 640 8192 12 512 15.48% 128
// 29 704 8192 11 448 13.93% 64
// 30 768 8192 10 512 13.94% 256
// 31 896 8192 9 128 15.52% 128
// 32 1024 8192 8 0 12.40% 1024
// 33 1152 8192 7 128 12.41% 128
// 34 1280 8192 6 512 15.55% 256
// 35 1408 16384 11 896 14.00% 128
// 36 1536 8192 5 512 14.00% 512
// 37 1792 16384 9 256 15.57% 256
// 38 2048 8192 4 0 12.45% 2048
// 39 2304 16384 7 256 12.46% 256
// 40 2688 8192 3 128 15.59% 128
// 41 3072 24576 8 0 12.47% 1024
// 42 3200 16384 5 384 6.22% 128
// 43 3456 24576 7 384 8.83% 128
// 44 4096 8192 2 0 15.60% 4096
// 45 4864 24576 5 256 16.65% 256
// 46 5376 16384 3 256 10.92% 256
// 47 6144 24576 4 0 12.48% 2048
// 48 6528 32768 5 128 6.23% 128
// 49 6784 40960 6 256 4.36% 128
// 50 6912 49152 7 768 3.37% 256
// 51 8192 8192 1 0 15.61% 8192
// 52 9472 57344 6 512 14.28% 256
// 53 9728 49152 5 512 3.64% 512
// 54 10240 40960 4 0 4.99% 2048
// 55 10880 32768 3 128 6.24% 128
// 56 12288 24576 2 0 11.45% 4096
// 57 13568 40960 3 256 9.99% 256
// 58 14336 57344 4 0 5.35% 2048
// 59 16384 16384 1 0 12.49% 8192
// 60 18432 73728 4 0 % 2048
// 61 19072 57344 3 128 3.57% 128
// 62 20480 40960 2 0 6.87% 4096
// 63 21760 65536 3 256 6.25% 256
// 64 24576 24576 1 0 11.45% 8192
// 65 27264 81920 3 128 10.00% 128
// 66 28672 57344 2 0 4.91% 4096
// 67 32768 32768 1 0 12.50% 8192
메모리 레이아웃
다음 그림은 Golang 프로세스의 가상 메모리 공간 레이아웃을 보여줍니다. 전체 힙 영역 mheap은 아레나, 중앙, 맥캐시로 구성되며, 이들은 모두 각각의 메모리 공간을 관리하는 자체 데이터 구조를 가지고 있으며, 메모리 사용률 향상과 메모리 조각화 또는 GC 쓰레기 수집 감소 등의 측면에서 메모리의 합리적인 구성이 이루어집니다.
mheap데이터 구조
type mheap struct {
//
lock mutex
//페이지 할당을 위한 데이터 구조
pages pageAlloc
//모든 mspan 구조체 포인터
allspans []*mspan
.......
//heapArena구조화된 포인터 배열
arenas [1 << arenaL1Bits]*[1 << arenaL2Bits]*heapArena
.......
//현재 영역의 시작 및 종료 주소
curArena struct {
base, end uintptr
}
//mcentral구조화된 배열
//Go 메모리 관리에서 mspan은 가장 작은 메모리 할당 단위이며, mcentral은 모든 mcache의 모든 스레드에 대한 메모리 관리를 담당하는 글로벌 mspan 관리자입니다.
//numSpanClasses은 Go 런타임에 의해 68로 사전 정의된 상수입니다.*2=136. 이 값은 실제로 mspan의 범주 수를 나타내며, 각 범주는 서로 다른 크기의 메모리 블록에 해당합니다. 구체적으로 Go 언어는 mspan을 67개의 서로 다른 카테고리로 나누며, 각 카테고리는 32KB에서 1GB까지 크기가 2의 거듭제곱입니다. 이 중 카테고리 0과 136은 예약된 카테고리로 실제 메모리 할당에 사용되지 않습니다. 따라서 실제로 사용 가능한 mspan 카테고리의 수는 68개입니다.
//사용 가능한 68개의 mspan 카테고리 중 68개는 스캔이 필요한 중앙 캐시이고 나머지 68개는 스캔할 필요가 없는 중앙 캐시입니다. 스캔해야 하는 중앙 캐시는 쓰레기 수집 시점에 스캔하여 참조되지 않는 객체를 찾고 해당 객체가 차지하는 메모리를 해제합니다. 스캔할 필요가 없는 중앙 캐시는 포인터를 포함하지 않기 때문에 점유하고 있는 메모리를 그냥 해제하면 됩니다.
//mspan을 여러 클래스로 나누면 Go 언어가 메모리를 보다 효율적으로 관리하고 할당하여 메모리 조각화 문제를 방지하고 메모리 사용의 효율성을 향상시킬 수 있습니다.
central [numSpanClasses]struct {
mcentral mcentral
pad [cpu.CacheLinePadSize - unsafe.Sizeof(mcentral{})%cpu.CacheLinePadSize]byte
}
.......
}
아레나 데이터 구조
type heapArena struct {
// 비트맵, 자세한 내용은 아래를 참조하세요.
bitmap [heapArenaBitmapBytes]byte
//은 8192 크기의 포인터 배열이며, 각 mspan은 8KB에 해당합니다.
//이것은 mspan이 너무 많다는 것을 나타내는 것일 뿐, mspan에 페이지가 하나만 있다는 것을 의미하지는 않습니다.
//mheap에서 메모리를 할당할 때 n 페이지가 하나의 mspan에 해당할 수 있으며, 이는 mheap에 할당됩니다._.alloc.allocSpan.setSpansmspan에 대한 포인터는 힙 아레나에 있는.spansn개의 위치는
spans [pagesPerArena]*mspan
//페이지 사용 여부를 나타냅니다.= 8192 / 8 = 1024
pageInUse [pagesPerArena / 8]uint8
//페이지에 태그가 있는지 여부에 관계없이 gc는
pageMarks [pagesPerArena / 8]uint8
//이것은 페이지인유즈와 유사한 또 다른 비트맵이지만, 어떤 스팬에 특수 설정이 포함되어 있는지 표시한다는 점을 제외하면 현재 주로 파이널라이저가 포함된 버킷 또는 힙 프로파일 데이터를 저장하는 데 사용되는 런타임 내부를 의미합니다.
pageSpecials [pagesPerArena / 8]uint8
//각 이진 비트가 아레나의 포인터 크기 메모리 셀에 해당하는 1MB 크기의 비트맵입니다. 디버깅이 켜져 있는 경우.gccheckmark체크마크 비트맵은 STW 상태 동안 GC 마크 데이터를 저장하는 데 사용됩니다. 이 디버그 모드는 STW 상태의 오브젝트 그래프를 탐색하여 동시 리사이클러가 살아남은 모든 오브젝트를 올바르게 표시하는지 확인합니다.
checkmarks *checkmarksMap
// mspan 데이터 구조는 현재 아레나에서 사용되지 않은 다음 페이지의 위치를 아레나의 시작 부분에서 오프셋하여 기록합니다. 페이지 할당기는 주소 순서대로 페이지를 할당하므로 제로 베이스 이후의 페이지는 아직 사용되지 않았으므로 제로 상태로 유지됩니다. 할당된 메모리를 여전히 제로화해야 하는지 여부를 빠르게 판단할 수 있습니다.
zeroedBase uintptr
}
mcentral데이터 구조
//go:notinheap
type mcentral struct {
spanclass spanClass //어떤 스팬 클래스가 현재 mcentral
//Go 언어에서 메모리 관리는 쓰레기 수집기를 통해 자동으로 처리됩니다. 쓰레기 수집기는 주기적으로 메모리를 스캔하여 더 이상 참조되지 않는 객체가 차지하는 메모리를 해제합니다.
//partial [2]spanSet 와 전체[2]spanSet는 메모리 사용량을 추적하는 데 사용되는 두 가지 목록입니다. 각각 자유 객체가 있는 스팬 세트와 자유 객체가 없는 스팬 세트를 나타냅니다.
//partial [2]spanSet이 목록에는 아직 해제되지 않은 객체가 있는 여러 스팬이 포함되어 있습니다. 이러한 객체는 프로그램 로직 오류 또는 릴리스 지연으로 인한 것일 수 있습니다. 이러한 스팬을 추적하면 잠재적인 메모리 누수를 식별하고 그에 따라 수정할 수 있습니다.
//full [2]spanSet이 목록에는 해당 스팬의 모든 객체가 해제되어 관리할 객체가 남아 있지 않은 여러 스팬이 포함되어 있습니다. 즉, 이러한 스팬은 완전히 비워졌으므로 안전하게 회수할 수 있습니다.
//이 두 가지 목록을 설정하면 메모리 사용량을 더 잘 이해하고 잠재적인 메모리 문제를 적시에 파악하여 해결할 수 있습니다.
partial [2]spanSet // 사용 가능한 공간이 있는 스팬의 모음
full [2]spanSet // 사용 가능한 공간이 없는 스팬의 모음 또는 연결된 테이블의 현재 스팬이 mcache에 할당되었습니다.
}
mcache데이터 구조
//go:notinheap
type mcache struct {
nextSample uintptr // 힙 분석을 위한 다음 샘플링
scanAlloc uintptr // 할당된 힙의 스캔을 나타내는 데 사용됩니다.
// 마이크로 객체 할당과 관련된 자세한 내용은 마이크로 객체 할당을 참조하세요.
tiny uintptr
tinyoffset uintptr
tinyAllocs uintptr
alloc [numSpanClasses]*mspan // 136mspan의 연쇄 테이블
stackcache [_NumStackOrders]stackfreelist //
flushGen uint32
}
캐시 할당 방법
//1.먼저 mcache 유형에 대한 포인터 변수 c가 정의됩니다.
//2.그런 다음 시스템 스택에서 작업을 수행하는 데 사용되는 함수인 systemstack 함수가 호출됩니다. 이 함수에서는 먼저 mheap_에서 잠금_.cacheallocmcache
//3.메모리 블록을 할당하고 이를 mcache 타입으로 변환하여 c에 할당하는 동시에, mheap_.sweepgenc에 할당.flushGen. 마지막으로, mheap_
//4.다음으로, c.alloc배열의 각 요소를 다음과 같이 설정하여&emptymspan. 이 배열을 초기화하거나 재설정할 수 있습니다.
//5.그런 다음 nextSample() 함수가 호출되고 반환값이 c에 할당됩니다..nextSample. 다음 샘플을 위한 메모리 캐시를 설정하거나 업데이트하기 위한 것일 수 있습니다.
//6.마지막으로 c를 반환합니다.
//이 코드의 주요 목적은 새 메모리 캐시를 생성하고 초기화 및 구성하는 것입니다.
func allocmcache() *mcache {
var c *mcache
systemstack(func() {
lock(&mheap_.lock)
c = (*mcache)(mheap_.cachealloc.alloc())
c.flushGen = mheap_.sweepgen
unlock(&mheap_.lock)
})
for i := range c.alloc {
c.alloc[i] = &emptymspan
}
c.nextSample = nextSample()
return c
}
엠스팬 데이터 구조
//go:notinheap
type mspan struct {
// 스팬의 앞뒤를 가리키는 포인터입니다.
next *mspan
prev *mspan
// 현재 스팬의 첫 페이지의 첫 번째 주소
startAddr uintptr
// 현재 스팬이 얼마나 많은 페이지로 구성되어 있는지를 나타냅니다.*npages*pgae size은 현재 스팬 할당의 크기입니다.
npages uintptr
manualFreeList gclinkptr // 무료 객체 목록
// freeindex ~nelems현재 스팬의 위치 인덱스로, 현재 스팬의 다음 빈 객체의 인덱스를 표시합니다.
freeindex uintptr
nelems uintptr // 현재 스팬에서 관리되는 오브젝트 수
allocCache uint64 // freeindex의 비트마커
allocBits *gcBits // 이 mspan에 있는 오브젝트의 비트맵
gcmarkBits *gcBits // 쓰레기 수집을 위해 mspan에 표시된 비트맵입니다.
spanclass spanClass // 현재 스팬 해당 스팬 클래스
.......
}



