深浅拷贝
1.深浅拷贝 01.深浅拷贝 1.1 深浅拷贝定义 浅拷贝就是只拷贝指针的值,指针指向的内容只有一份。 而深拷贝是把指针指向的值拷贝一份。 golang里面也有浅拷贝和深拷贝。 slice的浅拷贝就是指slice变量的赋值操作。 slice的深拷贝就是指使用内置的copy函数来拷贝两个slice。 1.2 深浅拷贝代码举例 12345678910111213141516171819202122232425package mainimport "fmt"func main() { SliceShallowCopy() SliceDeepCopy()}func SliceShallowCopy() { src := []byte {1,2,3,4,5,6} dst := src fmt.Println("浅拷贝原始数据",src) // [1 2 3 4 5 6] dst[0]=10 // 修改拷贝数据,原始数据会以前跟着改变 fmt.Println("after modify...
垃圾回收
1.垃圾回收 01.三种常见垃圾回收机制 1.0 垃圾回收是什么 传统的系统级编程语言(主要指C/C++)中,程序员必须对内存小心的进行管理操作,控制内存的申请及释放。 稍有不慎,就可能产生内存泄露问题,这种问题不易发现并且难以定位 后来开发出来的几乎所有新语言(java,python,php等等)都引入了语言层面的自动内存管理 也就是语言的使用者只用关注内存的申请而不必关心内存的释放 内存释放由虚拟机(virtual machine)或运行时(runtime)来自动进行管理 而这种对不再使用的内存资源进行自动回收的行为就被称为垃圾回收。 1.1 引计数 原理 当一个对象的引用被创建或者复制时,对象的引用计数加1;当一个对象的引用被销毁时,对象的引用计数减1. 当对象的引用计数减少为0时,就意味着对象已经再没有被使用了,可以将其内存释放掉。 优点 引用计数有一个很大的优点,即实时性,任何内存,一旦没有指向它的引用,就会被立即回收,而其他的垃圾收集技术必须在某种特殊条件下才能进行无效内存的回收。 缺点 引用计数机制所带来的维护引用计数的额外操作与Python运行...
内存泄露
1.内存泄漏 01.内存泄漏概念 1.1 内存泄漏定义 定义:由于疏忽或错误造成程序未能释放已经不再使用的内存。 1.2 go内存泄漏两种情况 情况1:僵尸进程 1234567 - 有goroutine泄漏,goroutine“飞”了,zombie goroutine没有结束 - 这个时候在这个goroutine上分配的内存对象将一直被这个僵尸goroutine引用着 - 进而导致gc无法回收这类对象,内存泄漏- ``` 情况2:全局数据结构挂住了本该释放的对象 - 有一些全局(或者生命周期和程序本身运行周期一样长的)的数据结构意外的挂住了本该释放的对象 - 虽然goroutine已经退出了,但是这些对象并没有从这类数据结构中删除,导致对象一直被引用,无法被回收。 02.内存泄漏排查 2.1 排除掉goroutine泄漏 首先,我利用压测工具对server进行100个websocket连接,模拟用户浏览行为,然后关闭连接。 打开浏览器查看goroutine数量,发现新起的goroutine全部已经销毁,没有观察到有泄漏的goroutine,因此排除此情...
内存逃逸
1.内存逃逸 01.内存逃逸 1.1 其他语言内存回收机制 在C/C++开发中,动态分配内存(new/malloc)需要我们手动释放资源。 这样做的好处是,需要申请多少内存空间可以很好的掌握怎么分配。 但是这有个缺点,如果忘记释放内存,则会导致内存泄漏。 在很多高级语言中(python/Go/java)都加上了垃圾回收机制。 1.2 什么是内存逃逸 函数内部申请的临时变量,正常会分配到栈里,栈中的内存分配非常快,自动回收,无需垃圾回收 但是若果申请的临时变量作为了函数返回值,编译器会认为在退出函数之后还有其他地方在引用 在编译的时候就会将变量存储到堆中,堆中的数据不会自动回收,必须使用垃圾回收机制清楚 我们将这种 由于某些原因,数据没有分配到栈中而是分配到堆中的现象叫做 内存逃逸 1.2 golang的内存分配之堆和栈 1.2.1 内存分片概述 Go的垃圾回收,让堆和栈堆程序员保持透明。 真正解放了程序员的双手,让他们可以专注于业务,“高效”地完成代码编写。 把那些内存管理的复杂机制交给编译器。 栈 可以简单得理解成一次函数调用内部申请到的内存,它们会随着函数的返回把内...
make和new
1.make和new 01.make和new 1.1 make和new比较 new 和 make 是两个内置函数,主要用来创建并分配类型的内存。 make和new区别 make 关键字的作用是创建于 slice、map 和 channel 等内置的数据结构 new 的作用是为类型申请一片内存空间,并返回指向这片内存的指针 123456789101112package mainimport "fmt"func main() { a := make([]int, 3, 10) // 切片长度为 1,预留空间长度为 10 a = append(a,1) fmt.Printf("%v--%T \n",a,a) // [0 0 0]--[]int 值----切片本身 var b = new([]int) //b = b.append(b,2) // 返回的是内存指针,所以不能直接 append *b = append(*b, 3) // 必须通过 * 指针取值,才能进行 app...
pointer
01.pointer 01.pointer 1.1 什么是pointer 在Go里面pointer就是1种可以把内存地址存储起来的数据类型。 我们使用pointer数据类型的变量可以记录下另1个变量的内存地址,方便我们修改这变量的值。 只需要记住以下几点: &变量名: 获取变量的内存地址 *pointor:通过指针类型的变量,获取该指针指向的值 12345678910package mainimport "fmt"func main() { name := "张三" p1 := &name // &变量名: 获取变量的内存地址 p2 := *&name // *pointor:通过指针类型的变量,获取该指针指向的值 fmt.Println(name,p1,p2) // 张三 0xc000088230 张三} 1.2 为什么Go中使用了指针? 因为指针可以帮助我们节省内存,我们知道在程序运行时值类型的变量被赋值之后会对值进行重新拷贝 如果我们每...
interface
1.interface 01.interface 1.1 interface作用 接口是 Go 语言的重要组成部分,它在 Go 语言中通过一组方法指定了一个对象的行为 接口 interface 的引入能够让我们在 Go 语言更好地组织并写出易于测试的代码 golang中的接口分为 1带方法的接口和空接口 iface:表示带方法的接口 eface:表示空接口 1.2 eface空接口 空接口eface结构比较简单,由两个属性构成 一个是类型信息_type,一个是数据信息 其数据结构声明如下: 1234type eface struct { _type *_type data unsafe.Pointer} 其中_type是GO语言中所有类型的公共描述,Go语言几乎所有的数据结构都可以抽象成 _type,是所有类型的公共描述 type负责决定data应该如何解释和操作 type的结构代码如下: 12345678910111213141516type _type struct { size uintptr ...
Channel
1.channel 01.channel的整体结构图 1.1 channel结构图 channel本质是一个hchan这个结构体 12345678type hchan struct { buf unsafe.Pointer // points to an array of dataqsiz elements sendx uint // send index recvx uint // receive index recvq waitq // list of recv waiters sendq waitq // list of send waiters lock mutex} 简单说明: buf是有缓冲的channel所特有的结构,用来存储缓存数据,是个循环链表 sendx 123和 recvx 123用于记录 buf 123456789 这个循环链表中的~发送或者接收的~index - `recvx`和`sendx`是根据循环链表`buf`的变动而改变的- ...
数组与切片
1.数组与切片 01.数组 1.1 数组 数组是一种非常有用的数据结构,因为其占用的内存是连续分配的。 由于内存连续,CPU能把正在使用的数据缓存更久的时间。 而且内存连续很容易计算索引,可以快速迭代数组里的所有元素。 golang中声明数组需要告诉数组长度,以及存放数据类型 一旦初始化成功,那么存储的数据类型和数组长度就都不能改变了 xxxxxxxxxx package mainimport "fmt"func main() { SliceShallowCopy() SliceDeepCopy()}func SliceShallowCopy() { src := []byte {1,2,3,4,5,6} dst := src fmt.Println(“浅拷贝原始数据”,src) // [1 2 3 4 5 6] dst[0]=10 // 修改拷贝数据,原始数据会以前跟着改变 fmt.Println(“after modify[src]:”,src) // [10 2 3 4 5 6]}func S...
Map
01.Map 01.map底层 [参考(opens new window)](https://www.bookstack.cn/read/qcrao-Go-Questions/map-map 的扩容过程是怎样的.md) 1.1 map底层浅析 笼统的来说,go的map底层是一个hash表,通过键值对进行映射 键通过哈希函数生成哈希值,然后go底层的map数据结构就存储相应的hash值,进行索引,最终是在底层使用的数组存储key,和value 稍微详细的说,就设计到go map 的结构:hmap 和bmap 1.2 Hash函数 哈希函数会将传入的key值进行哈希运算,得到一个唯一的值。 go语言把生成的哈希值一分为二,比如一个key经过哈希函数,生成的哈希值为:8423452987653321,go语言会这它拆分为84234529,和87653321。 那么,前半部分就叫做 高位哈希值 ,后半部分就叫做 低位哈希值 。 **高位哈希值:**是用来确定当前的bucket(桶)有没有所存储的数据的。 **低位哈希值:**是用来确定,当前的数据存在了哪个bucke...










