golang深入理解

1、select是随机的还是顺序的?select的使用场景?

select会随机选择一个可用通道做收发操作.
常用语gorotine的完美退出
golang 的 select 就是监听 IO 操作,当 IO 操作发生时,触发相应的动作
每个case语句里必须是一个IO操作,确切的说,应该是一个面向channel的IO操作

2、Go语言局部变量分配在栈还是堆?深入分析

Go语言编译器会自动决定把一个变量放在栈还是放在堆.
编译器会做逃逸分析,当发现变量的作用域没有跑出函数范围,就可以在栈上,反之则必须分配在堆。

3、简述一下你对Go垃圾回收机制的理解?golang GC

v1.1 STW
v1.3 Mark STW, Sweep 并行【优化STW】
v1.5 三色标记法【初始白色--> 灰色待处理队列--> 标记为黑色--> STW(sweep黑色)】
v1.8 hybrid write barrier(混合写屏障:优化STW)

4、golang的协程调度GMP原理? 【线程模型 M:N】

  • M(machine): 代表着真正的执行计算资源,可以认为它就是os thread(系统线程)。
  • P(processor): 是协程调度器。P的数量可以通过GOMAXPROCS设置。
  • G(goroutine): 调度系统的最基本单位goroutine,存储了goroutine的执行stack信息、goroutine状态以及goroutine的任务函数等。相当于OS的进程控制块PCB;它包含:函数执行的指令和参数,任务对象,线程上下文切换,字段保护,和字段的寄存器

调度器设计策略

1.复用线程:

  • work stealing机制:当本线程无可运行的Goroutine时,尝试从其他线程绑定的P队列偷取G,而不是消毁线程。
  • hand off机制:当本线程因为Goroutine进行系统调用阻塞时,线程释放绑定的P,把P转移给其它空闲的线程执行。

2.抢占:在goroutine中要等待一个协程主动让出CPU才执行下一个协程;在GO中,一个goroutine最多占用CPU 10ms, 防止其他goroutine被锁死。
3.利用并行:利用GOMAXPROCS设置P数量,最多有GPMAXPROCS个线程分布在多个CPU上同时执行。
4.全局G队列:当M执行work stealing从其它P的本地队列中偷不到G时,它可以从全局列队获取G.
5.自旋线程:

5、介绍下 golang 的 runtime 机制?深入分析

Runtime 负责管理任务调度垃圾收集运行环境。同时,Go提供了一些高级的功能,如goroutine, channel, 以及GC。
这些高级功能需要一个runtime的支持. runtime和用户编译后的代码被linker静态链接起来,形成一个可执行文件
这个文件从操作系统角度来说是一个user space的独立的可执行文件。
从运行的角度来说,这个文件由2部分组成,一部分是用户的代码,另一部分就是runtime。
runtime通过接口函数调用来管理goroutine, channel及其他一些高级的功能。从用户代码发起的调用操作系统API的调用都会被runtime拦截并处理。
Go runtime的一个重要的组成部分是goroutine scheduler。他负责追踪,调度每个goroutine运行,实际上是从应用程序的process所属的thread pool中分配一个thread来执行这个goroutine。
因此,和java虚拟机中的Java thread和OS thread映射概念类似,每个goroutine只有分配到一个OS thread才能运行。

6、如何获取 go 程序运行时的协程数量, gc时间, 对象数, 堆栈信息?

调用接口runtime.ReadMemStats可以获取以上所有信息, 注意: 调用此接口会触发 STW(Stop The World)

7、介绍下你平时都是怎么调试 golang 的 bug 以及性能问题的?

panic 调用栈 GDB
pprof
火焰图(配合压测)
使用go run -race 或者 go build -race 来进行竞争检测
查看系统 磁盘IO/网络IO/内存占用/CPU 占用(配合压测)

8、make 和 new 的区别

  • make和new都是golang用来分配内存的內建函数,且在堆上分配内存.
  • make 即分配内存,也初始化内存。new只是将内存清零,并没有初始化内存。
  • new(T)是为一个T类型的新值分配空间, 并将此空间初始化为T的零值, 并返回这块内存空间的地址, 也就是T类型的指针T, 该指针指向T类型值占用的那块内存.
  • new 分配返回的是指针,即类型 *Type。make 返回引用,即 Type。
  • make(T)返回的是初始化之后的T, 且只能用于slice, map, channel三种类型.
  • make(T, args) 返回初始化之后T类型的值, 且此新值并不是T类型的零值, 也不是T类型的指针T, 而是T类型值经过初始化之后的引用.

9、主协程如何等其余协程完再操作

使用channel进行通信,context,select

10、实现set

type inter interface{}
type Set struct {
    m map[inter]bool
    sync.RWMutex
}
 
func New() *Set {
    return &Set{
    m: map[inter]bool{},
    }
}
func (s *Set) Add(item inter) {
    s.Lock()
    defer s.Unlock()
    s.m[item] = true
}

11、defer关键字 defer使用的一些坑

  1. defer在匿名返回值和命名返回值函数中的不同表现
func watShadowDefer(i int) (ret int) {
    ret = i * 2
    if ret > 10 {
        ret := 10          // 新初始化了一个局部变量
        defer func() {
            ret = ret + 1  // defer 将当前作用域相关信息压栈
        }()
    }
    return  //100。知识点:变量作用域和defer 返回值。
}

func main() {
    result := watShadowDefer(50)
    fmt.Println(result)
}

12、slice切片:是对数组一个连续片段的引用,是一种引用类型

  • 切片提供了一个与指向数组的动态窗口
  • Go 中数组赋值和函数传参都是值复制的,而切片是引用传递的
  • 使用make创建切片:实现运行期动态指定数组长度,绕开编译期常量的限制
  • 切片扩容策略:小于1024个元素,翻倍扩容。超过1024后1.25倍扩容。
// 切片定义
type slice struct {
    array unsafe.Pointer
    len int
    cap int       // cap一定要比len大
}

// 从 slice 中得到一块内存地址
s := make([]byte, 200)
ptr := unsafe.Pointer(s[0])

// 从go的内存地址中构造一个slice
var ptr unsafe.Pointer
var s1 = struct {       //初始化一个结构体
    addr unitptr
    len int
    cap int
}{ptr, length, length}
s := *(*[]byte)(unsafe.Pointer(s1))   // ptr 转 slice

13、go切片的排序与查找操作,排序操作在sort包中

sort.Ints对整数进行排序,
sort.Strings对字符串进行排序,
sort.Float64对浮点数进行排序
var a = [...]int{1,8,38,2,348,484}   //数组是值类型,不能直接排序,必须转为切片    
sort.Ints(a[:])                      //SearchInts默认排序后的位置,一定要排序后在查找   
index:=sort.SearchInts(a[:],348)  

// 排序二维切片
intervals := [][]int{}
sort.Slice(intervals, func(i, j int) bool {
    return intervals[i][0]

14、go函数不支持默认参数,不支持重载,不支持命名函数嵌套,匿名函数可以嵌套

  • Go函数从实参到形参的传递永远是值拷贝,,有时函数调用后实参指向的值发生了变化,是因为参数传递的是指针的拷贝,实参是一个指针变量,传递给形参的是这个指针变量的副本,实质上仍然是值拷贝;
  • 闭包
package main
import "fmt"

func Accumulate(a int) func(b int) int {
    return func(b int) int {
        return a + b
    }
}

func main() {
    accAdd := Accumulate(1)
    fmt.Println(accAdd(2))    // 3
    fmt.Println(accAdd(4))    // 5
}

15、在golang中可以给任意类型(除指针)添加相应方法

type Interger int

func (i Interger) Less (j Interger) bool {
    return i < j
}

func main() {
    var i Interger = 1
    fmt.Print(i.Less(5))
}

17 json与golang类型对应关系

bool, for JSON booleans
float64, for JSON numbers
string, for JSON strings
[]interface{}, for JSON arrays
map[string]interface{}, for JSON objects
nil for JSON null

http请求时需要注意【不要连接完,立马defer】

resp, err := http.Get(url)
// 先判断操作是否成功
if err != nil {
    return err
}
// 如果操作成功,再进行Close操作
defer resp.Body.Close()

fallthrough 关键字的作用?

执行完fallthrough后直接跳到下一个条件语句,本条件执行语句后面的语句不执行

go 中除了加 Mutex 锁以外还有哪些方式安全读写共享变量? chan CSP

函数式选项(Functional Options)

关于Goroutine的启动和退出

golang实现协程池

go复合数据类型struct和json互相转换

在for循环中使用defer可能导致的性能问题

调用os.Exit时defer不会被执行

判断执行没有err之后,再defer释放资源

goroutine泄漏有没有处理,设置timeout,select加定时器

go的锁如何实现,用了什么cpu指令go锁的实现

go什么情况下会发生内存泄漏?

用channel实现定时器?(实际上是两个协程同步)

go为什么高并发好

你可能感兴趣的:(golang深入理解)