1. GMP调度模型
G: goroutine,是主要的执行体
M: 线程,每个协程都会被分配到响应的线程进行执行
P: 是线程的上下文,会维护一个本地goroutine队列。
working-steal机制: 本地goroutine队列空后,回去全局G队列获取G执行,当全局G队列也空之后,则会去其他M绑定的P中窃取G执行
2. golang的gc
三色标记混合写屏障
3. 什么是heap,什么是栈,以及各自存储什么数据
代码的本质:主要包含两部分:指令部分(cpu可执行的指令)、数据部分(常量等)。
代码执行:可执行文件
加载到内存中,cpu通过内存取指(cpu控制单元-从内存获取指令)、译值(cpu控制单元-解析获取的指令)、执行(cpu运算单元-执行具体指令)
内存的作用:1. 暂存可执行文件
中的指令、预置数据;2. 暂存执行过程中的中间数据;
栈内存:计算机对连续内存的采取线性分配的管理方式,便于高效存储指令运行过程中的临时变量(连续内存的线性分配,目的:简单高效的分配和回收内存)内存分配=入栈,内存回收=出栈。
4. slice与array的区别是什么?
共性:都是一组同一类型元素组成的序列,通过索引获取元素,len和cap获取长度和容量。
区别:
数组 | 切片 | |
---|---|---|
定义 | 数组是切片的底层数据结构 | 切片又称动态数组,是基于数组类型的封装(slice是对array一段连续片段的引用) |
容量、长度 | 在创建时就确定,且不可变 | 可变长 |
类型 | 值类型(作为func的参数传入时,将复制一份数组,而非传入同一个指针) | 引用类型(作为func的参数传入时,传入指针) |
使用频率 | 极少(一般最为内置类型,极少在业务代码中使用) | 高 |
slice拷贝:
内置函数copy
可拷贝两个切片,会将s_slice的数据逐个拷贝到d_slice所指向的数组中,拷贝过程不会扩容。(即len(s_slice)=10, len(d_slice)=5时,只拷贝前5个元素)
slice扩容:
内置函数
append
可向切片插入元素,当append
函数发现len超出cap时,会发生slice扩容。(扩容操作只关心容量)
扩容实际上是分配一块更大的内存,将原slice的数据拷贝进新slice,然后返回新slice,扩容后再将数据追加进去。
扩容规则:
|
|
Python切片与Go切片有何不同?
最大的不同:
Python的切片产生的是新的对象,对新对象的成员的操作不影响旧对象;
Go的切片产生的是旧对象一部分的引用,对其成员的操作会影响旧对象;
其他不同
- Go的切片,是相同类型元素组成的序列;Python的列表则不限制类型。
- Go的切片操作
[a:b]
不能为负数;Python可以,即允许从末尾向前引用。 - Go的切片操作
[a🅱️c]
中,c是容量;Python中的c是步长。
原因:底层实现不同
- Go的切片,底层是一个三元组。指针指向一块连续的内存,长度是已有成员数,容量是最大成员数。对它切片,是对原指针的移动,而非申请新的内存(Go的切片操作通常会和生成该切片的切片或数组共享内存。)
- Python的切片,其实就是指针数组。对它进行切片,会创建新的数组。在Python的切片中,并没有容量的概念。
slice
5. channel在使用中的注意事项(有无缓存; 写数据、读数据; 关闭channel)?
6. Mutex锁在使用中有什么注意事项?
- Go的锁不可以重入
- 锁不具备绑定关系,必须遵循谁申请,谁释放的原则(协程可以释放别的协程的锁,导致data-race问题)。