关于后端:Go源码解析之mgcgo

0次阅读

共计 21238 个字符,预计需要花费 54 分钟才能阅读完成。

File: mgc.go

mgc.go 是 Go 语言 runtime 包中的一部分,次要负责 Go 语言的垃圾回收机制 (Garbage Collector) 的实现。

Go 的垃圾回收机制采纳了标记 – 革除 (mark and sweep) 算法,其过程次要包含以下几个步骤:

  1. 标记阶段(Marking Phase):从根对象开始,遍历所有对象标记流动对象,即那些在程序中依然可达的对象。这个过程应用了三色标记法(Three-color marking algorithm)来提高效率。
  2. 制作工作列表(Making Worklist):将待回收的对象放入工作列表中。这个过程采纳了 Write Barrier 策略来防止漏标流动对象,并同时缩小在标记阶段遍历整个堆的工夫。
  3. 革除阶段(Sweeping Phase):开释那些未被标记的对象的内存空间。此时程序能够重新分配这些内存空间给其余对象。
  4. 内存压缩(Memory Compaction):对于一些碎片化重大的内存区域进行整顿,以进步程序的内存应用效率。

mgc.go 文件中记录了垃圾回收器的状态,以及负责整个垃圾回收过程中的过程治理,包含在垃圾收集中调配堆内存,革除不再应用的对象等等。它还蕴含了一些内存治理相干的函数,例如:gc() 函数、bgsweep() 函数等等。这些函数负责执行垃圾回收算法的各个步骤,确保 Go 程序在垃圾回收时的性能和稳定性。


Var:

gcphase

gcphase 是一个示意垃圾回收器所处阶段的变量。在 Go 语言的垃圾回收过程中,gcphase 用于判断垃圾回收器的具体状态,以确定是否能够执行某些操作或运行特定阶段的垃圾回收。

这个变量有以下几种状态:

  1. gcNone:没有进行垃圾回收。
  2. gcMark:进行标记阶段。
  3. gcMarkTermination:标记阶段完结,筹备进入打扫阶段。
  4. gcSweep:执行打扫阶段。
  5. gcSweepWait:打扫阶段完结,期待 goroutine 的完结。
  6. gcFinalize:执行最终打扫和某些其余操作。

gcphase 的作用是用于同步垃圾回收器的不同阶段,确保垃圾回收器在不同阶段之间的正确转换。同时,它也能够用于查看垃圾回收器是否曾经实现,以便其余代码能够平安地应用不再须要清理的内存。

writeBarrier

在 Go 语言中,当进行垃圾回收时,一个重要的操作是标记和清理不再应用的内存。标记操作能够通过遍历程序中的对象进行标记,而清理操作能够将被标记为不再应用的对象回收。在进行标记操作时,须要确保对象在被标记后不会再被批改,以防止标记的误判。

在任何时刻,如果程序中的 Go 程序对指针进行了赋值、传参、成员拜访或数组拜访操作等,就可能会扭转一个对象的地址或者创立新对象,这种状况下就可能呈现标记误判。为了防止这种误判,Go 语言引入了 write barrier 机制,也称为写阻碍。

write barrier 机制会在程序对指针进行批改时,触发一个拦截器,在记录批改前后指针所指向的对象,并标记其为“灰色”。

在 Go 语言中,write barrier 实现是在 mgc.go 文件中的 writeBarrier 变量中,这个变量是一个函数类型的值,它会被插入到 Go 语言的运行时零碎中的指针赋值和传递操作中。每次进行这些操作时,writeBarrier 函数就会被调用,用于实现标记和跟踪指针操作并记录它们的变动。

简而言之,writeBarrier 变量的作用是实现 Go 语言中的写阻碍机制,保障垃圾收集器程序在标记时不会误判。

gcBlackenEnabled

在 Go 语言中,gcBlackenEnabled 变量是垃圾回收器的一个开关变量,它的值会影响到 GC 的执行行为。具体来说,当 gcBlackenEnabled 为 true 时,GC 会默认执行彩色染色算法来标记内存对象的状态;当 gcBlackenEnabled 为 false 时,则会禁用彩色染色算法,改用灰色染色算法来标记内存对象状态。这个算法是用于标记内存对象是否曾经被扫描,从而防止对同一内存对象反复扫描的。

gcBlackenEnabled 变量的初始值为 true,如果用户程序想要敞开彩色染色算法,能够在程序的运行时环境中通过环境变量 GOGC=off 来禁用。禁用彩色染色算法会带来一些运行时的开销,但能够防止因为内存对象的繁多而导致的 GC 暂停过长,从而进步零碎的性能。因而,在理论开发中,咱们须要依据具体情况,来衡量开启或者敞开 gcBlackenEnabled 变量。

gcMarkWorkerModeStrings

gcMarkWorkerModeStrings 是用于形容垃圾回收器标记阶段的工作模式的变量。垃圾回收器的标记阶段是指在程序运行过程中辨认出哪些内存块能够被回收的阶段,是垃圾回收器中的重要组成部分。

gcMarkWorkerModeStrings 变量的作用是提供一种可读性更高的形式来设置和形容垃圾回收器标记阶段的工作模式。它容许用户通过设置不同的字符串来扭转垃圾回收器的默认行为,并以一种更加敌对的形式来理解垃圾回收器的工作模式。

具体来说,gcMarkWorkerModeStrings 变量蕴含了垃圾回收器标记阶段的三种不同工作模式,别离是:

  1. “local”:示意应用本地缓存来存储标记的对象,缩小线程间同步操作的次数,从而进步标记效率。
  2. “central”:示意应用地方缓存来存储标记的对象,能够放慢标记速度,但须要进行更多的线程间同步操作。
  3. “global”:示意应用全局缓存来存储标记的对象,在大型程序中能够更好地利用系统资源,但可能会对程序的运行速度造成肯定影响。

通过设置 gcMarkWorkerModeStrings 变量,用户能够更粗疏地管制垃圾回收器标记阶段的工作模式,从而优化程序的性能和内存应用。

work

在 Go 语言的 runtime 包中,mgc.go 文件是与垃圾回收(GC)相干的代码文件之一。其中,work 变量是一个全局变量,次要用于治理和跟踪 GC 的工作状态。

work 变量包含了以下字段:

  • wbuf:用于调配工作缓冲区的 slice;
  • wbuf1:工作缓冲区的备用 slice;
  • wbuf2:另外一个工作缓冲区的 slice;
  • ptr:以后正在应用的工作缓冲区指针;
  • n:以后调配的工作缓冲区大小;
  • barrier:用于爱护工作缓冲区的锁(mutex)。

在 Go 语言的垃圾回收算法中,工作缓冲区是用于存储指向可回收对象的指针的,用于标记这些对象以进行回收。work 变量的作用是确保 GC 的每个阶段都解决了所有指向可回收对象的指针,避免出现漏标(对象没有被标记进行回收)或误标(无需回收的对象被标记)的状况。

工作缓冲区的大小和调配形式是动静调整的,以充分利用系统资源和防止内存溢出。work 变量的字段 n 和 ptr 是用于管理工作缓冲区大小和调配的指针。同时,备用缓冲区 wbuf1 和 wbuf2 也保障了当 GC 须要在缓冲区溢出时,总可能疾速地进行切换和调配可用的缓冲区。

总之,work 变量是 Go 语言垃圾回收算法中至关重要的一个全局变量,它的作用是治理和跟踪 GC 的工作状态,确保 GC 可能精确地标记和回收内存中的可回收对象,防止内存透露和程序解体。

gcMarkDoneFlushed

gcMarkDoneFlushed 是在 Go 语言的垃圾回收机制中标记已实现的标记变量,它次要用于标记曾经实现的垃圾回收扫描操作,并告知零碎能够继续执行下一个阶段。

具体来说,gcMarkDoneFlushed 变量的作用体现在以下两个方面:

1. 标记扫描完结:
在 gc 扫描过程中,垃圾回收器须要扫描依据以后 goroutine 的堆栈、动态变量和全局变量等信息,并跟踪对象的援用关系来决定哪些对象是存活的,哪些是垃圾对象。当扫描完结时,gcMarkDoneFlushed 变量会被设置为 true,示意以后阶段的扫描曾经实现。

2. 触发垃圾回收:
同时,当曾经实现了垃圾回收扫描操作时,零碎会查看 gcMarkDoneFlushed 变量的状态。如果 gcMarkDoneFlushed 为 true,阐明以后阶段的依据援用关系扫描曾经实现,垃圾对象曾经被标记。这时候垃圾回收机制会启动垃圾清理过程对标记的垃圾对象进行回收,开释内存空间。反之,如果 gcMarkDoneFlushed 为 false,阐明垃圾回收扫描操作尚未实现,垃圾回收机制会持续扫描直到所有对象全副扫描实现。

综上所述,gcMarkDoneFlushed 变量是垃圾回收机制的一个标记,用于标记垃圾回收扫描阶段已实现,并且能够触发垃圾清理操作,从而实现无效的垃圾回收和内存治理。

poolcleanup

变量名:poolcleanup

作用:这个变量用于批示是否应该在每次垃圾收集后清理池。垃圾收集是指在 Go 程序运行期间由 go runtime 进行的主动内存治理过程。

具体介绍:

在 Go 中,当咱们须要应用大量的长期对象时,应用池是一种无效的形式来缩小内存调配和垃圾回收的开销。池是带有同步机制的对象池,它使咱们能够重用先前调配的对象以进步性能。

go runtime 中的 poolcleanup 变量管制池的清理工夫。如果 poolcleanup 的值为 true,则在每次垃圾收集后会清理池。这意味着池中的对象会在 gc 期间不会被解决,因而须要在垃圾收集后进行清理以回收资源。

如果 poolcleanup 的值为 false,则在每次垃圾收集中不会清理池。这能够进步性能,因为不须要破费额定的工夫来清理池,但这也可能会导致池中的对象在过多工夫内没有被回收,从而占用更多的内存。

在理论编程中,咱们能够依据具体的场景来抉择是否须要开启 poolcleanup。如果池中的对象的生命周期很短,可能没有必要在每次垃圾收集后清理池。然而,如果池中的对象的生命周期很长,例如在长时间运行的服务器程序中,倡议在垃圾收集后清理池,以防止内存透露和过多的内存占用。

boringCaches

变量 boringCaches 是 runtime 中的一个缓存构造。它次要用于减速垃圾回收器中的一部分操作。

在垃圾回收器中,须要频繁从堆上调配和回收内存。通过 boringCaches,能够实现对局部对象的复用,从而缩小调配和回收的次数,提高效率。

boringCaches 的具体实现是一个数组,数组中的每个元素都是一个指向 C 空间的指针。在垃圾回收器的操作中,能够将一些对象寄存到 boringCaches 中,而后在须要应用时间接从 boringCaches 中获取对应的对象,而无需从新分配内存。当对象不再须要时,能够将其放回 boringCaches 中以便复用。

boringCaches 在不同的垃圾回收器中有不同的用处。在 Go 1.5 及之前的版本中,boringCaches 次要用于保留已调配的对象以供下一次应用。而在 Go 1.6 及之后的版本中,boringCaches 也能够用于保留垃圾回收器中的外部数据结构,从而进步运行时的性能。

总之,boringCaches 通过复用一些对象来缩小调配和回收的次数,从而进步垃圾回收器的效率。它是垃圾回收器中的一个重要优化措施,也是 runtime 中重要的一个缓存构造。


Structs:

gcMarkWorkerMode

gcMarkWorkerMode 是 Go 语言中垃圾回收器中的一种工作模式,用于在并发标记阶段中协调多个标记器的工作。

具体来说,当 Go 语言的垃圾回收器须要回收内存时,它会启动一个并发的标记阶段,即后面提到的阶段二。在这个阶段中,垃圾回收器会应用多个 goroutine 并发扫描程序中正在应用的堆中的对象,标记沉闷对象和垃圾对象。在多核 CPU 上,这个并发扫描的过程更加高效。

而 gcMarkWorkerMode 构造体则是用来管制并发标记过程中的多个标记器的工作,它的次要作用有以下几个:

  1. 保护标记队列。在并发标记过程中,标记器会应用 work.markfor 扫描堆中的对象,标记沉闷的对象并放入标记队列中。gcMarkWorkerMode 构造体中的 markfor 属性会存储这个标记队列的指针,用来协调多个标记器之间的工作,防止抵触和反复工作。
  2. 管制标记器的运行状态。gcMarkWorkerMode 构造体中的 stop 和 shutdown 等属性能够管制标记器的运行状态,依据须要中途进行标记器的扫描工作。
  3. 提供线程平安的接口。gcMarkWorkerMode 构造体中的 pushgrey 和 commitgrey 等办法能够提供多个标记器之间的线程平安的接口,防止抵触和数据竞争。

总之,gcMarkWorkerMode 构造体是 Go 语言中垃圾回收器中实现并发标记的重要组成部分,它可能协调多个标记器之间的工作,进步垃圾回收的效率。

workType

workType 构造体是垃圾收集器的外围数据结构之一,次要用于保留和管理工作线程(G),用于协调和执行垃圾收集的工作。该构造体定义如下:

type workType struct {
    victim      uintptr           // 下一个被扫描对象的地址
    nobj        uintptr           // 以后扫描对象的数量
    obj         uintptr           // 以后正在扫描的对象地址
    bytesMarked uint64            // 以后线程刚实现标记的 bytes 数量
    scanp       uintptr           // 以后指向的对象或者下一个对象的地址
    gcw         *gcWork           // 当前工作线程的 gcWork 构造指针
    scanner     *scanState        // 扫描过程中的状态信息
    nest        int32             // 嵌套的 for 循环层数(用于调试)// ...(其余字段省略)}

其中,次要有以下几个字段:

  • victim:示意下一个要被扫描的对象的地址。在 Concurrent Mark 阶段,扫描线程会依据 victim 指针一一扫描 heap 中的对象。
  • obj:示意以后正在被扫描的对象的地址。如果 workType 构造体中其余字段没有被设置,则 obj 指向 victim 所指向的对象,否则 obj 指向其余对象。
  • bytesMarked:示意以后线程刚实现标记的字节数。在 Concurrent Mark 阶段,各工作线程在扫描 heap 时会逐步标记各对象是否是存活对象,bytesMarked 则记录曾经标记的存活对象占用的内存大小,留神它只记录以后线程标记的对象大小,因而须要用 atomic.AddUint64 原子加操作统计所有线程标记的内存大小。
  • gcw:示意当前工作线程的 gcWork 指针,用于追踪该工作线程的扫描进度。
  • scanner:示意扫描过程的状态信息,包含扫描模式(是 Concurrent 还是 Fallback 模式)、标记过程中遇到的全局对象(用于发现重要存活对象,如 Goroutine、栈等),以及用于增量式标记的跟踪状态(包含指针上一个标记的层数、扫描时上次扫描到的地址、是否正在扫描根集等)。

总的来说,workType 构造体在垃圾收集器中施展着至关重要的作用,它是维持工作线程和垃圾收集器协同工作的关键所在。通过该构造体,垃圾收集器能够跟踪工作线程的扫描进度、协调各工作线程的扫描工作、发现全局存活对象等,从而实现对整个堆中存活对象的精确标记和回收。

gcMode

gcMode 构造体定义了运行时的垃圾回收模式以及一些相干参数,用于管制垃圾回收的行为。具体来说,gcMode 构造体蕴含了以下字段:

  • kind:示意垃圾回收的模式,有“off”、“m”、“ms”、“mw”四种取值。别离代表敞开垃圾回收、标记 - 清理模式、标记 - 整顿模式、标记 - 整顿模式(针对只有一个内存堆的状况)。
  • triggerRatio:示意堆使用率超过多少时触发垃圾回收。默认为 0.5。
  • heapMinimum:示意堆最小大小。默认为 8192KB。
  • heapMaximum:示意堆最大大小。默认为 0,即不限度。
  • stackGuardMultiplier:示意协程的栈空间大小比例,默认为 0.875。

通过调整 gcMode 构造体的字段值,能够灵便地管制垃圾回收的触发和行为,进而影响程序的性能和稳定性。例如,能够通过批改 triggerRatio 和 heapMaximum 来调整垃圾回收的频率和堆大小,从而优化程序的内存应用和垃圾回收的效率。

gcTrigger

在 Go 语言中,gcTrigger 构造体用于管制 GC 触发的条件。在 Go 运行时,GC 须要在某些状况下触发,以回收不再应用的内存。gcTrigger 构造体中定义了一些用于管制 GC 触发条件的属性和办法。

gcTrigger 构造体蕴含以下属性:

  1. memstats:用于存储内存统计信息的构造体。
  2. gcSystemStartTime:系统启动工夫。
  3. lastGCPauseEndTime:上一次 GC 暂停完结工夫。
  4. triggerRatio:可被 GC 回收的堆内存占总内存的比例阈值。
  5. triggerBytes:可被 GC 回收的堆内存大小的阈值。
  6. lastTriggerRatio:上一次垃圾回收时可被回收的堆内存占总内存的比例。
  7. lastTriggerBytes:上一次垃圾回收时可被 GC 回收的堆内存大小。

gcTrigger 构造体蕴含以下办法:

  1. setTriggerRatio(ratio float64):设置 gcTrigger 的 triggerRatio 属性。
  2. setTriggerBytes(bytes uint64):设置 gcTrigger 的 triggerBytes 属性。
  3. trigger():判断是否须要触发 GC。当可被 GC 回收的堆内存大小超过 triggerBytes 或可被回收的堆内存占总内存的比例超过 triggerRatio 时,触发 GC。

总之,gcTrigger 构造体中的这些属性和办法用于管制 GC 的触发条件,以确保 GC 可能在适当的工夫回收内存,进步 Go 程序的性能和稳定性。

gcTriggerKind

在 Go 语言中,GC(垃圾回收)被用来主动治理内存。有时,垃圾回收程序会在应用程序正在运行时忽然触发,这可能会导致不必要的性能开销。

为了防止这种状况,Go 语言在 runtime/mgc.go 中定义了一个名为 gcTriggerKind 的构造体,用于形容垃圾回收触发的类型。

gcTriggerKind 构造体的作用是形容垃圾回收的类型,包含它是由内存调配还是使用量触发的、它是否由手动触发、以及它的严格水平等等。

具体来说,gcTriggerKind 构造体蕴含以下属性:

  • kind:触发垃圾回收的类型。包含以下四种类型:gcTriggerCycle、gcTriggerFraction、gcTriggerHeap、gcTriggerManual。
  • gcTriggerCycle:以后 GC 在固定距离(gcController 的 cycle 工夫)之后触发。也就是说,触发 GC 是依据肯定的工夫距离来触发的。
  • gcTriggerFraction:当堆大小减少了指定的分数(gcController 的 heapMinimum 和 heapGoal 参数)时,将触发 GC。依据该参数,GC 触发的频率将会随着堆的增长而减速,这样能够依据堆的应用状况来触发 GC。
  • gcTriggerHeap:当堆大小达到指定的值(gcController 的 heapSizeGoal 参数)时,将触发 GC。
  • gcTriggerManual:这个是人工触发的 GC。
  • gcTriggerScale:触发 GC 的严格性级别。级别越高,GC 触发的频率也越高,然而程序的性能也会更差。共有四个级别,从 0 到 3。
  • gcTriggerDisable:禁用垃圾回收

通过 gcTriggerKind 构造体,咱们能够灵便地管制垃圾回收的触发类型和频率,从而均衡应用程序的性能和内存应用。

gcBgMarkWorkerNode

在 go/src/runtime/mgc.go 文件中,gcBgMarkWorkerNode 构造体是用于示意与 BGMarkWorker 相干的状态的一种类型。它的定义如下:

// A gcBgMarkWorkerNode is a node in the concurrent work queue of
// the background mark worker. Thus it is an element of the doubly-
// linked list defined by the gcBgMarkWorkerState.
//
// One gcBgMarkWorkerNode is used to represent both the state of being
// enqueued for work and the state of being processed but not yet
// enqueued for the next phase of the mark worker.
//
// Depending on the state of the gcBgMarkWorkerState, it is mutable
// from one of potentially many concurrent GC workers or the mark worker
// itself; however, each transition MUST be made under the appropriate
// lock.
//
// Rather than label each and every field below as mutable or not,
// it is crucial to keep in mind the following invariant:
//
// The fields are only modified when the state is actively enqueued 
// or being dequeued. This doesn't overlap with the currently active 
// gcBgMarkWorkerState because each phase of the mark worker has a 
// private immutable snapshot of the work queue that is shuffled 
// independently of any other phase. If two phases are executing 
// concurrently (which would happen during "on-the-fly" single-P 
// marking or D phase), then they split the work hand over fist 
// rather than interleaving.
type gcBgMarkWorkerNode struct {
    // A node struct must start with a gcWork.
    work gcWork

    // Popped off the current mark queue.
    next *gcBgMarkWorkerNode
    prev *gcBgMarkWorkerNode

    // live == live objects; dead == garbage.
    leaf bool
    live uintptr
    dead uintptr
    target  *gcBgMarkWorkerState
}

在并发垃圾回收器中,每个后盾标记 worker 都保护着一个并发工作队列(concurrent work queue)。gcBgMarkWorkerNode 构造体就是这个队列中的元素。它有以下几个成员:

  • work:示意要被解决的 GC Work(例如存活对象的标记工作,死对象的清理工作)。
  • next / prev:示意下一个和前一个 gcBgMarkWorkerNode 元素。
  • leaf:示意该元素解决的是可达的存活对象(即“绿子树节点”),还是无法访问的垃圾对象(即“灰子树节点”)。
  • live / dead:别离示意其解决的存活对象和垃圾对象的数量。
  • target:示意这个元素所在的泛滥 GC work 队列的指向。

gcBgMarkWorkerNode 的次要作用是:

  • 代表了一个须要后盾标记 worker 解决的 GC Work。
  • 作为后盾标记 worker 的并发工作队列中的一个元素。
  • 记录了已标记的存活对象和已确定为垃圾的对象的数量。

Functions:

gcinit

gcinit 函数是 Go 语言垃圾回收机制的初始化函数,它会在程序启动时被调用。gcinit 会负责以下几个工作:

  1. 设置全局变量 gcphase 的初始值为_gcoff。gcphase 用来记录以后垃圾回收的阶段,具体取值能够是_gcoff、_gcmark、_gcmarktermination、_gcSweep、_gcSweepDone 等,不同阶段对应不同的垃圾回收操作。
  2. 初始化工作线程。在 Go 语言的垃圾回收过程中,须要应用多个工作线程来并行标记和清理内存。gcinit 会创立一组工作线程,并将它们初始化,筹备好执行垃圾回收工作。
  3. 设置全局变量 gcController。gcController 用来管制垃圾回收的流程,包含阶段转换和线程调度等,gcinit 会初始化 gcController 并将其设置为正在进行的垃圾回收的控制器。
  4. 初始化 P 的本地缓存。在 Go 语言的垃圾回收过程中,每个工作线程都会保护一个本地缓存(Local Cache),用来保留一些长期变量和对象。gcinit 会将所有工作线程的本地缓存初始化,并将它们和全局变量 gcController 进行关联。这样,每个工作线程都能够将本地缓存中的内容同步到全局缓存中,保障垃圾回收的正确执行。

总的来说,gcinit 函数的作用是初始化垃圾回收机制的各个组件,为垃圾回收做好筹备工作,保障其正确、高效地执行。

gcenable

在 Go 语言中,垃圾回收是由运行时零碎负责的。gcenable 是运行时零碎中的一个函数,其作用是启用垃圾回收。

具体来说,gcenable 函数会将标记位从 _GCoff 批改为 _GCmark,并触发一次强制执行的垃圾回收操作。此外,gcenable 函数还会查看其余相干参数,如 gcpercent(触发垃圾回收的最低堆使用量)和 maxprocs(最大并发执行的过程数),以确保垃圾回收零碎失常运行。

在启用垃圾回收之前,应用程序可能须要实现一些初始化工作。例如,创立一些 goroutine、分配内存等等。一旦这些初始化实现,应用程序能够调用 gcenable 函数,以启用垃圾回收零碎。

总之,gcenable 函数是 Go 运行时零碎中的一个要害性能,它使得垃圾回收能够失常工作,确保了 Go 代码的稳定性和可靠性。

setGCPhase

setGCPhase 函数是用于设置以后的垃圾回收阶段的函数。垃圾回收阶段包含 STW (Stop-The-World)、Mark、MarkTermination、Sweep、SweepTermination 等。该函数如果被调用时未处于 STW 阶段,会抛出谬误。

具体来说,当程序须要进行垃圾回收时,Go runtime 会依照垃圾回收阶段的程序进行不同的操作。setGCPhase 函数就是用来在不同阶段中切换的函数,例如在 Mark 阶段时调用 setGCPhase 函数来切换到 MarkTermination 阶段。另外,在切换不同阶段时须要进行一些波及到全局状态的操作,例如更新 P 的状态、清空缓存等,常常会调用一些相干的函数,以便在不同阶段中进行必要的全局操作。

总之,setGCPhase 函数次要作用是在垃圾回收过程中切换不同的阶段,并进行一些波及到全局状态的操作。它是 Go runtime 中垃圾回收局部的要害函数之一。

pollFractionalWorkerExit

pollFractionalWorkerExit 函数是用于查看并退出 Goroutine 的函数。在并发垃圾回收(Concurrent Garbage Collection,简称 CGC)中,有专门的 Goroutine 用于帮助 GC 实现其中的一些工作,这些 Goroutine 称为 helper goroutine,它们是在应用程序失常执行的状况下运行的。然而,因为这些 helper goroutine 不会自行退出,它们可能会占用应用程序中的一些资源并升高性能。pollFractionalWorkerExit 函数的作用就是用于查看这些 helper goroutine 是否有退出的机会,如果能够退出,就会强制让它们退出。

在函数实现中,pollFractionalWorkerExit 会获取 helper goroutine 的一份正本,通过查看正本的状态来判断是否有退出的机会。如果 helper goroutine 的正本状态处于 waitexit 状态(这意味着 helper goroutine 曾经筹备好退出了),则将其从 goroutine 队列中移除,并设置 helper goroutine 的状态为 exited。如果 helper goroutine 的状态不是 waitexit,则仅仅是将其正本状态设置为 pollidle 状态,以便后续轮询。

pollFractionalWorkerExit 函数个别是由 pollFractionalWorker 函数调用的,用于轮询并退出 helper goroutine。通过这种形式,能够保障 helper goroutine 不会无限度地占用资源,从而放弃应用程序的性能稳固。

GC

GC 是 Golang 的垃圾回收机制,它的作用是在程序运行的过程中对内存进行自动化治理,保障程序可能主动回收不再应用的内存,避免内存泄露和程序解体。

在 Go 语言的 GC 实现中,mgc.go 文件中的 GC 函数是其中最为外围的局部,它负责对程序的内存进行回收。GC 函数会应用可达性剖析算法来查看哪些内存是可达的,哪些是不可达的,对于不可达的内存会主动回收。

GC 函数的流程大抵如下:

  1. 通过遍历 G、P、M 等数据结构获取所有的根对象,根对象是程序中所有被援用的对象的入口点。
  2. 对所有根对象进行遍历,标记所有可达的对象为流动对象,不可达的对象则是垃圾对象。
  3. 对于所有不可达的对象,调用对象的 finalizer,让其执行垃圾回收前的清理工作。
  4. 对于所有不可达且没有 finalizer 的对象,间接将其回收。
  5. 通过垃圾回收后,将回收的内存重新分配给闲暇列表,以便下一次的内存调配应用。

须要留神的是,在执行 GC 函数时,程序须要进行所有的 goroutine 并暂停程序的失常运行,期待 GC 实现后再恢复程序的执行,这个过程称为“Stop The World”(暂停世界)。

GC 函数会在程序的一些关键点主动触发,例如当堆大小达到肯定阈值、或当程序调用了 runtime.GC()函数时,GC 函数会对内存进行主动回收。

gcWaitOnMark

gcWaitOnMark 是 runtime 中的一个函数,次要目标是期待标记阶段的实现。

在 Go 语言的垃圾回收中,标记阶段是 GC 的一部分。在这个阶段中,GC 会标记所有已调配对象中依然在应用的对象。标记结束后,GC 就会晓得哪些对象能够被回收,哪些对象依然在应用中。然而,期待标记阶段的实现可能会升高应用程序的性能,因而,gcWaitOnMark 函数的作用就是进步并行性,放慢标记阶段的实现。

具体来讲,gcWaitOnMark 函数的工作是阻塞以后 goroutine,期待标记阶段实现。如果标记阶段曾经实现,那么函数立刻返回。否则,函数会调用 gcController、gcBgMarkWorker 和 gcMarkDone 等函数来进行协调和期待。当标记阶段实现后,函数会返回并继续执行后续操作。

总之,gcWaitOnMark 函数的作用就是期待 GC 标记阶段的实现,并最大限度地进步并行性,放慢标记阶段的实现。

test

在 go/src/runtime/mgc.go 文件中,test func 次要是用于:

  1. 验证小对象内存调配是否失常
  2. 确保可达性剖析过程不会呈现死循环,保障垃圾回收不会影响应用程序的失常运行

具体来说,test func 会创立一些小对象(allocs)并且在子协程里调配和开释它们,测试零碎是否能够失常扩大堆空间。如果调配和开释失常实现,则阐明内存调配零碎失常工作。在调配和开释这些对象时,堆大小也会动静调整以适应需要。

测试还会模仿垃圾回收过程,即执行强制垃圾回收,而后查看回收后的堆大小是否正确。如果大小正确,则阐明垃圾回收器失常工作,并且不会对应用程序的失常运行产生影响。

测试还会验证标记过程是否失常实现,即在调配和开释这些小对象后,会标记所有可达对象,以确保可达性剖析过程不会进入死循环状态。

总之,test func 是用于验证垃圾回收器失常工作,并确保不会对应用程序的失常运行产生影响的重要函数。

gcStart

gcStart 是 Go 语言运行时底层的垃圾回收器(GC)开始执行的函数。它次要有以下几个作用:

  1. 初始化 GC 相干数据结构:gcStart 会初始化标记位图、堆栈缓存、roots 汇合等数据结构。这些数据结构是 GC 执行过程中必要的。
  2. 标记根对象:GC 开始前须要明确哪些对象是可达的根对象,即哪些对象是从内部援用的或者是全局变量。gcStart 会标记这些根对象以确保它们不会被回收。
  3. 调用标记阶段的回调函数:GC 执行过程中有一些回调函数会被调用,比方记录对象信息的回调、解决 finalizer 的回调等。gcStart 会调用标记阶段的回调函数。
  4. 标记残缺个堆:通过后面的筹备工作后,gcStart 会开始执行标记阶段,遍历整个堆,将可达对象标记为流动对象,未被标记的对象为垃圾对象。
  5. 执行清理阶段:当标记阶段完结后,gcStart 会执行清理阶段,将所有未被标记的对象(即垃圾对象)回收。清理过程包含与其它 goroutine 合作实现的事宜,例如中断工夫的清理,将被开释的内存归还给操作系统等。

总体来说,gcStart 是 Go 语言运行时 GC 的引擎,它通过在标记阶段标记流动对象,清理阶段回收垃圾对象,并在整个垃圾回收过程中调用相干回调函数,保障了 Go 语言程序的内存平安和高效应用。

gcMarkDone

在 Go 语言中,gcMarkDone 函数是用于标记已实现的标记阶段的。在垃圾回收过程中,标记阶段的工作是从根对象登程,标记所有可达的对象。标记实现后,就能够进行垃圾回收。gcMarkDone 函数的作用是将已实现的标记阶段状态设置为 done,以便进行下一阶段的垃圾回收。在 gcMarkDone 函数中,通过将相干的标记位设置为已实现来标记标记阶段已实现。在标记阶段实现后,咱们能够平安地清理任何未被应用的内存。同时,gcMarkDone 函数还会调用 gcSweep 函数来执行扫描操作,并将未被应用的内存开释回零碎。

总之,gcMarkDone 函数是用于标记实现垃圾回收中的标记阶段并清理未应用的内存。它是 Go 语言垃圾回收流程中十分重要的一个环节。

gcMarkTermination

gcMarkTermination 函数是垃圾回收器在并发标记阶段完结时调用的函数之一,它的作用是将一些未被扫描的对象标记为彩色,并把它们增加到彩色对象列表中。具体来说,gcMarkTermination 会做以下三件事件:

  1. 扫描 grayobject 列表,将其中仍未被扫描的对象标记为彩色。
  2. 扫描 graySpecial 列表,将其中的非凡对象(如 g 或 m)标记为彩色。
  3. 将一些未被扫描的堆对象(如跨度对象或大对象)标记为彩色,这些对象在扫描阶段可能被脱漏。

通过以上三个步骤,gcMarkTermination 函数能够确保所有的可达对象都被正确标记为彩色,以使垃圾回收器可能正确辨认哪些对象应该被保留,哪些对象应该被开释。

总之,gcMarkTermination 函数是垃圾回收器并发标记阶段的一个重要组成部分,它通过扫描未被正确标记的对象,最大水平地缩小垃圾回收的误判和漏判,从而更加高效地回收内存。

gcBgMarkStartWorkers

gcBgMarkStartWorkers 函数是 Go 语言规范库中 runtime 包中 mgc.go 文件中的一个函数,这个函数的次要作用是启动后盾标记的工作。在 go 程序中,当堆中对象数量达到肯定阈值时,须要进行垃圾回收,这个阈值就是 GC 触发器的阈值。在垃圾回收过程中,须要标记和回收不再应用的对象,这个过程须要占用 CPU 和内存资源,会影响应用程序的性能。

gcBgMarkStartWorkers 函数的作用就是通过启动后盾标记工人来升高 GC 对应用程序的影响。在这个函数外部,会查看以后是否有后盾标记工人在运行,如果没有,则依据以后 CPU 数量创立 n 个后盾标记工人,并启动它们,这样能够并发的运行标记过程,从而缩小 GC 对应用程序造成的瓶颈。这些后盾标记工人会在标记过程中协调采样器和全屏扫描工作,以及在标记实现后执行荡涤、压缩和其余后处理工作,最终实现垃圾回收的过程。

(gcBg)MarkStartWorkers 函数是 gcBackgroundMarkRunLoop 函数调用的子函数,而(gcBg)MarkStartWorkers 函数起到相似创立并启动 goroutine 的作用,即为 Mark State 申请和开释 P 以及创立和运行 G 的工作。在输出参数的状况下,返回一组操作 future(并且保障他们都进行,以及回到调用者)。对于未解决可能的谬误,(gcBg)MarkStartWorkers 解决了它们并且不会退出。从某种意义上说,这种办法施行了相似于实现操作系统中波及异样和爱护策略的形式。

gcBgMarkPrepare

gcBgMarkPrepare 是 Go 语言运行时中 mgc.go 文件中的一个函数,其作用为在后盾标记阶段开始前筹备标记。上面是该函数的具体解释:

  1. 首先,该函数会获取 gcController 状态(gc 背景标记控制器)并查看标记过程是否被勾销(stopped)。
  2. 接着,它会查看待处理的 goroutine 数量,如果没有,则更新 gcMarkWorkerMode 的状态为 markDone 并返回。
  3. 如果仍有待解决的 goroutine,它会首先查看以后 P(处理器)是否已筹备好应用,并且以后的 gcMarkWorkerMode 状态是否为“推送”(pullWait)。
  4. 如果是,则将 gcMarkWorkerMode 更新为“推送”(pulling),并返回将该 P 置于推卡片队列中的信号。这意味着其它 goroutine 将在 P 筹备好时推送工作。
  5. 如果以后 P 没有筹备好,则将 gcMarkWorkerMode 更新为“阻断”(blocking),并在进入阻塞之前从新查看是否进入状态 stopped。如果状态未更改,则以后 G(goroutine)将会进入休眠状态,直到某个 P 可用。
  6. 如果以后 gcMarkWorkerMode 状态为“推送”,则查看是否有期待推卡片的 goroutine。如果没有,则更新 gcMarkWorkerMode 为“推送实现”(markWait),返回并持续进行标记。
  7. 如果有期待推卡片的 goroutine,则将它们搁置在卡片推送队列的开端,并将 gcMarkWorkerMode 更新为“推送实现”(markWait),返回并持续进行标记。

总之,gcBgMarkPrepare 函数用于在标记阶段启动前筹备标记,依据以后状态(例如,筹备好的处理器数量和待处理的 goroutine 数量)推送或阻塞 goroutine 以优化标记过程。

gcBgMarkWorker

gcBgMarkWorker 函数是 Go 语言的垃圾回收器背景标记阶段的工作者函数,它的作用是协调和执行背景标记阶段的工作。

在 Go 语言中,垃圾回收分为两个阶段:标记阶段和清理阶段。在标记阶段中,GC 会标记哪些对象是可达的,而背景标记则是在程序继续执行的同时进行的。gcBgMarkWorker 函数就是在背景标记阶段中发挥作用的。

gcBgMarkWorker 函数的具体工作流程如下:

  1. 获取 P(processor):gcBgMarkWorker 函数会获取一个 P(processor),该 P 将用于执行 G(goroutine)中的工作。
  2. 获取工作:gcBgMarkWorker 函数会获取一个工作,该工作是从全局队列中获取的,用于标记已调配的对象。
  3. 执行工作:gcBgMarkWorker 函数会将获取到的任务分配给 P 执行。
  4. 解锁:执行完工作后,gcBgMarkWorker 函数会解锁其余工作,而后再次获取 P 和工作执行。
  5. 循环执行:gcBgMarkWorker 函数会始终循环执行 2 - 4 步,直到全局队列中没有待处理工作为止。

通过这些步骤,gcBgMarkWorker 函数能够协调和执行背景标记阶段的工作,并进行高效的垃圾回收。

gcMarkWorkAvailable

gcMarkWorkAvailable 函数是 Go 语言中的垃圾回收机制(GC)的外围函数,在 gcMarkWorkAvailable 函数中,会查看是否有更多的垃圾标记工作须要执行。如果须要执行更多的垃圾标记工作,则它会将 gcMarkWork.wakeup 中的 goroutine 唤醒。

这个函数的次要作用是确保标记阶段中的所有垃圾标记工作都可能失去充沛的解决,从而最大水平地缩小垃圾回收的工夫和频率。它在标记阶段中被屡次调用,以确保所有的垃圾标记工作都能失去解决。

在 Go 语言中,GC 的性能是十分重要的,因为它间接影响了代码的执行速度和响应工夫。gcMarkWorkAvailable 函数通过查看是否有更多的垃圾标记工作须要执行,让 Go 语言的垃圾回收机制更加强壮和高效。

gcMark

gcMark 函数是 Go 语言中 Garbage Collector 的其中一个外围函数,其次要作用是标记所有的存活对象。

在 Garbage Collection 算法中,标记阶段是十分重要的一环。简略来说,gcMark 函数的作用就是通过遍历对象图,来标识哪些对象是被程序援用的,还须要被保留下来,而哪些是垃圾,须要被回收。

gcMark 函数的执行流程如下:

  1. 初始化根对象汇合

该函数会在程序的全局变量、虚拟机栈、寄存器等计算机内存区域中找出所有的指针对象,并将其退出到根对象汇合中。

  1. 遍历对象图

接下来,该函数会从根对象汇合中取出一个对象,进而遍历相干的所有对象,标记它们为流动对象,即使标记它们的色彩为灰色。

  1. 标记灰色对象的相干对象

在上一步中,所有被标记为灰色的对象都是须要被细节扫描的。在这一步中,gcMark 函数会遍历这些灰色对象援用的所有对象,并标记它们为流动对象。

  1. 标记完结

gcMark 函数将完结标记过程,并将被标记为流动对象的对象从灰色变为彩色,此时标记阶段完结。

总之,gcMark 函数的作用是负责标记存活对象,使得垃圾回收器可能在后续的阶段中精确地回收无用对象,从而保障程序的内存平安和稳定性。

gcSweep

gcSweep 这个函数的作用是革除不再应用的对象,并将其返回给还未应用的空间池。gcSweep 函数实现了垃圾回收中的标记 - 革除阶段,在革除过程中,它扫描程序中的所有对象,并标记那些还在应用的对象,将不再应用的对象革除。

在革除阶段,gcSweep 函数会遍历所有的堆区,找到曾经死亡的对象,而后将它们的空间返回给闲暇内存池,以便可能为后续的对象调配更多的内存。gcSweep 函数会应用一个指针列表,该列表存储所有被调配的对象的指针,以便在革除时进行遍历。

在革除阶段的最初,gcSweep 函数会更新垃圾回收器的状态,并筹备进入下一个阶段。该函数还会更新堆的统计信息,在垃圾回收的过程中记录已解决的对象数量,并更新堆的大小等信息,以便在下一次垃圾回收时应用。

总之,gcSweep 函数是垃圾回收的要害之一,能够通过革除不再应用的对象,开释内存并进步程序的性能。

gcResetMarkState

gcResetMarkState 是在 Go 语言中垃圾回收器 (gc) 的运行过程中调用的一个函数,它的作用是重置垃圾回收器中与标记相干的状态,以便下一轮垃圾回收可能正确地进行。

在 Go 语言中的垃圾回收器执行过程中,须要分为两个阶段: 标记阶段和打扫阶段。在标记阶段中,会从根对象登程,遍历所有可能达到的对象,并将这些对象标记为流动对象。在这个过程中,为了避免对象被屡次标记或者不被标记的状况呈现,须要记录一些状态,并在实现标记后进行清理。

gcResetMarkState 函数就是负责重置这些状态的函数。具体来说,它会清理各种指针标记位,还会重置某些内存区域的状态,以避免垃圾回收器在下一轮回收时受到烦扰。

总之,gcResetMarkState 函数是垃圾回收器中要害的重置函数之一,它确保垃圾回收器在下一轮运行前的状态是正确的,这样能力精确地找到所有的垃圾对象进行回收。

sync_runtime_registerPoolCleanup

sync_runtime_registerPoolCleanup 是在 Go 语言中用于垃圾回收的一个函数,定义于 runtime/mgc.go 文件中。它的作用是向全局池(global pool)中注册一个清理函数(cleanup function),以便在每个垃圾回收周期完结后主动执行。

具体来说,垃圾回收器在回收过程中会创立一些长期对象,例如内存块和长期指针等。这些对象会存在于全局池中,期待下一轮垃圾回收周期完结后清理。如果不及时清理这些对象,将会导致内存透露和零碎性能降落。

为了解决这个问题,Go 语言提供了一个在垃圾回收周期完结后主动执行的全局清理函数机制。这个函数由 sync_runtime_registerPoolCleanup 注册,并在垃圾回收器的全局清理函数调度器中执行。它的作用是清理全局池中的长期对象,以便下一轮垃圾回收器可能从新应用它们。

总之,sync_runtime_registerPoolCleanup 函数的作用是向全局池中注册一个清理函数,用于主动清理垃圾回收中产生的长期对象,以进步零碎的性能和稳定性。

boring_registerCache

boring_registerCache 是 Go 语言的垃圾回收器(GC)中的一个函数,它的作用是注册一块新的缓存区域,以供垃圾回收器应用。

在 Go 语言中,当程序中的某个内存块不再被应用时,垃圾回收器会将这个内存块标记为垃圾,并将其回收。GC 会扫描程序的内存空间,辨认出那些曾经不再应用的内存块,而后将它们开释回操作系统。为了进行这个操作,GC 须要保护一些缓存区域,以记录哪些内存块能够被回收。

boring_registerCache 就是用来注册这些缓存区域的函数。当程序创立一个新的对象时,GC 会调用这个函数来为该对象调配一个缓存区域。当这个对象不再应用时,GC 会将它从缓存区域中删除,并将它标记为垃圾。

这个函数的名字中蕴含了“boring”一词,意思是它是垃圾回收器中的一个平庸的函数。因为大多数状况下,程序员很少须要间接调用这个函数,因为 Go 语言的 GC 会主动进行垃圾回收,而无需程序员手动干涉。

clearpools

clearpools 函数定义在 mgc.go 文件中,它的原型如下所示:

// clearpools flushes all cached memory in the runtime.
// This includes mcache and deferred mspan frees.
// It must be called without any other locks held.
// Otherwise it is free to block or allocate memory itself.
// This function is not safe for concurrent invocation.
func clearpools() {// ...}

该函数的作用是清空所有的内存池(memory pool),包含 mcache 和 deferred mspan 开释。内存池是在运行时(runtime)中由内存管理器(memory manager)调配和治理的一组预约义大小的内存块。这些内存块用于跟踪和调配堆对象和栈帧等运行时数据结构。内存池缩小了在分配内存时的零碎调用次数,从而进步了运行时的性能。

clearpools 函数必须在没有其余锁被持有的状况下调用,否则它可能阻塞或分配内存。此外,该函数不适用于并发调用。因而,它通常在垃圾回收期间被调用,以确保垃圾回收器可能正确拜访和治理所有内存池。它还能够在程序退出时调用,以开释所有未开释的内存池并确保程序退出时没有内存透露。

itoaDiv

itoaDiv 是一个用于将一个无符号整数转化成字符串的函数,它被用于扫描堆中对象,给对象调配编号,并将这些编号序列化成字符串。

itoaDiv 函数的作用如下:

  1. 将一个无符号整数 num 转换为字符串,应用的是基于 10 进制的转换形式。
  2. 通过除法、取模来实现转换,divVal 和 divMod 别离用来示意除数和模数。
  3. 从低位到高位顺次填充字符串,num 被 divVal 整除的后果一直被填充到字符串的开端,直至 num 被 divVal 整除的后果为 0。
  4. 序列化过程中默认应用的字符集是 ASCII 表中的数字字符。

这个函数次要用于在进行垃圾回收时,将对象的编号序列化成字符串,以便于对这些对象进行跟踪和调试,并在肯定水平上进步了垃圾回收的效率。

fmtNSAsMS

在 go/src/runtime 中,mgc.go 文件中的 fmtNSAsMS 函数的作用是将纳秒工夫格局的数字转换为人类可读的毫秒工夫格局的字符串,例如将 1000000 转换为 ”1ms”。

该函数接管一个示意纳秒工夫的 int64 类型参数,首先会依据这个数值来确定要转换成多少毫秒,而后再依据毫秒数的大小来抉择不同的单位(ms, us, ns)。最初,将转换成的值和单位拼接起来并返回一个字符串。函数的实现过程中还包含了数值的四舍五入解决,以便失去更准确的后果。

这个函数通常用于在 GC 过程中输入日志和统计信息,帮忙开发人员理解 GC 的执行状况和性能体现。

gcTestMoveStackOnNextCall

gcTestMoveStackOnNextCall 函数是一个用于测试的辅助函数,次要用于模仿在垃圾回收过程中,在函数调用时须要将以后堆栈挪动到新的堆栈上的状况。

具体来说,当垃圾回收器对堆进行垃圾回收时,会扫描堆栈中的指针以确定哪些对象在应用中,哪些对象能够被开释。然而,在进行垃圾回收的时候,会呈现堆栈须要挪动的状况。因为垃圾回收的过程中,会存在压缩堆或者调整堆的状况,如果此时持续向堆栈上分配内存,会影响当前的垃圾回收,所以须要将曾经调配的堆栈挪动到新的中央。

gcTestMoveStackOnNextCall 函数就是用于模仿这种场景的测试函数,当该函数调用时,会返回一个新的栈地址,而后将以后堆栈挪动到这个新的地址上,从而模仿在垃圾回收过程中堆栈须要挪动的状况。

能够看出,gcTestMoveStackOnNextCall 函数在运行时垃圾回收的时候起到了十分重要的作用,它能够帮忙开发人员测试代码在垃圾回收过程中的正确性,而不须要真的进行堆栈挪动操作,从而进步了开发效率和代码品质。

gcTestIsReachable

gcTestIsReachable 是用来测试一个对象是否能够被标记为可达状态的函数。

在垃圾回收器中,须要扫描整个堆来标记所有可达的对象。对于每个须要扫描的对象,gcTestIsReachable 函数会被调用来确定该对象是否是可达的。如果对象可达,它将保留在堆中,并持续被应用。否则,它将被垃圾回收器标记为不可达,并在下一次垃圾回收时被革除。

gcTestIsReachable 函数的实现形式取决于对象的类型。对于惯例对象,它将查看对象是否在某个根对象 (例如堆栈或全局变量) 中被援用。对于不同类型的对象,例如字符串、函数等,它有不同的实现形式。

总之,gcTestIsReachable 函数是垃圾回收器中的重要组成部分,用于确定哪些对象须要保留在堆中,哪些对象能够被垃圾回收器清理。

gcTestPointerClass

在 Go 语言中,垃圾回收是十分重要的一个性能,负责回收不再应用的内存,防止内存透露等问题。在 Go 语言中,垃圾回收是通过扫描内存中的指针来实现的,而 GC 中的指针分类就是依据指针的类型进行分类,以便于 GC 能够更好地辨认和回收内存。

在 Go 语言的 runtime/mgc.go 文件中,gcTestPointerClass 函数是用于测试一个指针所属的指针类别的函数。一个指针的指针类别决定了 GC 如何扫描它,以及垃圾回收期间是否可能须要复制它指向的对象。gcTestPointerClass 函数应用一个非凡的标记位来确定指针的类型,判断该指针是否是指向堆对象的指针,从而将该指针标记为无效的指针,防止垃圾回收误删除该指针援用的对象。此外,gcTestPointerClass 函数还通过查看指针所处的页的状态来确定该指针指向的对象是否在以后的堆页中,从而帮忙垃圾回收器回收该对象。

总之,gcTestPointerClass 函数在 Go 语言的垃圾回收中起到十分重要的作用,它帮忙 GC 辨认和治理指针,以确保内存的正确回收和利用。

本文由 mdnice 多平台公布

正文完
 0