Golang底层原理剖析
文章平均质量分 81
golang
cheems~
业精于勤,荒于嬉;行成于思,毁于随。
展开
-
Golang底层原理剖析专栏汇总
汇总目录Golang内置Log包的基本使用Golang日志库Zap基本使用Golang日志切割归档Golang关键字type的两种用法GolangFlag基本使用浅谈Golang并发控制WaitGroup浅谈Golang上下文Context浅谈Golang通道channel浅谈Golang线程安全的sync.Map浅谈Golang广播sync.Cond浅谈Golang对象池sync.pool浅谈Golang原子atomicGolang底层原理剖析之string类型与字符编码Gol原创 2021-11-17 01:05:23 · 2578 阅读 · 4 评论 -
Golang内置Log包的基本使用
Go内置Log包的基本使用使用Logger配置logger配置日志格式-Flags配置日志前缀-Prefix配置日志输出位置-Output使用标准logger自定义logger总结使用Loggerlog包定义了Logger类型,该类型提供了一些格式化输出的方法。本包也提供了一个预定义的“标准”logger,可以通过调用函数Print系列(Print|Printf|Println),Fatal系列(Fatal|Fatalf|Fatalln)和Panic系列(Panic|Panicf|Panicln)来使用原创 2021-10-28 00:08:07 · 2208 阅读 · 0 评论 -
Golang日志库Zap基本使用
ZapUber-go Zap介绍安装Zap LoggerLoggerSugared Logger配置LoggerNew函数详情JSON EncoderLog Encoder更改时间编码添加OptionUber-go Zap介绍Zap是非常快的、结构化的,分日志级别的Go日志库。go get -u go.uber.org/zapreadmezap git安装Zap LoggerZap提供了两种类型的日志记录器—Sugared Logger和Logger。在性能很好但不是很关键的上下文中,使原创 2021-10-28 17:06:38 · 4493 阅读 · 0 评论 -
Golang日志切割归档
Golang日志切割归档使用Lumberjack进行日志切割归档测试使用Lumberjack进行日志切割归档Zap本身不支持切割归档日志文件为了添加日志切割归档功能,我们将使用第三方库Lumberjack来实现 go get -u github.com/natefinch/lumberjack要在zap中加入Lumberjack支持,我们需要修改WriteSyncer代码。我们将按照下面的代码修改getLogWriter()函数func getLogWriter() zapcore.Write原创 2021-10-28 19:35:27 · 786 阅读 · 0 评论 -
Golang type的两种用法
自定义类型和类型别名自定义类型类型别名自定义类型在Go语言中有一些基本的数据类型,如string、整型、浮点型、布尔等数据类型,Go语言中可以使用type关键字来定义自定义类型。自定义类型是定义了一个全新的类型。我们可以基于内置的基本类型定义,也可以通过struct或者函数类型来定义。//将MyInt定义为int类型 type MyInt int通过Type关键字的定义,MyInt就是一种新的类型,它具有int的特性,但是他们不是同一种类型。package mainimport "原创 2021-10-28 20:12:41 · 805 阅读 · 0 评论 -
Golang Flag基本使用
Golang Flag基本使用os.Argsflag包参数类型定义命令行flag参数flag.Type()flag.TypeVar()flag.Parse()flag其他函数实例os.Args如果你只是简单的想要获取命令行参数,可以像下面的代码示例一样使用os.Args来获取命令行参数。func main() { if len(os.Args) > 0 { for index, arg := range os.Args { fmt.Printf("args[%d]=%v\n", in原创 2021-10-28 21:26:55 · 2012 阅读 · 0 评论 -
浅谈Golang并发控制WaitGroup
WaitGroupWaitGroup前言代码核心后记WaitGroup前言理解WaitGroup的实现 - == 核心是CAS ==的使用Add与Done应该放在哪? - Add放在Goroutine外,Done放在Goroutine中,逻辑复杂时建议用defer保证调用WaitGroup适合什么样的场景? - 并发的Goroutine执行的逻辑相同时,否则代码并不简洁,可以采用其它方式代码package mainimport ( "fmt" "sync")func wg()原创 2021-10-25 20:25:35 · 619 阅读 · 0 评论 -
浅谈Golang上下文Context
ContextContext前言代码Context前言Context上下文 - 结合Linux操作系统的CPU上下文切换/子进程与父进程进行理解如何优雅地使用context - 与select配合使用,管理协程的生命周期Context的底层实现是什么? - mutex与channel的结合,前者用于初始部分参数,后者用于通信代码package mainimport ( "context" "fmt" "time")// Tip: 通过 cancel 主动关闭func ct原创 2021-10-25 22:35:42 · 632 阅读 · 0 评论 -
Golang底层原理剖析之string类型与字符编码
浅谈字符与string结构Unicode简介Unicode 编码方案之UTF-8stringstring结构string注意点Unicode简介Unicode记录着世界上所有字符对应的一个数字,它仅仅只是一个字符集,规定了符合对应的二进制代码,至于这个二进制代码如何存储则没有任何规定。Unicode 没有规定字符对应的二进制码如何存储。以汉字“汉”为例,它的 Unicode 码点是 0x6c49,对应的二进制数是 110110001001001,二进制数有 15 位,这也就说明了它至少需要 2 个字节原创 2021-10-26 19:42:30 · 1212 阅读 · 0 评论 -
Golang底层原理剖析之slice类型与扩容机制
浅谈Golang slice类型与扩容机制slice类型扩容机制slice类型在runtime下的slice.go可以看到type slice struct { array unsafe.Pointer len int cap int}slice基础结构包括保存数据的array、长度len与容量cap,其底层是数组array这里的ints[3]和ints[4]不能访问,否则属于越界访问,会引发panic扩容机制在runtime下的slice.go内有一个扩容growslic原创 2021-10-26 20:34:37 · 989 阅读 · 0 评论 -
浅谈Golang通道channel
浅谈Golang channel前言代码底层实现两个有趣的例子前言channel用于Goroutine间通信时的注意点 - 合理设置channel的size大小 / 正确地关闭channel合理地运用channel的发送与接收 - 运用函数传入参数的定义,限制 <- chan 和 chan <-channel的底层实现 - 环形队列+发送、接收的waiter通知,结合goroutine的调度思考理解并运用channel的阻塞逻辑 - 理解channel的每一对 收与发 之间的逻辑,巧原创 2021-10-28 23:00:43 · 761 阅读 · 0 评论 -
浅谈Golang线程安全的sync.Map
sync.Map 前言前言sync.Map的核心实现 - 两个map,一个用于写,另一个用于读,这样的设计思想可以类比缓存与数据库sync.Map的局限性 - 如果写远高于读,dirty->readOnly 这个类似于 刷数据 的频率就比较高,不如直接用 mutex + map 的组合sync.Map的设计思想 - 保证高频读的无锁结构、空间换时间...原创 2021-10-28 23:48:38 · 547 阅读 · 0 评论 -
浅谈Golang广播sync.Cond
sync.Cond前言代码源码分析前言sync.Cond的核心实现 - 通过一个锁,封装了notify 通知的实现,包括了单个通知与广播这两种方式sync.Cond与channel的异同 - channel应用于一收一发的场景,sync.Cond应用于多收一发的场景代码package mainimport ( "fmt" "sync" "time")// 示例来自https://stackoverflow.com/questions/36857167/how-to-correc原创 2021-10-29 23:17:29 · 929 阅读 · 0 评论 -
浅谈Golang对象池sync.pool
sync.pool前言使用源码GetPut理解sync.poolvictim前言sync.Pool的核心作用 - 读源码,缓存稍后会频繁使用的对象+减轻GC压力sync.Pool的Put与Get - Put的顺序为local private-> local shared,Get的顺序为 local private -> local shared -> remote shared ->victim -> New思考sync.Pool应用的核心场景 - 高频使用且生命周期原创 2021-10-30 13:57:02 · 799 阅读 · 0 评论 -
浅谈Golang原子atomic
atomic前言代码前言atomic 适用的场景 - 简单、简单、简单!不要将atomic用在复杂的业务逻辑中atomic.Value 与 mutex - 学习用两者解决问题的思路了解 data race 机制 - atomic可以有效地减少数据竞争代码package mainimport ( "fmt" "sync" "sync/atomic")func atomicAdd() { var data, data2 int64 var wg sync.WaitGroup原创 2021-10-30 16:46:41 · 968 阅读 · 0 评论 -
Golang底层原理剖析之内存对齐
内存对齐为什么要内存对齐如何内存对齐结构体内存对齐为什么要内存对齐假设数据总线64位,每次读取连续的8个字节,所以每次读取的地址一定是8的倍数,如果非要错开一个地址,由于最后一个字节对应的位置与前7个不同,不能在一次读取中读完整,那么就需要分两次读,把两次结果拼接起来拿到所需数据,这必然会影响性能。如何内存对齐所以为保证程序顺利高效的运行,编译器会把各种类型的数据安排到合适的地址并占用合适的长度,这就是内存对齐,每种类型的对齐值就是它的对齐边界,内存对齐要求数据存储地址以及占用的字节数都要是它的对原创 2021-10-30 19:46:20 · 404 阅读 · 0 评论 -
Golang底层原理剖析之函数调用栈-栈帧布局与函数跳转
栈帧布局与函数跳转栈帧布局函数跳转栈帧布局我们按照编程语言的语法定义的函数,会被编译器编译为一堆机器指令,写入可执行文件,程序执行时,可执行文件被加载到内存,这些机器指令对应到虚拟地址空间中,位于代码段。如果在一个函数中调用另一个函数,编译器就会对应生成一条call指令,程序执行到这条指令时,就会跳转到被调用函数处开始执行,而每个函数的最后都有一条ret指令,负责在函数结束后跳回到调用处,继续执行。函数执行时需要有足够的内存空间,供函数存放局部变量,参数等数据,这段空间对应到虚拟地址空间的栈,栈是原创 2021-10-30 23:11:09 · 897 阅读 · 0 评论 -
Golang底层原理剖析之函数调用栈-传参和返回值
函数调用栈-传参和返回值defer与return时机传值的swap函数传指针的swap函数匿名返回值函数具名返回值函数调用多个函数的小问题defer与return时机return赋值和返回是两个步骤,不是原子操作,如果有defer会插在两个步骤中:返回值赋值(return value)defer语句 //可有可无返回值返回传值的swap函数我们通过函数调用栈看看问题到底出在哪假设main函数栈帧在这里,先分配局部变量locals,这里函数调用没有返回值,所以局部后面就是给被调用函数传原创 2021-10-31 16:59:03 · 825 阅读 · 0 评论 -
Golang底层原理剖析之闭包
闭包function value闭包定义捕获列表被捕获的变量没有被修改的情况被捕获的变量被修改的情况有修改并被捕获的是参数被捕获的是返回值总结function valuego中函数是头等对象,可以作为参数传递,可以作为返回值,也可以绑定到变量。go中称这样的参数,返回值或者变量为function value。函数指令在编译期间生成,而function value本质是上一个指针,但是并不直接指向函数的指令入口。而是指向一个runtime.funcval结构体,这个结构体里只有一个地址,就是这个函数原创 2021-11-01 17:11:03 · 2049 阅读 · 0 评论 -
Golang底层原理剖析之defer
defer层原理剖析Go1.12Go1.13Go1.14Go1.12关于defer,我们知道它会在函数返回之前倒叙执行,像这样的代码,编译后的伪指令是这样的defer指令对应到两部分内容,deferproc负责把要执行的函数保存起来,我们称之为defer注册,deferproc函数会返回0,和panic recover有关,先忽略,对应要跳转的也先忽略。defer注册完成后程序会继续执行后面的逻辑,直到返回之前通过deferreturn执行注册的defer函数。正是因为先注册后调用,才实现了def原创 2021-11-02 00:10:45 · 865 阅读 · 10 评论 -
浅谈Golang 不同版本的defer
deferGo1.12前言步骤解析deferprocdeferreturn存在的问题Go1.13runtime.deferprocStackruntime.deferreturnGo1.14问题Go1.12关于defer底层刨析请看专栏这一篇博文Golang defer底层原理剖析前言defer指令对应到两部分内容deferproc负责把要执行的函数保存起来,我们称之为defer注册。返回之前通过deferreturn执行注册的defer函数。注册defer会创建一个_defer结构体原创 2021-11-02 18:54:16 · 456 阅读 · 0 评论 -
Golang底层原理剖析之panic与recover
panic与recoverpanicrecover总结panic我们已经知道当前执行的goroutine中有一个defer链表的头指针,其实它也有一个panic链表头指针。panic链表连起来的,是一个一个_panic结构体,和defer链表一样,发送新的panic时,也是在链表头上插入新的_panic结构体。所以链表头上的panic,就是当前正在执行的那一个。来看个例子,这里函数A注册了两个defer函数A1和A2后发生panic, 执行到panic后,panic后面的代码就不会执行了,而是进入原创 2021-11-03 16:14:20 · 989 阅读 · 0 评论 -
Golang底层原理剖析之map
哈希表名词理解hmapbmap扩容规则翻倍扩容等量扩容名词理解存储的键值对的数目与桶的数目的比值陈为负载因子。如果哈希表存储的键值对较多,一次性迁移所有桶花费的时间就比较久,所以通常在哈希表扩容时,先分配足够多的新桶,然后用一个字段记录旧桶的位置,一个字段记录旧桶迁移的进度,在哈希表每次读写操作时,如果检测到当前处于扩容阶段,就完成一部分键值对迁移任务,直接所有旧桶迁移完成,旧桶不再使用,才算真正完成一次哈希表的扩容,像这样把键值对迁移的时间分摊到多次哈希表操作中的方式,就是渐进式扩容,可以避免一次性原创 2021-11-03 22:11:19 · 1442 阅读 · 0 评论 -
浅谈Golang map使用与陷阱
mapmap介绍使用示例map常见使用过程中的问题问题示例map介绍map 读取某个值时 - 返回结果可以为 value,bool 或者 value。注意后者,在key不存在时,会返回value对应类型的默认值map 的 range 方法需要注意 - key,value 或者 key。注意后者,可以和slice的使用结合起来map 的底层相关的实现 - 串联 初始化、赋值、扩容、读取、删除 这五个常见实现的背后知识点,详细参考示例代码链接与源码使用示例package mainimport原创 2021-11-04 00:43:45 · 977 阅读 · 0 评论 -
Golang底层原理剖析之method
method方法方法表达式 & 方法变量方法如果我们定义一个类型A,并给它关联一个方法,然后就可以通过这个类型A的变量来调用这个方法了,这种调用方式其实是“语法糖”实际上和下面这种方式是一样的。这里变量a就是所谓的方法接收者,它会作为方法Name的第一个参数传入。这一点我们可以通过右边的代码验证一下,Go中函数类型只和参数与返回值相关,所以这两个类型值相等就能够证明方法本质上就是普通的函数。而接收者就是隐含的第一个参数。下面来看看方法调用的情况,man函数栈帧局部如图,传参值拷贝,函数内a原创 2021-11-04 21:41:46 · 348 阅读 · 0 评论 -
Golang底层原理剖析之类型系统,接口与类型断言
interface前言类型系统认识接口空接口非空接口类型断言空接口.(具体类型)非空接口.(具体类型)空接口.(非空接口)非空接口.(非空接口)前言如果我们自定义一个结构体类型T,并给它关联一个方法F1,这个方法调用之前也介绍过,这个方法本质上就是函数,只不过在调用时接收者会作为第一个参数传入。这在编译阶段自然行得通。但是到了执行阶段,反射,接口动态派发,类型断言,这些语言特性或机制,又该如何动态的获取数据类型信息呢?类型系统在Go语言中,左边这些属于内置类型,而我们通过自己定义的类型,属于原创 2021-11-05 00:24:50 · 1283 阅读 · 2 评论 -
浅谈Golang接口interface
interface前言interface源码前言关于接口的底层原理剖析点击Golang底层原理剖析之类型系统,接口与类型断言interfaceinterface的两种类型 - 数据结构的interface,侧重于类型;面向对象中接口定义的interface,侧重于方法的声明了解interface的底层定义 - eface和iface,都分为两个部分:类型与数据iface底层对类型匹配进行了优化 - map+mutex组合源码package mainimport ( "fmt")原创 2021-11-05 00:57:02 · 493 阅读 · 0 评论 -
Golang底层原理剖析之反射reflect
反射前言reflect.TypeOfreflect.ValueOf前言反射的作用,就是把类型元数据暴露给用户使用,其实在了解了类型系统和接口以后,反射所做的事情就没什么神奇的了。我们已经介绍过runtime包中,类型元数据以及空接口和非空接口的结构了,但是这些类型都是为导出的,所以reflect包中又定义了一套,这些类型定义在两个包中是保持一致的。reflect.TypeOfreflect包提供TypeOf函数,用于获取一个变量的类型信息。它接收一个空接口类型的参数,并返回一个reflect原创 2021-11-05 18:20:15 · 1174 阅读 · 0 评论 -
浅谈OS虚拟内存
虚拟内存虚拟内存虚拟内存当一个可执行文件被加载到内存中执行时,就成为了一个运行的程序,也就是一个“进程”在DOS时期采用的是“实地址”模式,进程直接使用物理地址,但是这种模式下,进程可以任意修改物理内存,很容易发生占用其他进程的内存的情况,甚至可能会覆盖操作系统使用的内存。 所以出现了“保护模式”,进程不直接使用物理内存地址,而是使用虚拟的内存地址,这些地址被称为线性地址,操作系统负责把虚拟地址映射到物理内存怎么映射?首先要知道“保护模式”提供内存分页机制,比如在32位系统下物理内存中每4K原创 2021-11-05 22:35:33 · 612 阅读 · 0 评论 -
浅谈OS进程和线程
虚拟内存用户空间和内核空间线程系统调用线程切换和进程切换用户空间和内核空间虽然每个进程都有自己的虚拟地址空间,但是为了进一步保障系统运行安全,虚拟地址空间被划分为用户空间和内核空间。操作系统运行在内核空间,用户程序运行在用户空间。内核空间由所有进程的地址空间共享,但是用户程序不能直接访问内核空间。操作系统保存的进程控制块PCB自然是在内核空间,这里除了页目录以外还可以找到很多重要的内容,例如进程和父进程ID,状态,打开文件句柄表等等。线程线程就是进程中的执行体,它要有指定的执行入口,通常是某原创 2021-11-06 02:29:04 · 497 阅读 · 0 评论 -
Golang底层原理剖析之上下文Context
Context前言Context前言如何优雅地使用context点击浅谈Golang上下文ContextContext在Go语言并发编程中,用一个goroutine来处理一个任务 ,而它又会创建多个goroutine来负责不同子任务的创建非常常见,这些场景中往往会需要在API边界之间以及过程之间,传递截止时间,取消信号,或其他与请求相关的数据,这时候就可以使用Context,context包在Go1.7的时候被加入到官方库中。主要内容可以概括为一个接口,四种具体实现,还有六个函数。Cont原创 2021-11-06 18:23:26 · 976 阅读 · 0 评论 -
协程和IO多路复用
协程和IO多路复用协程IO多路复用协程我们已经知道线程是进程中的执行体,拥有一个执行入口,以及从进程虚拟地址空间中分配的栈(用户栈和内核栈),操作系统会记录线程控制信息,而线程获得CPU时间片以后才可以执行,CPU这里栈指针,指令指针,栈基等寄存器都要切换到对应的线程。如果线程自己又创建了几个执行体(携程),给它们各自指定执行入口,申请一些内存分给它们用作执行栈,那么线程就可以按需调度这几个执行体了。为了实现这些执行体的切换,线程也需要记录它们的控制信息。包括ID,执行栈的位置,执行入口地址,原创 2021-11-06 20:59:11 · 1269 阅读 · 0 评论 -
GolangGMP模型 GMP(一):HelloWorld程序的执行过程
@TOC前言TODO概念理解G:goroutine 协程P:processor 处理器M:thread 内核线程一个HelloWorld程序,编译后成为一个可执行文件,执行时,可执行文件被加载到内存。对于进程虚拟地址空间中的代码段,我们感兴趣的是程序执行入口,它并不是我们熟悉的main.main,不同平台下程序执行入口不同。在进行一系列检查与初始化等准备工作后,会以runtime.main为执行入口,创建main goroutine。main goroutine执行起来以后,才会调用我们原创 2021-11-07 23:46:05 · 1638 阅读 · 0 评论 -
GolangGMP模型 GMP(二):goroutine的创建,运行与恢复
这里写目录标题前言理解前言理解还是这个hello goroutine的例子,只不过给协程入口增加了一个参数,我们已经知道main goroutine执行起来会创建一个hello goroutine,而创建的任务,就交由newproc函数来负责。我们通过函数栈帧看一下newproc函数的调用过程,main函数栈帧自然分配在main goroutine的协程栈中 ,还记得go语言函数栈帧布局吧,call指令入栈的返回地址之后 ,是调用者栈基,然后是局部变量区间,以及调用其他函数时,传递是返回值和参原创 2021-11-08 20:43:01 · 1314 阅读 · 0 评论 -
GolangGMP模型 GMP(三):协程让出,抢占,监控与调度
GMP三前言理解前言TODO理解我们已经知道,协程执行time.Sleep时,状态会从_Grunning变为_Gwaiting ,并进入到对应timer中等待,而timer中持有一个回调函数,在指定时间到达后调用这个回调函数,把等在这里的协程恢复到_Grunnable状态,并放回到runq中。那谁负责在定时器时间到达时,触发定时器注册的回调函数呢?其实每个P都持有一个最小堆,存储在P.timers中,用于管理自己的timer,堆顶timer就是接下来要触发的那一个。而每次调度时,都会调用c原创 2021-11-08 22:49:37 · 2057 阅读 · 0 评论 -
深入理解GMP模型
深入理解GMP模型前言源码剖析数据段上重要的全局变量newproc创建协程Gruntime.gopark挂起Gruntime.goready唤醒Gsysmon监控线程schedult调度图解调度Goroutine调度器的GMP模型的设计思想(1)GMP模型简介(2)调度器的设计策略(3) “go func()”经历了什么过程(4)调度器的生命周期Go调度器GMP调度场景的全过程分析场景1:G1创建G2场景2:G1执行完毕场景3,4,5:G2开辟过多的G => G2的P本地队列满了再创建G7 =>原创 2021-11-09 21:13:15 · 5634 阅读 · 0 评论 -
Golang底层原理剖析之垃圾回收GC
GC前置知识概念了解Go V1.3-标记清除法步骤缺点Go V1.5-三色标记法步骤图示去除STW的三色标记法的图示去除STW的三色标记法产生的问题Go V1.8-三色标记+混合写屏障“强-弱” 三色不变式强三色不变式弱三色不变式两种屏障方式插入写屏障介绍图示删除写屏障介绍图示两种屏障的缺点混合写屏障步骤混合写屏障的场景分析对象被堆对象删除引用,被栈对象引用对象被栈对象删除引用,被另一个栈对象引用对象被堆对象删除引用,被另一个堆对象引用对象被栈对象删除引用,被堆对象引用总结前置知识概念了解Root s原创 2021-11-10 23:38:42 · 1058 阅读 · 0 评论 -
浅谈Golang T和*T方法集的关系
T和*T的方法集四种调用场景接口传值,调用值接收者接口传指针,调用指针接收者接口传指针,调用值接收者(success)接口传值,调用指针接收者(error)原因剖析总结四种调用场景接口传值,调用值接收者package maintype Iface interface { hello()}type Stu struct {}func (c Stu) hello() { panic("implement me")}func main(){ var IfTs Iface var s原创 2021-11-14 13:39:02 · 1287 阅读 · 0 评论 -
Golang底层原理剖析之互斥锁sync.Mutex
互斥锁sync.MutexMutex概括Mutex.state状态标识Mutex源码剖析Mutex概括Mutex(Mutual exclusion),Go中Mutex的数据结构是这样的,因为足够简单,所以不需要额外的初始化,零值就是一个有效的互斥锁,处于Unlocked状态。state存储的是互斥锁的状态,加锁和解锁,都是通过atomic包提供的函数原子性,操作该字段。sema用作一个信号量,主要用于等待队列。Mutex有两种模式,在正常模式下,一个尝试加锁的goroutine会先自旋四次,自旋锁原创 2021-11-14 23:35:48 · 2772 阅读 · 1 评论 -
浅谈Golang互斥锁sync.Mutex
sync.Mutex概述Mutex的数据结构Mutex.state的状态正常模式饥饿模式源码解析Lock正常模式饥饿模式Unlock概述Mutex的数据结构Go中Mutex的数据结构是这样的,因为足够简单,所以不需要额外的初始化,零值就是一个有效的互斥锁,处于Unlocked状态。state存储的是互斥锁的状态,加锁和解锁,都是通过atomic包提供的函数原子性,操作该字段。sema用作一个信号量,主要用于等待队列。type Mutex struct { state int32 sema ui原创 2021-11-16 00:17:16 · 1461 阅读 · 0 评论