rune=int32
byte=uint8
* / % << >> & &^
+ - | ^
== != < <= > >=
&&
||
符号 | 功能 | 例 |
---|---|---|
%[1]o | 使用第一个操作数 | fmt.Printf("%d %[1]o %#[1]o\n", o) |
%#o | 用%o、%x或%X输出时生成0、0x或0X前缀 用Go语言语法打印值 |
fmt.Printf("%d %[1]x %#[1]x %#[1]X\n", x) |
%q | 打印带单引号字符,无损打印 | |
%c | 打印字符 | |
% x | 在每个十六进制数字前加入一个空格 | |
%t | 打印布尔类型 | |
%T | 打印数据类型 |
整数转字符串Itoa(Int to ASCII)
FormatInt(int, base)
FormatUint(int, base)
Atoi(s)
ParseInt(s, base, bit)
容量 | 扩容大小 | 扩容比例 |
---|---|---|
2 | 1 | 1 |
4 | 2 | 1 |
8 | 4 | 1 |
16 | 8 | 1 |
32 | 16 | 1 |
64 | 32 | 1 |
128 | 64 | 1 |
256 | 128 | 1 |
512 | 256 | 1 |
1024 | 512 | 1 |
1280 | 256 | 0.25 |
1696 | 416 | 0.325 |
2304 | 608 | 0.35849056 |
3072 | 768 | 0.33333334 |
4096 | 1024 | 0.33333334 |
5120 | 1024 | 0.25 |
7168 | 2048 | 0.4 |
9216 | 2048 | 0.2857143 |
12288 | 3072 | 0.33333334 |
15360 | 3072 | 0.25 |
19456 | 4096 | 0.26666668 |
24576 | 5120 | 0.2631579 |
30720 | 6144 | 0.25 |
38912 | 8192 | 0.26666668 |
49152 | 10240 | 0.2631579 |
61440 | 12288 | 0.25 |
76800 | 15360 | 0.25 |
96256 | 19456 | 0.25333333 |
120832 | 24576 | 0.25531915 |
151552 | 30720 | 0.2542373 |
189440 | 37888 | 0.25 |
237568 | 48128 | 0.25405404 |
296960 | 59392 | 0.25 |
371712 | 74752 | 0.25172412 |
464896 | 93184 | 0.2506887 |
581632 | 116736 | 0.25110132 |
727040 | 145408 | 0.25 |
909312 | 182272 | 0.25070423 |
1136640 | 227328 | 0.25 |
1421312 | 284672 | 0.25045046 |
1776640 | 355328 | 0.25 |
2221056 | 444416 | 0.2501441 |
2777088 | 556032 | 0.2503458 |
3471360 | 694272 | 0.25 |
4339712 | 868352 | 0.2501475 |
5425152 | 1085440 | 0.250118 |
6781952 | 1356800 | 0.25009438 |
8477696 | 1695744 | 0.25003776 |
10597376 | 2119680 | 0.2500302 |
type struct xx{
Color bool `json:"color,omitempty"`
}
api
的情况下自由开发(bytes.Buffer
预分配空间)顺序通信进程(communicating sequential processes)
不要通过共享内存进行通信,而应通过通信进行内存共享
for elem := range ch
,在channel关闭时自动退出循环x, ok := <-ch
中ok的值判断channel是否关闭,以及x为数据或零值var a chan int
创建的channel)会panichappens before: 接受者接收数据发生在唤醒发送者goroutine之前
package main
import "fmt"
type T int
func IsClosed(ch <-chan T) bool {
select {
case <-ch:
return true
default:
}
return false
}
func main() {
c := make(chan T)
fmt.Println(IsClosed(c)) // false
close(c)
fmt.Println(IsClosed(c)) // true
}
发送
+++++++++++++++
| buf | -----> | x | x | x | x | x |
| sendx |
| recvx |
| sendq | -----> +++++++++
| recvq | | g | ---> G1
| closed | | elem | ---> 6
| lock | | ... |
+++++++++++++++ +++++++++
buf满时,用sudog包裹g和要发送的数据,入队sendq,并将当前gorutine进行gopark(m解除当前的g, m重新进入调度循环, g没有进入调度队列)
出现新接收方时,sendq出队,从buf拷贝队头,从sender拷贝到队尾,goready(放入调度队列,等待被调度)
读取空channel时,用sudog包裹g和要发送的数据,入队recvq,gopark
关闭channel
读取已关闭channel
type hselect struct {
// 总 case 数
tcase uint16 // total count of scase[]
// 当前已初始化填充的 case
ncase uint16 // currently filled scase[]
// 轮询顺序
pollorder *uint16 // case poll order
// case 的 channel 的加锁顺序
lockorder *uint16 // channel lock order
// case 数组,为了节省一个指针的 8 个字节搞成这样的结构
// 实际上要访问后面的值,还是需要进行指针移动
// 指针移动使用 runtime 内部的 add 函数
scase [1]scase // one per case (in order of appearance)
}
for d := range ch {
// do some happy things with d
}
for {
select {
case d, ok := <-ch1:
if !ok {
break outer
}
case d, ok := <-ch2:
if !ok {
break outer
}
}
}
select不支持throughfall,随机选择case使用堆实现
type _panic struct {
argp unsafe.Pointer // pointer to arguments of deferred call run during panic; cannot move - known to liblink
arg interface{} // argument to panic
link *_panic // 指向上一个panic
recovered bool // recover标志
aborted bool // the panic was aborted
}
recover流程
// iface描述接口包含方法
type iface struct {
tab *itab // 接口类型
data unsafe.Pointer // 具体值
}
type itab struct {
inter *interfacetype // 接口类型
_type *_type // 实体类型,内存对齐方式
fun [1]uintptr // 数组,保存实际方法地址(第一个方法)
}
type interfacetype struct {
typ _type // 描述 Go 语言中各种数据类型的结构体
pkgpath name // 接口包名
mhdr []imethod // 接口定义函数列表
}
// eface不包含任何方法的空接口
type eface struct {
_type *_type
data unsafe.Pointer
}
type _type struct {
// 类型大小
size uintptr
ptrdata uintptr
// 类型的 hash 值
hash uint32
// 类型的 flag,和反射相关
tflag tflag
// 内存对齐相关
align uint8
fieldalign uint8
// 类型的编号,有bool, slice, struct 等等等等
kind uint8
alg *typeAlg
// gc 相关
gcdata *byte
str nameOff
ptrToThis typeOff
}
type arraytype struct {
typ _type
elem *_type
slice *_type
len uintptr
}
type chantype struct {
typ _type
elem *_type
dir uintptr
}
type slicetype struct {
typ _type
elem *_type
}
type structtype struct {
typ _type
pkgPath name
fields []structfield
}
判断myWriter是否实现io.Writer接口
var _ io.Writer = (*myWriter)(nil)
var _ io.Writer = myWriter{}
interface保存一对数据
变量实际的值 变量的静态类型
类型转换
<结果类型> := <目标类型> ( <表达式> )
类型断言
<目标类型的值>,<布尔参数> := <表达式>.( 目标类型 ) // 安全类型断言
O(mn)
,Go 会对方法集的函数按照函数名的字典序进行排序,所以实际的时间复杂度为 O(m+n)
。var w io.Writer
w = os.Stdout
w = new(bytes.Buffer)
w = nil
定义值初始化
将
*os.File
赋值给w
使用w.Write([]byte("hello"))
效果和os.Stdout.Write([]byte("hello"))
一样
将
*bytes.Bufffer
赋值给w
将nil赋值给w
w将恢复到和他定义时相同的状态
接口值可以持有非常大的动态值
var x interface{} = time.Now()
接口动态值的动态类型必须可比较才能进行比较,否则会导致panic(如Slice)
var x interface{} = []int{1, 2, 3}
fmt.Println(x == x) // panic: comparing uncomparable type []int
type Interface interface {
Len() int
Less(i, j int) bool // i, j are indices of sequence elements
Swap(i, j int)
}
// 使用组合实现Reverse
type reverse struct{ Interface } // that is, sort.Interface
func (r reverse) Less(i, j int) bool { return r.Interface.Less(j, i) }
func Reverse(data Interface) Interface { return reverse{data} }
在包级别声明的变量会在main入口函数执行前完成初始化
内存块:元数据(大小,是否使用,下块内存地址),用户数据,对齐字段
Page:x64为8KB
mspan:内存管理基本单位,一组连续Page
mcache:保存各种大小mspan(无锁访问),每个P拥有1个mcache
mcentral:所有线程共享缓存,需加锁访问,每个级别mspan有2个链表(1个包含指针对象,1个不包含指针对象)
mheap:堆内存,将申请到的内存页组织成Span,然后组织成树结构(非链表)
寻找mspan
mcentral
mheap的span管理
mcentral向mheap申请span
mheap申请内存
1.创建对象规格大小对照表。 2.计算相关区域大小,并尝试从某个指定位置开始保留地址空间。 3.在heap里保存区域信息,包括起始位置和大小。 4.初始化heap其他属性。
保留虚拟地址空间(不分配内存)被划分成三个区域:
页所属span指针数组 GC标记位图 用户内存分配区域
+-------------------+--------------------+----------------------------------------------+
|spans 512MB |bitmap 32GB |arena 512GB |
+-------------------+--------------------+----------------------------------------------+
spans_mapped bitmap_mapped arena_start arena_used arena_end
内存扩张
Tips
+------------------sysmon---------------//--------+
| |
| |
+---+ +---+-------+ +--------+ +---+---+
go func() ---> |G| ---> |P|local| <===balance===> |global| <--//--- |P|M|
+---+ +---+-------+ +--------+ +---+---+
| | |
| +---+ | |
+----> |M| <---findrunnable---+---steal<--//------+
+---+
| 1. 语句go func() 创建G
| 2. 放入P本地队列(或平衡到全局队列)
+---execute<-----schedule 3. 唤醒或新建M执行任务
| | 4. 进入调度循环schedule
| | 5. 竭力获取待执行G任务并执行
+-->G.fn-->goexit--+ 6. 清理现场,重新进入调度循环
work stealing:当M绑定的P没有可运行的G时,它可以从其他运行的M’那里偷取G。
hand off:当M因为G进行系统调用阻塞时,释放绑定的P,把P转移给其他空闲的M上执行
默认为CPU数量GOMAXPROCS(逻辑),最多有GOMAXPROCS个线程,可能分布在不同的核上运行
可以控制并发数量,以达到控制资源利用的目的
type g struct {
stack stack // 描述了真实的栈内存,包括上下界
m *m // 当前的m
sched gobuf // goroutine切换时,用于保存g的上下文
param unsafe.Pointer // 用于传递参数,睡眠时其他goroutine可以设置param,唤醒时该goroutine可以获取
atomicstatus uint32
stackLock uint32
goid int64 // goroutine的ID
waitsince int64 // g被阻塞的大体时间
lockedm *m // G被锁定只在这个m上运行
}
type gobuf struct {
sp uintptr
pc uintptr
g guintptr
ctxt unsafe.Pointer
ret sys.Uintreg
lr uintptr
bp uintptr // for GOEXPERIMENT=framepointer
}
type m struct {
g0 *g // 带有调度栈的goroutine
gsignal *g // 处理信号的goroutine
tls [6]uintptr // thread-local storage
mstartfn func()
curg *g // 当前运行的goroutine
caughtsig guintptr
p puintptr // 关联p和执行的go代码
nextp puintptr
id int32
mallocing int32 // 状态
spinning bool // m是否out of work
blocked bool // m是否被阻塞
inwb bool // m是否在执行写屏蔽
printlock int8
incgo bool // m在执行cgo吗
fastrand uint32
ncgocall uint64 // cgo调用的总数
ncgo int32 // 当前cgo调用的数目
park note
alllink *m // 用于链接allm
schedlink muintptr
mcache *mcache // 当前m的内存缓存
lockedg *g // 锁定g在当前m上执行,而不会切换到其他m
createstack [32]uintptr // thread创建的栈
}
type p struct {
lock mutex
id int32
status uint32 // 状态,可以为pidle/prunning/...
link puintptr
schedtick uint32 // 每调度一次加1
syscalltick uint32 // 每一次系统调用加1
sysmontick sysmontick
m muintptr // 回链到关联的m
mcache *mcache
racectx uintptr
goidcache uint64 // goroutine的ID的缓存
goidcacheend uint64
// 可运行的goroutine的队列
runqhead uint32
runqtail uint32
runq [256]guintptr
runnext guintptr // 下一个运行的g
sudogcache []*sudog
sudogbuf [128]*sudog
palloc persistentAlloc // per-P to avoid mutex
pad [sys.CacheLineSize]byte
}
type schedt struct {
goidgen uint64
lastpoll uint64
lock mutex
midle muintptr // idle状态的m
nmidle int32 // idle状态的m个数
nmidlelocked int32 // lockde状态的m个数
mcount int32 // 创建的m的总数
maxmcount int32 // m允许的最大个数
ngsys uint32 // 系统中goroutine的数目,会自动更新
pidle puintptr // idle的p
npidle uint32
nmspinning uint32
// 全局的可运行的g队列
runqhead guintptr
runqtail guintptr
runqsize int32
// dead的G的全局缓存
gflock mutex
gfreeStack *g
gfreeNoStack *g
ngfree int32
// sudog的缓存中心
sudoglock mutex
sudogcache *sudog
}
return n
编译后
返回值=n
call defer func // 可以操作返回值
return
type _defer struct {
siz int32 // 函数的参数总大小
started bool // defer 是否已开始执行?
sp uintptr // 存储调用 defer 函数的函数的 sp 寄存器值
pc uintptr // 存储 call deferproc 的下一条汇编指令的指令地址
fn *funcval // 描述函数的变长结构体,包括函数地址及参数
_panic *_panic // 正在执行 defer 的 panic 结构体
link *_defer // 链表指针
}
runtime.deferproc
defer
指针指向上一个defer(栈)deferproc
的返回值,0表示成功,继续处理defer声明或后续代码runtime.deferreturn
SetFinalizer
obj被回收时,需要进行一些操作,如写日志等,可通过调用函数
runtime.SetFinalizer(obj interface{}, finalizer interfice{})
obj为指针类型
finalizer为参数为obj且无返回值的函数
当GC发现obj不可达时,会在独立goroutine中执行finalizer(obj)
扫描和标记完成后,白色为回收对象黑色为活跃对象
在执行写操作之前的代码,设置写屏障
GC循环
+-----------循环 N----------------+
+ 扫描终止、标记/标记终止 +
+ | | +
+ \ / +
+ 标记终止完成/清理 + ===> +------循环 N+1------+
<>只在当前文件中可以访问
SUBQ $0x18, SP // 对SP做减法,为函数分配函数栈帧
ADDQ $0x18, SP // 对SP做加法,清除函数栈帧
$num // 常数
MOVB $1, DI // 1 byte
MOVW $0x10, BX // 2 bytes
MOVD $1, DX // 4 bytes
MOVQ $-10, AX // 8 bytes
// 常见计算指令
ADDQ AX, BX // BX += AX
SUBQ AX, BX // BX -= AX
IMULQ AX, BX // BX *= AX
// 无条件跳转
JMP addr // 跳转到地址,地址可为代码中的地址,不过实际上手写不会出现这种东西
JMP label // 跳转到标签,可以跳转到同一函数内的标签位置
JMP 2(PC) // 以当前指令为基础,向前/后跳转 x 行
JMP -2(PC) // 同上
// 有条件跳转
JNZ target // 如果 zero flag 被 set 过,则跳转
FP: 使用形如 symbol+offset(FP)
的方式,引用函数的输入参数。例如 arg0+0(FP)
,arg1+8(FP)
,使用 FP 不加 symbol 时,无法通过编译
PC: 实际上就是在体系结构的知识中常见的 pc 寄存器,在 x86 平台下对应 ip 寄存器,amd64 上则是 rip。
SB: 全局静态基指针,一般用来声明函数或全局变量。
SP: plan9 的这个 SP 寄存器指向当前栈帧的局部变量的开始位置,使用形如 symbol+offset(SP)
的方式,引用函数的局部变量。offset 的合法取值是 [-framesize, 0),注意是个左闭右开的区间。假如局部变量都是 8 字节,那么第一个局部变量就可以用 localvar0-8(SP)
来表示。
go语言参数和返回值都在栈上传递
c语言前6个参数和返回值在寄存器上传递
源码
package main
func main() {
s := make([]int, 3, 10)
_ = f(s)
}
func f(s []int) int {
return s[1]
}
汇编代码
"".f STEXT nosplit size=53 args=0x20 locals=0x8
// 栈帧大小为8字节,参数和返回值为32字节
0x0000 00000 (main.go:8) TEXT "".f(SB), NOSPLIT, $8-32
// SP栈顶指针下移8字节
0x0000 00000 (main.go:8) SUBQ $8, SP
// 将BP寄存器的值入栈
0x0004 00004 (main.go:8) MOVQ BP, (SP)
// 将新的栈顶地址保存到BP寄存器
0x0008 00008 (main.go:8) LEAQ (SP), BP
0x000c 00012 (main.go:8) FUNCDATA $0, gclocals·4032f753396f2012ad1784f398b170f4(SB)
0x000c 00012 (main.go:8) FUNCDATA $1, gclocals·69c1753bd5f81501d95132d08af04464(SB)
// 取出slice的长度len
0x000c 00012 (main.go:8) MOVQ "".s+24(SP), AX
// 比较索引1是否超过len
0x0011 00017 (main.go:9) CMPQ AX, $1
// 如果超过len,越界了。跳转到46
0x0015 00021 (main.go:9) JLS 46
// 将slice的数据首地址加载到AX寄存器
0x0017 00023 (main.go:9) MOVQ "".s+16(SP), AX
// 将第8byte地址的元素保存到AX寄存器,也就是salaries[1]
0x001c 00028 (main.go:9) MOVQ 8(AX), AX
// 将结果拷贝到返回参数的位置(y)
0x0020 00032 (main.go:9) MOVQ AX, "".~r1+40(SP)
// 恢复BP的值
0x0025 00037 (main.go:9) MOVQ (SP), BP
// SP向上移动8个字节
0x0029 00041 (main.go:9) ADDQ $8, SP
// 返回
0x002d 00045 (main.go:9) RET
0x002e 00046 (main.go:9) PCDATA $0, $1
// 越界,panic
0x002e 00046 (main.go:9) CALL runtime.panicindex(SB)
0x0033 00051 (main.go:9) UNDEF
0x0000 48 83 ec 08 48 89 2c 24 48 8d 2c 24 48 8b 44 24 H...H.,$H.,$H.D$
0x0010 18 48 83 f8 01 76 17 48 8b 44 24 10 48 8b 40 08 .H...v.H.D$.H.@.
0x0020 48 89 44 24 28 48 8b 2c 24 48 83 c4 08 c3 e8 00 H.D$(H.,$H......
0x0030 00 00 00 0f 0b .....
rel 47+4 t=8 runtime.panicindex+0
CPU架构
最终结果
go bulid -gcflags "-N -l" -o xxx
-N -l:禁用编译器初始化
最终机器码汇编
go tool objdump -s "runtime\.init\b" xxx
过程汇编
go build -gcflags -S xxx.go
go tool compile -N -l -S xxx.go
package main
import (
"os"
"runtime/trace"
)
func main() {
f, err := os.Create("trace.out")
if err != nil {
panic(err)
}
defer f.Close()
err = trace.Start(f)
if err != nil {
panic(err)
}
defer trace.Stop()
// Your program here
}
查看
go tool trace trace.out
go test -run=none -bench=ClientServerParallel4 -cpuprofile=cprof net/http
将分析结果写入cprof
go tool pprof --text http.test cprof
--list funcname // 查看
--web // web可视化
go test
默认测试所有*_test.go文件 后跟具体文件名表示测试指定文件-bench=regexp
指向相应的benchmarks-cover
覆盖率测试-run=regexp
只运行regexp匹配的函数-v
显示详细测试命令
指示 | 作用 |
---|---|
//go:noinline |
不要内联。 |
//go:nosplit |
跳过栈溢出检测。 |
//go:noescape |
禁止逃逸,而且它必须指示一个只有声明没有主体的函数 |
//go:norace |
跳过竞态检测 |
s := test{
data: make(map[string]interface{}, 8),
}
s.data["count"] = 8
buf := new(bytes.Buffer)
enc := gob.NewEncoder(buf)
enc.Encode(s.data)
b := buf.Bytes()
fmt.Println(b)
dec := gob.NewDecoder(bytes.NewBuffer(b))
s2 := test{
data: make(map[string]interface{}, 8),
}
ds := dec.Decode(&s2.data)
fmt.Println(ds)
for _, v := range s2.data {
fmt.Printf("%v\t%[1]T", v)
}
使用gob序列化Go对象,反序列化时可以保持原有Go对象类型(JSON无法保持浮点,整型)
引用类型或包含引用类型在32bit平台为4Byte,64bit平台为8Byte
原文:https://www.cnblogs.com/quanee/p/11747015.html