一
init和main的对比
相同点:
两个函数在定义时不能有任何的参数和返回值 Go程序自动调用
不同点:
init可以应用于任意包中,且可以重复定义多个。
main函数只能用于main包中,且只能定义一个。
make 被用来分配引用类型的内存,返回的还是这三个引用类型本身,make常用。
new 被用来分配除了引用类型的所有其他类型的内存,返回的是指向类型的指针。new不常用。
闭包的特点:
让外部访问函数内部变量成为可能;
局部变量会常驻在内存中;
可以避免使用全局变量,防止全局变量污染;
有一块内存空间被长期占用,而不被释放,会造成内存泄漏
接口:泛型 多态 隐藏具体实现
反射:运行时动态的获取变量类型信息和值信息的机制
GC原理
1首先创建三个集合:白、灰、黑。
2新创建的对象放入白色集合中。
3然后从根节点开始遍历所有对象,把遍历到的对象从白色集合放入灰
4之后遍历灰色集合,将灰色对象_引用的对象_从白色集合放入灰色集合,
5之后将此灰色对象放入黑色集合
6之后遍历灰色集合,将黑色对象_引用的对象_从灰色集合放入黑色集合,
7通过write-barrier(写屏障)检测对象有变化,直到灰色对象中没有对象
8收集所有白色对象垃圾
9一次垃圾回收完成后,将黑色集合变色成白色集合,会进一步gc操作,依此循环
GC优化
1.减少对象
2.少用+
3.避免string和[]byte转化 可以用 strings.Join代替
gc触发条件
1.超过内存大小阈值
2.达到定时时间
内存管理算法TCMalloc
并发模型CSP
调度模型GPM
G: 表示Goroutine,G并非执行体,每个G需要绑定到P才能被调度执行。
P: Processor, 表示逻辑处理器, G和M的调度对象
G只有绑定到P(在P的local runq中)才能被调度,P给M分配任务
M: Machine,对内核级线程的封装,数量对应真实的CPU数(真正干活的对象)
elk的match是拆分匹配
term是精确匹配
二
channel线程安全吗
是安全的
HashMap 线程安全原因
不安全
1.同时添加相同的元素可能会发生碰撞覆盖2.同时对数组进行扩容会有数据丢失
HashMap 不安全解决
Hashtable
ConcurrentHashMap
Synchronized Map
slice与arr区别
● 切片是指针类型,数组是值类型
● 数组的长度是固定的,而切片不是(切片是动态的数组)
● 切片比数组多一个属性:容量(cap)
● 切片的底层是数组
读写锁或者互斥锁读的时候能写
Go中读写锁包括读锁和写锁,
多个读线程可以同时访问共享数据;
写线程必须等待所有读线程都释放锁以后,才能取得锁;
同样的,读线程必须等待写线程释放锁后,才能取得锁,也就是说读写锁要确保的是如下互斥关系,
可以
同时读,
读-写,写-写都是互斥的。
Channel是同步的还是异步的.
Channel是异步进行的。
channel存在3种状态:
nil,未初始化的状态,只进行了声明,或者手动赋值为nil
active,正常的channel,可读或者可写
close,已关闭,千万不要误认为关闭channel后,channel的值是nil
实现消息队列(多生产者,多消费者)
package main
import (
"fmt"
"time"
)
func producer(pname string, ch chan int) {
for i := 0; i < 4; i++ {
fmt.Println("producer--", pname, ":", i)
ch <- i
}
}
func consumer(cname string, ch chan int) {
//可以循环 for i := range ch 来不断从 channel 接收值,直到它被关闭。
for i := range ch {
fmt.Println("consumer-----------", cname, ":", i)
}
}
func main() {
//用channel来传递"产品", 不再需要自己去加锁维护一个全局的阻塞队列
ch := make(chan int)
go producer("生产者1", ch)
go consumer("消费者1", ch)
time.Sleep(10 * time.Second)
close(ch)
}
孤儿进程,僵尸进程
孤儿进程:一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被
init进程(进程号为1)所收养,并由init进程对它们完成状态收集工作。
僵尸进程:一个进程使用fork创建子进程,如果子进程退出,而父进程并没有调用wait或waitpid获取子进程的
状态信息,那么子进程的进程描述符仍然保存在系统中。这种进程称之为僵死进程。
tcp与udp区别
tcp传输的是数据流,而udp是数据包,tcp会进过三次握手,udp不需要
TCP三次握手四次挥手
https://blog.csdn.net/fujian9544/article/details/100045552
三
网址题目
Golang面试题解析_GoRustNeverStop的博客-CSDN博客
做的答案
1.defer后入先出 panic在defer之后进行执行
原因出现panic恐慌时候,会先按照defer的后入先出的顺序执行,最后才会执行panic。
2.结构体中套入一个结构体就实现了继承
3.地址与变量
第一个:go func中i是外部for的一个变量,地址不变化,遍历完成后,最终i=10, 故go func执行时,i的值始终是10。过程中的i对应的地址与协程中的地址不是一个,所以不对应。
第二个:go func中i是函数参数,与外部for中的i完全是两个变量。 尾部(i)将发生值拷贝,每次拷贝一个新值,使用过程中的量
func main() {
runtime.GOMAXPROCS(1)
wg := sync.WaitGroup{}
wg.Add(20)
for i := 0; i < 10; i++ {
go func() {
fmt.Println("A: ", i)
wg.Done()
}()
}
for i := 0; i < 10; i++ {
go func(i int) {
fmt.Println("B: ", i)
wg.Done()
}(i)
}
wg.Wait()
}
5.select
- select 中只要有一个case能return,则立刻执行。
- 当如果同一时间有多个case均能return则伪随机方式抽取任意一个执行。
- 如果没有一个case能return则可以执行”default”块。
6.defer
package main
// 在这个例子中,我们将看到如何使用 Go 协程和通道实现一个工作池 。
import "fmt"
func calc(index string, a, b int) int {
ret := a + b
fmt.Println(index, a, b, ret)
return ret
}
func main() {
a := 1
b := 2
defer calc("1", a, calc("10", a, b))
a = 0
defer calc("2", a, calc("20", a, b))
b = 1
}
// 10 1 2 3
// 20 0 2 2
// 2 0 2 2
// 1 1 3 4
// defer是一个栈的结构 前面的就是进来了 数值已经锁定了
7.make
func main() {
s := make([]int, 5)
s = append(s, 1, 2, 3)
fmt.Println(s)
}
[0 0 0 0 0 1 2 3]
s := make([]int, 0)
s = append(s, 1, 2, 3)
fmt.Println(s)
//[1 2 3]
四
题目
Golang - 面试知识点小结_赵萱婷的博客-CSDN博客
做的
1.格式输出
- %T 类型
- %t 布尔
- %d 10进制整数
- %x 16进制整数
- %f 浮点数
- %s 字符串
2.接口
一个类如果实现了一个接口的所有函数,那么这个类就实现了这个接口,即这个类的对象可以使用接口的方法。
3.init
包的init 是初始化函数,在包引入的时候就会调用,一个包可以写多个init函数。
init 函数先于main函数自动执行,不能被其他函数调用;
init 函数没有输入参数、返回值;
每个包可以有多个init函数;
同一个包的init执行顺序,golang没有明确定义,编程时注意不要依赖这个执行顺序,不同包的init函数的执行顺序依照包导入的依赖关系决定执行顺序。
4.执行顺序
5.多参数函数
6.类型转换
类型(变量)
7.引用
获取指针类型的所指向的值, 可以使用: “*”取值符号。
比如 var p *int, 使用p获取p指针,slice、map、chan都是引用类型。
内置函数 new 计算类型大小,为其分配零值内存,返回指针。而 make 会被编译器翻译 成具体的创建函数,由其分配内存和初始化成员结构,返回对象而非指针。
8.切片
9.类成员函数
10.接口
如果两个接口有相同的方法列表,那么他们就是等价的,可以相互赋值。
11.锁
当一个goroutine(协程)获得了Mutex后,其他gorouline(协程)就只能乖乖的等待,除非该gorouline释放了该Mutex
RWMutex在 读锁 占用的情况下,会阻止写,但不阻止读
RWMutex在 写锁 占用情况下,会阻止任何其他goroutine(无论读和写)进来,整个锁相当于由该goroutine独占
12.通道
如果给一个 nil 的 channel 发送数据,会造成永远阻塞
如果从一个 nil 的 channel 中接收数据,也会造成永久爱阻塞
给一个已经关闭的 channel 发送数据, 会引起 pannic
从一个已经关闭的 channel 接收数据, 如果缓冲区中为空,则返回一个零值
无缓冲的 channel是同步的,而有缓冲的channel是非同步的。
13.通道关闭
通道关闭后,如果通道里面仍然有值,就会继续读取,直到没有值,读取到false
给一个关闭的通道传递值会引起panic
如果给一个 nil 的 channel 发送数据,会造成永远阻塞,靠协程进行疏通
如果从一个 nil 的 channel 接收数据,会造成永久阻塞,靠协程进行疏通
14.断言类型
x.(T)被称为断言类型,这里x表示一个接口的类型和T表示一个类型
15.继承
在一个结构体里面加一个架构体,就是继承
16.switch
go 当中switch语句和其他语言类似,只是有一个特殊的地方,switch后面可以不跟表达式
17.make与new
new返回的是指针
make用于slice map channel初始化,返回实例
18异同Printf(),Sprintf(),FprintF()
Printf()格式化输出到屏幕
Sprintf()格式化输出到变量
FprintF()输出到文件
19.数组与切片
数组定长值传递长度也是类型的一部分
切片变长引用传递 make初始化
20.数组与切片的传递
数组是值传递,只能硬改值,如果是调用函数在函数内改变值的话,就不会达到目的。
切片是引用值,可以硬改,如果调用函数在函数内部的变化,也会反映到函数外部,它意味着[]切片类型默认使用了*切片。
21.runtime.GOMAXPROCS(逻辑CPU数量)
runtime.GOMAXPROCS 的作用是:调整并发的运行性能。 runtime.Gosched()
<1: 不修改任何数值。
=1: 单核心运行。
>1: 多核心运行。
22.defer执行顺序
23.切片
24.异步全局与局部变量
均为输出10, 第一个go func中i是外部for的一个变量,地址不变化。遍历完成后,最终i=10。
故go func执行时,i的值始终是10。
25.接口
结构体的空不是空 不等于nil
package main
import (
"fmt"
)
type People interface {
}
type Student struct {
}
func live() People {
// 这个是结构体类型的
var stu *Student
return stu
// 这个是接口类型的
var stu People
return stu
}
func main() {
var a People
fmt.Println(live())
fmt.Println(a)
if a == nil {
fmt.Println("A")
} else {
fmt.Println("B")
}
if live() == nil {
fmt.Println("C")
} else {
fmt.Println("D")
}
b:=live()
if b == nil {
fmt.Println("E")
} else {
fmt.Println("F")
}
}
// <nil>
// <nil>
// A
// D
// F
17.创建
slice chanel map在声明类型之后,还要进行make
var与:矛盾,有了var就不需要: 有了冒号就不需要var了
array make([长度]类型) 赋值 a[1] 或者{}
slice make([]类型,长度,容量) 赋值 a[1]或者{}
map make(map[类型]类型) 赋值 mapp[键值]=值
channel make(chan 类型) 赋值 ch<-值