Go源码解析之arena.go

arena.go文件实现了Go语言的堆内存管理器,包括内存分配、回收、对齐和并发操作。主要函数有arenaAlloc、arenaFree等,用于线程安全的内存操作。arena_newArena分配新arena对象,arena_arena_New创建内存空间,arena_arena_Slice管理slice内存,arena_arena_Free释放arena内存,arena_heapify进行堆化操作,newUserArena创建用户arena,new分配arena,refill填充内存,userArenaNextFree跟踪空闲空间,userArenaHeapBitsSetType表示堆空间位图,newUserArenaChunk分配新chunk,isUnusedUserArenaChunk检查chunk是否可释放,setUserArenaChunkToFault标记chunk为不可用,inUserArenaChunk检查内存是否在用户arena中,freeUserArenaChunk释放用户arenachunk,allocUserArenaChunk分配用户arenachunk。

File: arena.go

arena.go文件实现了Go语言的堆内存管理器,它提供了跨平台的内存分配和回收服务。目的是管理堆内存区域,以保证高效、可扩展、可配置和可靠的使用。arena.go中的代码实现了跨平台内存管理器对应的核心功能,包括内存分配、内存回收、内存对齐、内存复制、内存对齐以及并发的分配和回收操作。

下面介绍arena.go中主要的数据结构和函数:

1.arenaAlloc

这个函数的主要作用是分配内存区域,并且在并发操作时可以保证线程安全。在实现上,它通过arena.alloc_m和arena.allocSlow分别管理内存的分配和回收过程。它使用了一些基于计算机体系结构的技术,如CAS指令和锁分离技术等,以保证内存分配的性能和可靠性。

2.arenaFree

这个函数的主要作用是释放堆内存区域,将其归还给操作系统。与arenaAlloc不同,它使用了spinlock来实现线程间的同步和互斥,以防止不同线程之间的竞争和冲突。

3.arenaAlloc(向量)

这些函数用于向操作系统请求更多的堆内存区域,并将其与arena中的free list相结合。这些函数采用了线程安全的技术来避免内存泄漏和段错误等问题。它们还使用了一些高级的技术,如内存对齐和地址映射,以提高内存分配的效率和性能。

4.arenaReclaim

这个函数的主要作用是重新回收内存中的空闲区域,以避免内存碎片化和资源浪费。它使用一种基于自适应分配的算法来优化内存回收过程,并通过多线程技术加快回收效率。它还实现了垃圾回收机制,用于检测和清除运行时堆中的无用对象和数据结构。

总之,arena.go文件实现了Go语言堆内存管理器的核心功能,是Go语言的重要组成部分。它为开发人员提供了高效、可扩展、可配置和可靠的内存管理服务,可以满足不同环境下的内存需求,并提高计算机系统的运行效率和性能。

Functions:

arena_newArena

arena_newArena函数是用来分配新的arena对象的。arena对象是存储堆内存的结构体,包括该arena的起始地址、结束地址、当前可用地址、该arena中的所有span等信息。

该函数会先从p所在M的central缓存中获取一个mspan对象,如果缓存中没有可用的mspan对象,则会调用mheap的alloc manual span方法,分配一块拥有预定义大小的堆内存给该mspan。然后会设置mspan的属性(如对应arena的地址、span的类型等)并将该mspan添加到该arena的span列表中。最后将该arena的起始地址和arena对象指针返回。

函数定义如下:

func arena_newArena(p *pageAlloc) (h *heapArena) {
    co := &p.central[0]
    s := co.alloc()
    for s == nil {
        list := (*[maxTinyIdx + 1]mspan)(atomic.Loadp(unsafe.Pointer(&p.tiny)))
        for i := 0; i < len(list) && s == nil; i++ {
            s = list[i].pop()
        }
        if s == nil {
            s = (*mspan)(p.allocManual(SpanBytes))
            s.init(_PageAlloc, nil000)
        }
    }
    // Initialize the arena.
    h = (*heapArena)(s.startAddr)
    h.pageIdx = p.pageIndex(h.startAddr)
    h.end = h.startAddr + _PageSize*_ArenaSize
    h.pageBitmap.init(uintptr(h.startAddr), _ArenaSize)
    h.spans = h.freeSpan[:]
    return
}

参数p为指向pageAlloc的指针,表示当前的M的堆内存分配器。函数首先从central缓存中获取一个mspan,如果获取失败则会从tiny list或者手动分配一块新的mspan。然后将该mspan的地址作为arena的起始地址,并将该mspan添加到该arena的span列表中。最后返回该arena对象的指针。

arena_arena_New

arena_arena_New函数在runtime包中的arena.go文件中,用于创建一个新的内存空间(arena)。这个arena被用于存储小对象,以减少内存分配和垃圾回收的开销。

具体而言,arena是一个连续的内存块,由小的堆块(span)组成。它提供了几种操作方法,如分配和释放内存,以及获取总内存使用情况等。

arena_arena_New函数中,会先通过mheap_.arenaHint字段获取一个预留的内存地址,然后调用sysReserve函数将该内存地址所在的内存页映射到进程的虚拟地址空间中,并返回这个内存的起始地址。在这个内存的起始地址处,创建一个新的arena结构体,并将其返回。

需要注意的是,arena并不是线程安全的,因此在使用它时需要采取一些同步措施。

arena_arena_Slice

arena.go 文件中的 arena_arena_Slice 主要用于管理 arena 结构中的 slice 型数据。具体来说,它是 arena 结构的一个内嵌类型,可以通过 arena 结构中的 arena_ arena_Slice 字段调用。

arena_arena_Slice 的作用是在 arena 结构中为 slice 型数据提供内存空间。arena_arena_Slice 实际上是一个包含了 slice 的指针和长度信息的结构体指针。它的内存空间位于 arena 结构体中的可变大小区域,也就是所有动态分配的对象都存放的区域。

具体来说,当程序需要使用 slice 型变量时,会调用 arena_arena_Slice 的 makeSlice() 方法来分配内存。makeSlice() 方法会根据 slice 的长度和元素类型,计算出所需的内存大小,并在 arena 结构体中分配这段内存空间。然后,makeSlice() 方法返回一个指向该内存空间起始地址的 slice 指针。

由于 slice 的长度可能会随着程序运行的变化而发生变化,因此 arena_arena_Slice 还提供了 growSlice() 方法来扩展 slice 的长度。growSlice() 方法会为 slice 分配额外的内存空间,并将扩展后的 slice 拷贝到新的空间中。

总之,arena_arena_Slice 是 Go 语言运行时环境中用于管理内存的重要组成部分,主要用于为 slice 型变量提供内存空间,并提供了相关的操作方法来对其进行扩展和管理。

arena_arena_Free

arena_arena_Free函数是 Go 语言运行时中的一个函数,用来释放 arena 内存。它的作用是将 arena 内存块返回给内存池。arena 内存块是运行时中用来分配小对象的一块内存,它是按页(通常为 8KB)大小划分的,当一块内存不足时就会从内存池中申请一块新的 arena 内存块来使用,从而避免了频繁申请内存的开销。

arena_arena_Free 函数接收一个 uintptr 类型的参数 arena,它表示需要释放的 arena 内存块的地址。释放 arena 内存块时,该函数会先将 arena 内存块清空,然后再将其返回给内存池。这里所说的清空是指将 arena 内存块中所有分配的对象都进行释放,使其可以被重新使用。

总之,arena_arena_Free 函数的作用是为了内存管理的效率和性能而设计的,它可以回收 arena 内存块,避免了频繁的申请和释放内存的开销,从而提高了程序的运行效率。

arena_heapify

arena_heapify函数是runtime中用来对arena进行堆化的函数。在Go语言中,arena是一种内存分配策略,它将内存分配划分为若干个区域,每个区域大小为2的幂次方。在分配内存时,如果当前arena没有足够的空间,就会分配一个新的arena,并将其加入到链表中。

arena_heapify函数的作用是对arena链表进行堆化操作,即将链表中的arena按照大小进行堆排序(小的在前,大的在后),以便在内存分配时能够快速找到大小合适的arena。

具体实现过程如下:

  1. 检查当前arena是否为空,如果是,则返回nil。

  2. 将当前arena标记为堆化状态,即将其的heapified标志设置为true。

  3. 遍历arena链表,找到大小最小的arena(即从前往后第一个未被堆化的arena),并将其作为根节点。

  4. 遍历剩余的arena,将它们插入到堆中,直到所有的arena都被插入到堆中。

  5. 对堆进行调整,将所有的arena按照大小进行堆排序。

  6. 将堆中的arena按照大小顺序重新组成链表,返回链表的头节点。

arena_heapify函数通常在分配内存前调用,以保证能够快速找到大小合适的arena。在内存分配时,如果当前arena没有足够的空间,就会从堆中查找大小合适的arena,以加速内存分配的过程。

init

在Golang的runtime中,arena.go文件中的init函数主要用于初始化arena结构体。arena结构体是用于管理和分配堆空间的数据结构,其内部包含多个span,每个span表示一段连续的内存空间,并且span的大小是固定的。arena中的init函数会主要完成以下几个任务:

  1. 初始化arena结构体,将其内部的各个字段设置为默认值,比如arena的大小、已使用的空间等;
  2. 创建一些种子(span),用于后续分配和管理内存空间,这些种子(span)具有不同的大小和数量,以适应不同的内存申请需求;
  3. 设置一些arena结构体的属性,并为其分配一定数量的内存空间,以便后续对内存空间的分配和管理。

通过这些任务的完成,init函数为arena结构体的正常运行提供了必要的基础设置和资源。在Golang的runtime中,init函数是在程序启动时自动执行的,以保证arena结构体的正常工作。

newUserArena

newUserArena是一个函数,它的作用是创建一个新的用户arena(arena是指一块连续的内存空间,用于存储对象)。在Go的运行时系统中,每个线程都有自己的arena,用于分配对象。这个函数主要是为了支持用户自定义的内存分配器。

具体来说,newUserArena会创建一个新的arena,并将它与当前线程关联起来。它会使用Go的内存分配器来申请一块大小为size的内存空间,并将该空间划分成多个大小相等的块,称为span。这些span会被添加到arena中作为可用的内存块,用于分配对象。

当线程需要分配对象时,它会使用arena中的span来分配内存。如果arena中的span用尽了,线程会向全局的内存池(global pool)申请新的span。如果全局内存池中也没有可用的span,那么它会调用用户定义的分配函数来申请新的内存空间。

总之,通过使用newUserArena函数,用户可以自定义内存分配器,并将其与线程相关联,提高程序的性能和可扩展性。

new

在go语言中,arena是一种堆的实现方式。每个arena由多个fixed-size的分配块(span)组成,而span又由多个连续的小块(object)组成。Arena的主要目的是提高内存分配的效率。

arena.go 中的 new 函数是用来分配新的arena的。该函数首先会从全局pool(p.allspans)中查找一个大小与请求匹配的可用的span(可能需要从堆中分配),然后将该span放入到arena的列表中,并返回可用空间的地址。

具体过程如下:

  1. 首先函数会判断请求的大小是否超过了maxTinySize (16 bytes)。如果超过该值,则会将请求分配到新的arena中。
  2. 接下来,函数会在全局pool(p.allspans)中查找是否存在大小与请求匹配的、分配状态为 _MSpanInUse的span。如果找到了,则将该span从全局pool中删除,并将该span加入到刚创建的arena的列表中,并返回可用空间的地址。
  3. 如果没有找到符合条件的span,则将该span的大小从相应的大小类中的span队列中获取,然后将该span放入局部mcache的 full列表中,并重新执行step 2。

总之, new 函数的主要作用是用来分配和管理arena中的span,以及协调全局pool中的span。

slice

在Go语言的runtime中,arena.go这个文件是实现了一种小对象内存分配器,它用于处理小内存对象的分配和回收。在这个文件中,slice是用来表示内存管理的小块内存的缓存区。

具体来说,slice在arena.go中的作用如下:

  1. 用于保存已分配但未被使用的小块内存,以便在需要时快速获取。

  2. 用于扩展内存管理器的批量分配大小。

  3. 用于在内存分配器的各个goroutine之间共享缓存的内存块。

  4. 提供一些辅助函数,比如用于移动内存块、合并相邻的空闲块等。

总的来说,slice在arena.go中是一个非常重要的数据结构,用于支持高效的小对象内存管理。通过对内存块的缓存和复用,可以大大降低内存分配和回收的开销,进而提高程序的性能。

free

在Golang中,Arena是一种内存分配的方式,它将内存分配在固定大小的连续块(chunks)中。Arena.go文件中的free函数用于将已经分配的chunk所占用的内存释放回heap,以便可再次分配到其他chunk。

具体来说,当goroutine不再需要一个chunk时,它会调用free函数。该函数将该chunk添加到一个可用chunk的链表中,并调用mheap对象的release函数,告诉内存管理模块可以将该chunk返回heap中。

释放chunk的流程可以总结为以下步骤:

  1. 通过chunkheader获取chunk的大小和next指针
  2. 将该chunk添加到可用chunk的链表中
  3. 调用mheap.release函数通知内存管理模块可以将该chunk返回heap中

以上就是arena.go文件中free函数的作用。它是Golang内存管理的重要组成部分,通过释放已分配的chunk,可以避免内存泄漏,提高内存利用率,从而更有效地利用系统资源。

alloc

alloc函数是Go语言运行时系统中的一个重要函数,它的主要作用是为某个特定的size大小的对象分配内存,并返回分配的内存地址。

在runtime/arena.go文件中,alloc是一个内部函数,它用于从arena中分配连续的一段内存。arena是一种特殊的内存池,它由一组连续的内存块组成,每个块大小相同。

在alloc函数中,它会遍历所有的内存块,查找第一个可以容纳size大小的内存块,并将其标记为已使用。同时,它还会记录已经使用的内存大小,以便后续再次使用时进行快速分配。如果没有找到可用的内存块,则会从系统中申请一个新的内存块,并加入到arena中。

从整体上来看,alloc函数的作用是为Go语言程序提供一种快速分配内存的能力,避免了程序频繁调用系统malloc函数的开销,提高了程序的性能。同时,由于arena是一种线程本地内存池,它可以缓解不同线程同时竞争内存分配的问题,避免了锁的开销。

总之,alloc是Go语言运行时系统中的一个关键函数,它实现了内存的快速分配,提高了程序的性能,并且解决了多线程竞争内存的问题。

refill

arena.go文件中的refill函数用于重新填充一个锁定的mcache,以便在需要时分配新的对象。具体来说,refill函数负责从堆中获取一定数量的对象(称为span),然后将这些span划分为对象块并添加到mcache中。一旦mcache中的对象用尽,就会调用refill函数来填充新的对象。

在refill函数中,首先会检查mcache是否为空。如果不为空,则返回,因为我们已经有足够的对象可以分配。否则,我们需要从heap中获取新的对象。refill函数首先会获取一些heap arenas,然后尝试将它们分配给mcache。如果分配成功,则将新的对象添加到mcache中。如果没有足够的heap arenas可用,则会调用grow函数来扩展heap。

总结一下,refill函数的作用就是确保mcache中始终有足够的对象可用于分配。它从heap中获取新的span,然后将它们分配到mcache中。如果没有足够的heap arenas可用,则会调用grow函数来扩展heap。

userArenaNextFree

userArenaNextFree是runtime中arena.go文件中的一个变量,它的作用是跟踪某个特定heap arena空闲空间的位置。arena是用来存储heap对象的连续内存块,一个arena被划分为多个fixed-size的span,每个span都可以用来存放某种大小的对象。userArenaNextFree指向当前arena上还未被使用的第一个空间的地址。

在初始化一个heap arena时,该变量被初始化为arena起始地址加上一个固定的偏移量,这个偏移量用于保留某个metadata(元数据)信息。之后每当有对象需要分配空间时,runtime将会检查当前arena是否有足够的空间,如果没有,当前goroutine将会尝试去获取一个新的空闲arena;如果当前arena有足够的空间,就会将需要分配的对象空间从userArenaNextFree地址开始分配,并更新userArenaNextFree变量为下一个空闲空间的地址。

需要注意的是,userArenaNextFree只能被所属arena的所属的goroutine来修改,这避免了不同goroutine之间的竞争条件。同时,由于每个arena中的span大小是一个固定的值,因此可以通过简单的数学运算快速地计算出下一个可用的span地址(也就是userArenaNextFree更新后的值)。

userArenaHeapBitsSetType

userArenaHeapBitsSetType是一个未导出的结构体类型,定义在go/src/runtime/arena.go中。它的作用是表示arena(堆区)中的位图,记录了arena中每个字节是否被分配或占用的状态。

具体来说,userArenaHeapBitsSetType包含两个字段:data和n。其中,data是一个uint64类型的切片,用于存储arena中的位图数据;n表示arena的大小,单位是字节,也就是说,位图中有n*8个二进制位,每个二进制位表示arena中对应的一个字节的状态。

用户线程可以通过arenaBitsSetType类型提供的以下几个方法来管理位图:

  • func (b *userArenaHeapBitsSetType) init(n uintptr, ptrSize uintptr):初始化一个长度为n的位图,ptrSize表示指针的大小,在位于arena底部的GC元数据对象中使用。
  • func (b userArenaHeapBitsSetType) isMarked(offset uintptr) bool:返回位图中偏移为offset8处的二进制位是否被标记为已分配或已占用。
  • func (b userArenaHeapBitsSetType) mark(offset uintptr):将位图中偏移为offset8处的二进制位标记为已分配或已占用。
  • func (b userArenaHeapBitsSetType) clear(offset uintptr):将位图中偏移为offset8处的二进制位标记为未分配或未占用。

这些方法在管理arena中内存分配时非常重要,可以帮助用户线程在操作heap时更加高效、准确。

userArenaHeapBitsSetSliceType

userArenaHeapBitsSetSliceType是一个slice类型,用于在arena.go文件中表示堆空间的位图标志信息。堆空间可以被分为一系列固定大小的chunk,每个chunk有一个对应的位图标志表示该chunk是否正在被使用。用户程序可以通过设置和查询这些位图标志来管理堆空间的分配和释放。

在userArenaHeapBitsSetSliceType中,每个元素表示一个chunk的位图标志信息,使用一个64位的整数来存储。具体来说,元素的第i位表示chunk中第i个bit的状态。如果位图标志被设置为1表示该bit已经被分配,如果设置为0表示该bit为空闲。

这个slice类型的作用是让用户程序可以方便地访问和管理堆空间的位图标志信息,尤其是在并发环境下。由于chunk是固定大小的,用户程序可以直接通过计算偏移量来获取对应的位图标志信息。而通过使用userArenaHeapBitsSetSliceType,用户程序可以直接通过索引来访问和修改位图标志信息,而无需手动计算偏移量,提高了代码的可读性和可维护性。

newUserArenaChunk

newUserArenaChunk是在Go语言运行时系统的runtime包中的arena.go文件中定义的一个函数。该函数的作用是为堆分配器分配新的内存块,从而将内存可用空间扩展到新的内存区域。

在Go语言程序运行时,需要为运行时系统分配可用的内存。为此,Go语言运行时将堆分为多个段,每个段都有一个基础地址和一组字节数。每个段又被分成多个块。在堆中,内存块的大小从几个字节到几个千字节不等。

newUserArenaChunk函数的主要作用是管理这些堆空间。每次调用该函数都会为堆分配器创建一个新的内存块,为新的对象分配内存。

在堆内存管理过程中,如果当前堆的内存使用率(即已使用的内存块与总内存块的比率)不足一定比例,就需要调用newUserArenaChunk来增加可用的内存。在新的内存块中,堆分配器可以为新分配的对象提供底层内存。这样,程序就可以在新的内存块中创建更多的对象。

总的来说,newUserArenaChunk函数是Go语言运行时系统中的一部分,是用于管理堆内存的工具之一。它的作用是为堆分配器提供新的内存块,从而管理程序的内存使用情况,确保Go语言程序可以高效地运行。

isUnusedUserArenaChunk

在Go语言的运行时系统中,堆空间管理使用了一种叫做Arena的技术。Arena是一个内存区域,其中包含了多个chunk,每个chunk有固定大小(一般为8KB),分别用于存储不同大小的内存块。当程序需要分配内存时,系统会在对应的chunk中寻找可用内存块,如果找不到,则会分配新的chunk进行扩展。

isUnusedUserArenaChunk函数是arena.go文件中的一个函数,其作用是检查指定的chunk是否可以释放。一个chunk是否可以释放的条件是:

  1. 这个chunk是属于用户的(不是系统保留的)。
  2. 这个chunk没有分配过任何内存块,也就是说,整个chunk都是空闲的。

具体的实现过程如下:

  1. 首先,通过chunkIndex函数,计算出chunk在arena中的索引。
  2. 判断chunk是否为系统保留的chunk,如果是,则返回false。
  3. 判断chunk是否已经分配过内存,如果是,则返回false。
  4. 如果以上两个条件都不满足,则返回true,表示这个chunk可以被释放。

在Go语言的运行时系统中,arena是由多个chunk组成,每个chunk都是固定大小的。isUnusedUserArenaChunk函数用于判断指定的chunk是否可以被释放,以便在堆空间中腾出更多的空间。

setUserArenaChunkToFault

setUserArenaChunkToFault是Go语言中runtime的一个函数,它的作用是将指定的用户内存区域设置为不可用。

在Go语言中,arena是一种用于分配小内存块的机制,通常采用mmap或VirtualAlloc等系统调用在运行时根据需要动态地将物理内存映射到虚拟内存中的一组连续段,这些段被称为Chunk。而setUserArenaChunkToFault函数的作用就是将某个chunk标记为不可用,这意味着对应的内存区域不能再被使用了。

具体来说,这个函数会将chunk对应的虚拟内存区域的映射关系移除,同时将这个区域的保护位设置为无法访问(PAGE_NOACCESS或PROT_NONE)。这么做的目的是防止程序误访问已经被释放的内存,从而避免了内存泄漏和使用已经释放的内存的潜在风险。

在使用setUserArenaChunkToFault函数时,必须保证这个chunk内已经没有分配出去的小块,否则会导致访问这些小块时发生内存访问异常。因此,在调用这个函数之前,需要首先对这个chunk进行空闲块的回收,确保其中没有正在被使用的内存块。

inUserArenaChunk

在Go语言运行时中,arena.go文件中的inUserArenaChunk函数的作用是检查给定的内存块是否位于当前goroutine的用户内存池中。

用户内存池是一个用于分配小块内存的自定义内存池。这是为了避免由于多次小内存分配而导致的内存碎片化,从而提高内存使用效率。在goroutine启动时,默认创建一个用户内存池。每个用户内存池是由多个arena chunk构成的,每个arena chunk都是一个固定大小的内存块,它们在需要时动态增长。当用户分配内存时,程序会从此内存池中获取内存。

当inUserArenaChunk函数被调用时,它会检查给定的内存块是否位于当前goroutine的用户内存池中。如果是,则返回true,否则返回false。这个函数通常用于跟踪内存使用情况,帮助诊断内存相关问题。

freeUserArenaChunk

freeUserArenaChunk函数是Go语言中运行时系统中的一个函数,它的作用是释放由用户请求的arena chunk占用的内存。arena chunk是一种在Go语言中用于分配堆上内存的数据结构。

在Go语言的运行时系统中,arena chunk是一种包含从堆上分配的内存的数据结构。这些数据结构被存储在arena中,arena是一种可以重新使用的内存池。如果一个arena chunk不再被使用,它可以通过freeUserArenaChunk函数释放,这样它所占用的内存就可以被重新分配给其他的arena chunk。

当用户请求一个arena chunk时,运行时系统就会在arena中查找可用的chunk,然后将其中一部分内存分配给用户,将另一部分留给后续需要分配内存的应用程序。一旦这个arena chunk被释放,运行时系统会将其中的所有内存返回给arena,使用freeUserArenaChunk函数实现。

在实现过程中,freeUserArenaChunk函数会将arena chunk放回arena中,以便其他应用程序可以使用它。具体来说,它会将arena chunk的状态设置为可用状态,并将其与相邻的空闲arena chunk合并,以便更大的连续内存块可以被重复使用。

总的来说,freeUserArenaChunk函数是Go语言运行时系统中的一个重要函数,它可以回收由用户请求的arena chunk占用的内存,以便这些内存可以被重新利用。

allocUserArenaChunk

allocUserArenaChunk函数定义于go/src/runtime/arena.go文件中,用于在操作系统中分配新的内存块,并为全局内存池中的用户线程分配一块指定大小的内存区域。该函数是 runtime 包中一些内存分配算法的基础。

在Go的运行时系统中,内存是按照“页面”(Page)来管理的。每个页面的大小通常是 4KB,一块内存由多个页面组成。Go内存管理器对于任意的用户线程,都会维护一个page分配器,在这个page分配器中,Go内存管理器会为每个线程分配内存区域。在应用程序中,平均每个线程会经常申请和释放这些内存块。

allocUserArenaChunk函数的具体作用是为每个用户线程(goroutines)分配新的用户内存池。每个用户内存池是固定大小的,并由多个大小相同的内存块组成。相邻的内存块之间是紧密相连的,它们的大小都是4KB的页(在POSIX系统中)或8KB(在Windows系统中)。一个新的内存块可以在不与其它用户内存池重叠的情况下添加到现有的用户内存池中。这些内存块的分配不但是线程专用的,还和物理内存的页面分配相关联,这使得内存分配更高效。

当一个用户线程首次访问它的用户内存池时,allocUserArenaChunk函数会被调用,以便为这个线程分配一个新的用户内存块。在函数执行的过程中,系统会为该线程分配一块虚拟内存区域,然后将这块虚拟内存区域与系统中的物理内存页面相对应。此后,Go程序可以在该线程的用户内存块中进行内存分配和管理操作了。


Structs:

userArena

在Go语言的runtime源代码中,arena.go文件定义了userArena类型,主要用于管理堆(heap)中的内存块。具体来说,它是一个结构体,包含了以下字段:

  • ptr:指向分配的内存块的指针;
  • end:指向分配的内存块的结束位置的指针;
  • next:指向下一个可用的userArena的指针;
  • freeindex:自由内存块的索引数组的下一个可用存储位置的索引。

其中,ptr和end指针用于跟踪堆中当前arena内存块的使用情况。next指针用于存储下一个可用的userArena实例,以便在需要时快速进行分配。freeindex索引数组用于存储内存块的索引以及如何恢复空闲列表的索引。

在Go语言中,线程的堆空间是由一些称为arena的内存块组成的。每个线程都有一个heapArena的结构体,用于管理和操作堆内存。当需要分配内存时,runtime会先搜索当前arena数据块是否还有剩余空间,如果没有,则会从heapArena中的自由列表(freeindex)中分配一个新的arena数据块,并将其与当前arena数据块连接在一起,然后再进行分配。当一个arena数据块被使用完毕时,它将被添加到heapArena的自由列表中,以便稍后进行重复利用。

因此,userArena的作用是管理heapArena中的内存块,以便在需要时为创建堆空间的线程提供已分配的内存块。它允许Go运行时系统管理大量的内存操作,提高了内存操作的效率和性能,并减少了内存泄漏的可能性。

liveUserArenaChunk

liveUserArenaChunk 函数在 runtime 包中的 arena.go 文件中,是用来找到当前正在使用的 arena 中最后一个被分配的 chunk 块的函数。

arena 是 Go 语言中内存分配器的一个概念,其作用是管理内存分配情况,为堆上分配内存提供支持。arena 是由若干个大小相等的 chunk 组成的,每个 chunk 包含大小相等的字节块,并且每个字节块大小都是固定的。

通过调用 liveUserArenaChunk 函数可以获取到最后一个被分配的 chunk 块,该函数会遍历 arena 中的所有 chunk,找到最后一个被分配的 chunk,并返回该 chunk 的地址。

具体实现过程如下:

  • 遍历所有的 arena,对于每个 arena:
  • 计算当前 arena 中的 chunk 数量
  • 如果当前 arena 中的 chunk 数量为 0,那么继续遍历下一个 arena
  • 如果当前 arena 中存在 chunk,那么找到该 arena 中最后一个被分配的 chunk,并返回该 chunk 的地址

这样,我们可以通过 liveUserArenaChunk 函数获取到当前正在使用中的最后一个 chunk,以方便我们进行内存分配管理和监控。

本文由 mdnice 多平台发布

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值