golang涉及的名词概念解释
编程
编程可以看做以各种方式控制和组合计算机运行中的各种操作,以达到各种目的。
操作
一个操作通常是通过函数(function)调用或者使用操作符运算来完成的。条件和循环控制语句可以看做是特殊的操作
数据
数据通常被抽象各种类型(type)和值(value) 一个类型可以看作是值的模板。一个值可以看作是某个类型的实例。
类型
类型分成两类 自定义类型 和 预声明类型(内置类型)
值
确定的值可以用值的 字面形式 或者说是 字面量
值使用 变量 和 (具名)常量
代码要素
包含 具名的函数、 具名的值、类型
代码要素名必须为标识符
关键字
关键字帮助编译器和解释器解析代码的,这些关键字不能被当做标识符
零值
每种类型都有一个零值,一个类型的零值可以看做是此类型的默认值
- 一个布尔类型的零值表示真假中的假
- 数值类型的零值都是零
- 字符串类型的零值时一个空字符串
基本类型的字面量表示形式
一个值的字面形式称为一个字面量,它表示此值在代码中文字体现形式(和内存中的表现形式相对应)。一个值可能会有很多种字面量形式。
值的可寻址性
有些值可以被寻址,需要注意的是所有常量都是不可被寻址
表达式、语句
表达式表示一个值 语句表示一个操作(赋值,函数字面量,函数调用)
基本类型
内置类型,byte是uint8的一个内置别名,rune是int32的一个内置别名
组合类型
- 指针类型 - 类C指针
- 结构体类型 - 类C结构体
- 函数类型 - 函数类型在Go中是一等公民类别
- 容器类型
- 数组类型 - 定长容器类型
- 切片类型 - 动态长度和容量容器类型
- 映射类型 - 使用hash表实现的
- 通道类型 - 通道用来同步并发的协程
- 接口类型 - 接口在反射和多态中发挥着重要角色
具名类型和无名类型
一个具名类型可能为
- 一个预声明类型;
- 一个定义(非自定义泛型)类型;
- 一个(泛型类型的)实例化类型;
- 一个类型参数类型(使用在自定义泛型中)。
其它类型称为无名类型。一个无名类型肯定是一个组合类型
类型别名
与类型定义类似
type table = map[string]int
底层类型
每个类型都有一个底层类型
- 内置类型的底层类型是它自己
- 一个无名类型(必为一个组合类型)的底层类型为它自己。
- 在一个类型声明中,新声明的类型和源类型共享底层类型。
规则
- 规则1:预定义类型和类型字面量的底层类型是它们自身。
- 规则2:自定义类型的底层类型递归向下查找,直到查找到预定义类型或类型字面量为止。
// a的底层类型是int,因为int是预定义类型
type a int
// b的底层类型是int
// 因为a是自定义类型,继续向下找到int,int是预定义类型
type b a
// c的底层类型是[]int
// 因为[]int是类型字面量
type c []int
// d的底层类型是[]int
// 因为c是自定义类型,继续向下找到[]int
// []int是类型字面量
type d c
// e的底层类型是[]a
// 因为[]a不是自定义类型,也不是预定义类型,是类型字面量
type e []a
// f底层类型是*e
// *e不是自定义,不是预定义
// *e是类型字面量
type f *e
作用
类型间赋值,类型之间强制转换,都与底层类型有着密切关系
值部
在运行时刻,很多值是存储在内存的。每个这样的值都有一个直接部分,但是有一些值还可能有一个或多个间接部分。每个值部分在内存中都占据一段连续空间。 通过安全或者非安全指针,一个值的间接部分被此值的直接部分所引用。
Go也可以被看作是C语言的一个扩展框架。 在C中,值的内存结构都是很透明的;但在Go中,对于某些类型的值,其内存结构却不是很透明。 在C中,每个值在内存中只占据一个内存块(一段连续内存);但是,一些Go类型的值可能占据多个内存块。 一个Go值分布在不同内存块上的部分为此值的各个值部(value part)。 一个分布在多个内存块上的值含有一个直接值部和若干被此直接值部引用着的间接值部。
每个值在内存中只分布在一个内存块上的类型 | 每个值在内存中会分布在多个内存块上的类型 |
---|---|
单直接值部 | 直接值部->底层间接值部 |
布尔类型 | 切片类型 |
各种数值类型 | 映射类型 |
指针类型 | 通道类型 |
非类型安全指针类型 | 函数类型 |
结构体类型 | 接口类型 |
数组类型 | 字符串类型 |
值尺寸
一个值存储在内存中是要占据一定的空间的。此空间的大小称为此值的尺寸。值尺寸是用字节数来衡量的。 在Go中,当我们谈及一个值的尺寸,如果没有特殊说明,我们一般是指此值的直接部分的尺寸。 某个特定类别的所有类型的值的尺寸都是一样的。因为这个原因,我们也常将一个值的尺寸说成是它的类型的尺寸(或值尺寸)。
指针类型的基类型
如果一个指针类型的底层类型表示为*T,则此指针类型的基类型为T所表示的类型。
函数类型的签名
一个函数和其类型的签名由此函数的输入参数和返回结果的类型列表组成。 函数名称和函数体不属于函数签名的构成部分。
引用类型和引用值
正常理解 切片、映射、通道都属于引用类型,但是Go中一些函数调用的参数是通过引用来传递的。(Go中所有函数调用的参数都是通过值赋值直接值部的方式来传递的)
使用指针持有者类型来代替引用类型来理解。
golang的堆栈 内存分配中的堆和栈
栈:由操作系统自动分配释放,存放函数的参数值,局部变量的值等。其操作方式类似数据结构中的栈
堆:一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收,分配方式倒是类似于链表
堆栈缓存方式
栈使用的是一级缓存, 他们通常都是被调用时处于存储空间中,调用完毕立即释放。
堆则是存放在二级缓存中,生命周期由虚拟机的垃圾回收算法来决定(并不是一旦成为孤儿对象就能被回收)。所以调用这些对象的速度要相对来得低一些。
Go的堆栈分配
- 每个goroutine维护着一个栈空间,默认最大为4KB.
- 当goroutine的栈空间不足时,golang会调用runtime.morestack(汇编实现:asm_xxx.s)来进行动态扩容.
- 连续栈是当栈空间不足的时候申请一个2倍于当前大小的新栈,并把所有数据拷贝到新栈,接下来的所有调用执行都发生在新栈上.
- 每个function维护着各自的栈帧(stack frame),当function退出时会释放栈帧.
Golang逃逸分析
- 在编译程序优化理论中,逃逸分析是一种确定指针动态范围的方法,用于分析在程序的哪些地方可以访问到指针。
- Golang在编译时的逃逸分析可以减少gc的压力,不逃逸的对象分配在栈上,当函数返回时就回收了资源,不需要gc标记清除。
- 如果你定义的对象的方法上有同步锁,但在运行时,却只有一个线程在访问,此时逃逸分析后的机器码,会去掉同步锁运行,提高效率。
golang只有在function内的对象可能被外部访问时,才会把该对象分配在堆上.
- 函数返回值是函数内声明的指针,那么就会有逃逸现象
- 参数是指针是不会引起逃逸的
Go并发注意
为了避免 goroutine 泄露,请注意:生成子 goroutine 的父 goroutine 需要负责停止子 gotoutine,即谁创建谁销毁
设计系统的时候,应该一开始就考虑 timeout 和 cancel。
分布式系统需要支持 timeout 的几个理由
- 饱和 系统饱和
- 数据过期
- 防止死锁
分布式系统应该支持 cancel 操作的几个理由
- 超时
- 用户干预
- 父节点取消
- 重复请求