Go自学笔记(持续更新...)

2020-04-20  本文已影响0人  皇甫LG

一、各数据类型内存布局

    +-+
    |1|
    +-+
     |
    +-+-+-+-+-+-+-+-+
    |0|0|0|0|0|0|0|1|
    +-+-+-+-+-+-+-+-+
    +-----+
    |  4  |
    +-----+
       |
       |
    +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ 
    |0|0|0|0|0|0|0|0| |0|0|0|0|0|0|0|0| |0|0|0|0|0|0|0|0| |0|0|0|0|0|1|0|0|
    +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ 
    +-----+
    |  8  |
    +-----+
       |
       |
    +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ 
    |0|0|0|0|0|0|0|0| |0|0|0|0|0|0|0|0| |0|0|0|0|0|0|0|0| |0|0|0|0|0|0|0|0|
    +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+     
    +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ 
    |0|0|0|0|0|0|0|0| |0|0|0|0|0|0|0|0| |0|0|0|0|0|0|0|0| |0|0|0|0|1|0|0|0|
    +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ 
    +-----+
    |  8  |
    +-----+
       |
       |
      同上
var s string = "hello"
    +---------+---------+
    | Pointer | len=5   |   # pointer和len各占4|8个字节
    +---------+---------+
       |
       |
    +---+---+---+---+---+
    | h | e | l | l | o |    # [5]byte
    +---+---+---+---+---+
struct{a byte; b byte; c int32} = {1,2,3}
    +---+---+---+---+---+---+---+---+
    | 1 | 2 | 0 | 0 | 3 | 0 | 0 | 0 |    # 内存对齐原则
    +---+---+---+---+---+---+---+---+

struct{a *int; b int}
    +-----------+-----+
    | pointer a |  b  |   
    +-----------+-----+
        |
        |
        +-----+
        | int |   
        +-----+

x = []int{0,1,2,3,4,5,6,7}
    +---------+---------+---------+
    | Pointer | len=8   | cap=8   |    # 各占8个字节
    +---------+---------+---------+
       |
       |
        +---+---+---+---+---+---+---+---+
        | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |    # [8]int
        +---+---+---+---+---+---+---+---+
            |
            |
            +---------+---------+---------+
            | Pointer | len=2   | cap=5   | # x[1:3:6]
            +---------+---------+---------+           
    
var in interface{}    
    +---------+---------+
    |  *itab  |  *data  |   # 各占8个字节
    +---------+---------+
       |           |
       |           |
        +------+    +------+
        | Itab |    | data | 
        +------+    +------+
var n = new([3]int)
    +---------+
    | pointer |   #占8个字节
    +---------+
       |
       |
       +---+---+---+
       |0  | 0 | 0 |
       +---+---+---+

slice = make([]int,1,3)
    +---------+---------+---------+
    | pointer |  len=1  |  cap=3  |    # 各占8个字节
    +---------+---------+---------+
       |
       |
       +---+---+---+
       |0  | 0 | 0 |    # [3]int
       +---+---+---+
    
    
channel = make(chan int);
    +---------+
    | pointer |     # 实际返回的是一个指针包装对象
    +---------+ 
       |
       |
       +---------------+
       | chan.c  Hchan |
       +---------------+

二、基础概念总结(持续更新...)

func StrEncodeTest() {
    type MyData struct {
        One int
        two string
    }
    in := MyData{1, "data"}
    fmt.Printf("%#v\n", in)

    en, _ := json.Marshal(in)
    fmt.Println(string(en))

    var out MyData
    json.Unmarshal(en, &out)
    fmt.Printf("%#v\n", out)
}
# 移除单个元素
func (s *Slice) SliceRemove(value int) {
    for i, v := range *s {
        if v == value {
            *s = append((*s)[:i], (*s)[i+1:]...)   ##slice后面‘...’ 由于元素类型相同,因此append会提示使用..., 使用两个参数即可
        }
    }
}

# 移除多个元素
*s = append((*s)[:begin-1], (*s)[end:]...) 
A. 给一个 nil channel 发送数据,造成永远阻塞
B. 从一个 nil channel 接收数据,造成永远阻塞
C. 给一个已经关闭的 channel 发送数据,引起 panic
D. 从一个已经关闭的 channel 接收数据,如果缓冲区中为空,则返回一个零值
# for语句中的迭代变量在每次迭代中都会崇勇,即for中创建闭包函数接受到的参数始终是同一个值,在goroutine开始执行时都会得到同一个迭代值。

fun main(){
    data := []string{"one", "two", "three"}
    
    for _, v := range data {
        go func(){ //闭包
            fmt.Println(v)
        }() 
    }
    time.Sleep(3*time.Second)
}

# 输出 three,three,three

# 解决方法:在for 内部实用局部变量保存迭代值,在传参:
   for _, v :=range data {
       vCopy := v
       go func(){
           fmt.Println(vCopy)
       }()
   }
package main

import "sync"

type SafeMap struct {
    sync.RWMutex
    Map map[int]int
}

func main() {
    safeMap := newSafeMap(10)

    for i := 0; i < 100000; i++ {
        go safeMap.writeMap(i, i)
        go safeMap.readMap(i)
    }

}

func newSafeMap(size int) *SafeMap {
    sm := new(SafeMap)
    sm.Map = make(map[int]int)
    return sm

}

func (sm *SafeMap) readMap(key int) int {
    sm.RLock()
    value := sm.Map[key]
    sm.RUnlock()
    return value
}

func (sm *SafeMap) writeMap(key int, value int) {
    sm.Lock()
    sm.Map[key] = value
    sm.Unlock()
}

通过sync.RWMutex读写锁控制达到安全访问map,此时会有一定的性能损耗。
建议使用==sync.Map== 是go1.9版本中提供的一种效率比较高并发安全。

a) func (u *user) notify() { show(&u) }

b1) func (u user) notify() { show(u) }
b2) func (u user) notify() { show(&u) }
Array:
    数组是固定长度的数据类型,包含相同类型的连续元素,内存分配是连续的。初始值为0;
    一个数组可以被赋值给任意相同类型(长度和元素类型)的数组。
    建议在函数中不要使用数组作为参数传递。

Slice:
    一种动态数组,底层内存是连续分配的。内存结构【指向底层数组的指针|长度|容量】;
    var slice []int  // nil slice 
    slice :=make([]int,0) //empty slice
    slice :=[]int{}    //empty slice
    实用append方法可以对slice进行扩容和删除。
    函数使用slice作为参数传递是比较廉价的,sizeof(slice)=16字节。
    
Map:
    Map是一种无序的K-V集合。由于是无序的,因此无法决定迭代时返回顺序。
    在函数使用map作为参数传递时,不是传递的map拷贝,而是引用类型,因此会修改原map的内容的。详情请参考:[轻松理解Go函数传参内幕](https://www.jianshu.com/p/198d9961b76a)

Set:
    go本身不提供set,需要自己实现。参考:https://github.com/deckarep/golang-set
    
总结:array是slice和map的底层结构。slice有cap限制,但是可以通过append增加元素,map没有cap限制,可以无限增加。因此内建函数 cap 只能作用在 slice 上。在函数间传递slice和map是比较廉价的,它们不会传递底层数组的拷贝。
go语言编译器会自动决定把一个变量放在栈还是放在堆,编译器会做逃逸分析(escape analysis),当发现变量的作用域没有跑出函数范围,就分配在栈,反之则分配在堆。
M(machine): 代表着真正的执行计算资源,可以认为它就是os thread(系统线程)。
P(processor): 表示逻辑processor,是线程M的执行的上下文。
G(goroutine): 调度系统的最基本单位goroutine,存储了goroutine的执行stack信息、goroutine状态以及goroutine的任务函数等。
panic 调用栈
pprof
火焰图(配合压测)
使用go run -race 或者 go build -race 来进行竞争检测
查看系统 磁盘IO/网络IO/内存占用/CPU 占用(配合压测)
Runtime 负责管理任务调度,垃圾收集及运行环境。一个重要的组成部分goroutinue scheduler,负责追踪调度每个routine运行,实际上是从应用程序的process所属的thread pool中分配一个thread来执行这个goroutine,每个gotine只有分配到一个os thread才能运行。
编译后的可执行文件,从运行角度看是有2个部分组成,1)用户代码,2)runtime。 runtime通过接口函数调用来管理goroutine,chanal等其他一些高级功能,从用户代码发起的调用操作系统的API都会被runtime拦截并处理。
上一篇下一篇

猜你喜欢

热点阅读