思路
不要问什么答什么 要学会扩充
比如问你go map的原理
- map 是什么
数据结构,字典,k/v 结构 - map的应用场景有哪些
快速查找、计数器、配置管理、去重、缓存实现 - map有哪些限制
无序性、非线程安全的读写 - map的key的访问
v:= mp[key]
v,ok := mp[key]
for k,v:=range mp {} - map相关原理
语言层面
通用
go
Go并发原语 groutine select channel sync包
有没有了解channel的底层结构体。
使用channel时需要注意哪些事项,以及哪些场景下会panic。
map扩容:map什么时候触发扩容,哈希冲突的解决方案是什么
go内存分配的实现原理是什么
怎么在go中并发编程下等待多个协程的结束,Add()是什么意思。
go的slice不断append时是如何分配内存的,扩容规则是什么。
defer是用来做什么的,应用场景有哪些。
多个defer执行顺序:多个defer的执行顺序是怎样的。
map和slice哪个是线程安全的,map手动加锁和sync.Map的区别是什么。
如何控制 goroutine 的生命周期(channel 的作用,context 的作用)
协程切换的时机?
sema锁是什么?mutex源码中的结构有看过吗?
正常模式和饥饿模式?
如何检测锁异常?
go vet 查看是否存在拷贝锁 race 竞争检测
olang在1.1之后引入了竞争检测机制,可以使用 go run -race 或者 go build -race来进行静态检测。
其在内部的实现是,开启多个协程执行同一个命令, 并且记录下每个变量的状态.
竞争检测器基于C/C++的ThreadSanitizer运行时库,该库在Google内部代码基地和Chromium找到许多错误。这个技术在2012年九月集成到Go中,从那时开始,它已经在标准库中检测到42个竞争条件。现在,它已经是我们持续构建过程的一部分,当竞争条件出现时,它会继续捕捉到这些错误。
竞争检测器已经完全集成到Go工具链中,仅仅添加-race标志到命令行就使用了检测器。
$ go test -race mypkg // 测试包
$ go run -race mysrc.go // 编译和运行程序
$ go build -race mycmd // 构建程序
$ go install -race mypkg // 安装程序
要想解决数据竞争的问题可以使用互斥锁sync.Mutex,解决数据竞争(Data race),也可以使用管道解决,使用管道的效率要比互斥锁高.
- 你知道 Go 条件编译吗?
Golang支持两种条件编译的实现方式:
编译标签(build tags):
编译标签由空格分隔的编译选项(options)以”或”的逻辑关系组成
每个编译选项由逗号分隔的条件项以逻辑”与”的关系组成
每个条件项的名字用字母+数字表示,在前面加!表示否定的意思
不同tag域之间用空格区分,他们是OR关系
同一tag域之内不同的tag用都好区分,他们是AND关系
每一个tag都由字母和数字构成,!开头表示条件“非”
% head headspin.go
// Copyright 2013 Way out enterprises. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build someos someotheros thirdos,!amd64
// Package headspin implements calculates numbers so large
// they will make your head spin.
package headspin
文件后缀(file postfix):
这个方法通过改变文件名的后缀来提供条件编译,这种方案比编译标签要简单,go/build可以在不读取源文件的情况下就可以决定哪些文件不需要参与编译。
文件命名约定可以在go/build 包里找到详细的说明,简单来说如果你的源文件包含后缀:_GOOS.go,那么这个源文件只会在这个平台下编译,_GOARCH.go也是如此。这两个后缀可以结合在一起使用,但是要注意顺序:_GOOS_GOARCH.go, 不能反过来用:_GOARCH_GOOS.go. 例子如下:
mypkg_freebsd_arm.go // only builds on freebsd/arm systems
mypkg_plan9.go // only builds on plan9
3. 如何实现交叉编译?
我们知道golang一份代码可以编译出在不同系统和cpu架构运行的二进制文件。go也提供了很多环境变量,我们可以设置环境变量的值,来编译不同目标平台。
GOOS: 目标平台; GOARCH: 目标架构。
# 编译目标平台linux 64位
GOOS=linux GOARCH=amd64 go build main.go# 编译目标平台windows 64位
GOOS=windows GOARCH=amd64 go build main.go
数据结构
slice
动态数组,底层依然是数组
扩容 1024 翻倍与1.25
如果你预测到切片的增长很大,可以考虑在创建切片时预先设置合适的容量,以减少内存分配和复制的次数
slice是线程安全的么?
map
基本结构介绍
桶的集合 哈希种子 旧桶 溢出桶 迁移进度字段
桶每个存8个 根据高八位 低八位 多的溢出桶中
存储是先key再value 类似数组 这样存储的好处是可以消除字节对齐带来的空间浪费
生成map的时候会预先生成一些溢出桶 存在字段中
扩容条件
- 负载因子过高:负载因子是指map中元素数量与桶数量的比值。当负载因子超过一定阈值时,map会进行扩容。默认情况下,负载因子的阈值为6.5,即当map中的元素数量超过桶数量的6.5倍时,会触发扩容。
- 溢出桶过多:
渐进式扩容 每次新建 删除等操作
是线程安全的么?map是线程不安全的
sync.map
是线程安全的么?
defer
defer顺序 里面如果panic
channel
是线程安全的么?
GC
GC 三色标记
-
如果 goroutine 一直占用资源怎么办,GMP模型怎么解决这个问题
如果有一个goroutine一直占用资源的话,GMP模型会从正常模式转为饥饿模式,通过信号协作强制处理在最前的 goroutine 去分配使用 -
如果若干个线程发生OOM,会发生什么?Goroutine中内存泄漏的发现与排查?项目出现过OOM吗,怎么解决?
线程 如果线程发生OOM,也就是内存溢出,发生OOM的线程会被kill掉,其它线程不受影响。 -
Goroutine中内存泄漏的发现与排查
go中的内存泄漏一般都是goroutine泄露,就是goroutine没有被关闭,或者没有添加超时控制,让goroutine一只处于阻塞状态,不能被GC。在Go中内存泄露分为暂时性内存泄露和永久性内存泄露。
暂时性内存泄露,string相比切片少了一个容量的cap字段,可以把string当成一个只读的切片类型。获取长string或者切片中的一段内容,由于新生成的对象和老的string或者切片共用一个内存空间,会导致老的string和切片资源暂时得不到释放,造成短暂的内存泄漏。
永久性内存泄露,主要由goroutine永久阻塞而导致泄漏以及time.Ticker未关闭导致泄漏引起。
- Go的垃圾回收算法
Go 现阶段采用的是通过三色标记清除扫法与混合写屏障GC策略。其核心优化思路就是尽量使得 STW(Stop The World) 的时间越来越短。
GC 的过程一共分为四个阶段:
栈扫描(STW),所有对象开始都是白色
从 root 开始找到所有可达对象(所有可以找到的对象),标记灰色,放入待处理队列
遍历灰色对象队列,将其引用对象标记为灰色放入待处理队列,自身标记为黑色
清除(并发)循环步骤3 直到灰色队列为空为止,此时所有引用对象都被标记为黑色,所有不可达的对象依然为白色,白色的就是需要进行回收的对象。三色标记法相对于普通标记清除,减少了 STW 时间。这主要得益于标记过程是 “on-the-fly”的,在标记过程中是不需要 STW的,它与程序是并发执行的,这就大大缩短了 STW 的时间。
写屏障: 插入屏障, 在A对象引用B对象的时候,B对象被标记为灰色。(满足强三色不变性)
删除屏障,被删除的对象,如果自身为灰色或者白色,那么被标记为灰色。(满足弱三色不变性)
混合写屏障: GC开始将栈上的对象全部扫描并标记为黑色(之后不再进行第二次重复扫描,无需STW), GC期间,任何在栈上创建的新对象,均为黑色。 被删除的对象标记为灰色。 被添加的对象标记为灰色。
go内存逃逸
面试应该从以下角度回答
- 什么是逃逸?
- 导致内存逃逸的原因是什么
- 常见的发生逃逸的情况与逃逸分析
- 如何避免
Go程序启动时发生什么
Golang 程序的运行入口是 runtime 定义的一个汇编函数。这个函数核心有三个逻辑:
第一、通过 runtime 中的 osinit、schedinit 等函数对 golang 运行时进行关键的初始化。在这里我们将看到 GMP 的初始化,与调度逻辑。
第二、创建一个主协程,并指明 runtime.main 函数是其入口函数。因为操作系统加载的时候只创建好了主线程,协程这种东西还是得用户态的 golang 自己来管理。golang 在这里创建出了自己的第一个协程。
第三、调用 runtime·mstart 真正开启调度器进行运行。
当调度器开始执行后,其中主协程会进入 runtime.main 函数中运行。在这个函数中进行几件初始化后,最后后真正进入用户的 main 中运行。
第一、新建一个线程来执行 sysmon。sysmon的工作是系统后台监控(定期垃圾回收和调度抢占)。
第二、启动 gc 清扫的 goroutine。
第三、执行 runtime init,用户 init。
第四、执行用户 main 函数。
本题:感觉只要答出来M0的创建和M0的G0是怎么初始化 runtime 环境、goroutine的生命周期的就好,再往深的地方走面试就不用面了,时间能都砸这个上面
GMP
P的数量怎么设置:在程序中通过runtime.GOMAXPROCS() 来设置
M的数量怎么设置:runtime/debug包中的SetMaxThreads函数来设置
最高能有多少个P:应该是内核数量
最高多少M:最⼤量一般默认是10000 但是内核很难支持这么多的线程数
GMP模型中协程的最长运行时间是多久:10ms
Work Stealing偷多少:
M 优先执行其所绑定的 P 的本地运行队列中的 G,如果本地队列没有 G,则会从全局队列获取,为了提高效率和负载均衡,会从全局队列获取多个 G,而不是只取一个,个数是自己应该从全局队列中承担的,globrunqsize / nprocs + 1;同样,当全局队列没有时,会从其他 M 的 P 上偷取 G 来运行,偷取的个数通常是其他 P 运行队列的一半;
groutine生命周期
其实就是回答gmp模型
java
除了 clone() 还有哪些方式可以对对象进行深拷贝?
2、java 对象的内存结构?标记字是做什么的?
3、写个单例?为何静态内部类实现的单例可以做到线程安全且可延迟加载?
4、new Hashmap<1000> 和 new Hashmap<10000> 在数据都塞满的时候有什么区别?(提示 扩容相关)
5、java 弱引用和虚引用的区别?
6、垃圾回收时标记存活对象的三色标记法原理,以及在出现漏标、错标情况时是如何解决的?
7、jvm 调优你如何做的?现象->排查过程->解决方式->不同解决方案的对比与选择
8、为何引入 JIT 编译?逃逸分析是什么?
9、多线程中的三大问题 java 是如何解决的?
10、synchronized 底层实现原理?释放锁之后如何通知其他线程获取锁?
11、讲讲 AQS?
12、synchronized 做了哪些优化?(偏向锁、轻量级锁、自旋锁、锁粗化、锁消除等)
13、LongAdder 实现原理?
Spring 启动类的注解,介绍一下
因为我项目中用到了,所以被提问了 Spring 二次开发常用的扩展点,还涉及到了 Bean 的生命周期。 BeanPostProcessor,在你项目中如何使用的
Spring 中你常用哪些注解? Autowired 实现原理
看你项目中用到了 Netty,简单介绍下吧。这里还有个 问题是问到 Netty 和 SpringBoot 整合的,但我一直都没理解她想问什么
粘包拆包问题,Netty 解决粘包拆包的 Decoder
Spring 事务了解吗,Spring 事务的注解不生效,是什么原因
Java 引用类型,强软弱虚
Java 是引用传递还是值传递
Object 类你了解哪些方法
常用 GC 算法,常用的垃圾收集器, G1 了解吗
场景题: cpu 打满且频繁 full GC,怎么解决?
有 jvm 调优的经验吗?实际工作中遇到过内存相关的问题吗?用过哪些堆栈工具调试?
Mysql 索引,数据结构为什么使用 B+ 树
索引覆盖了解吗
索引失效的场景
简单描述一下数据库的四种隔离级别以及对应的三种相关问题
MVCC + 锁 保证隔离性
造成幻读的原因了解吗,快照读、当前读。
数据库自增 ID 和 UUID 对比
HashMap 源码,数据结构,如何避免哈希冲突,对比 HashTable
HashMap 源码中,计算 hash 值为什么有一个 高 16 位 和 低 16 位异或的过程?
为什么重写 equals 还要重写 hashCode,不重写会有什么问题
ConcurrentHashMap 底层实现,扩容问题。
计算机网络
tcp和udp区别
tcp的可靠传输
网络中的三张表——ARP表、MAC表、路由表
览器寻址url过程?
dns作用 解析流程
下一跳路由转发数据包的过程?
讲讲http协议
- 特性 无状态 无连接 媒体独立 进一步到cookie seesion
- 请求响应报文:
- 请求行:方法、 url、协议版本、
- 请求头:(connection、connection-type、user-agent、content-type、gzip、encoding)
- 请求携带数据:比如page:1
- 响应报文 对比多了一个状态码
- 更进一步细化
- 不同版本的区别
0.9 get和纯网页
1.0 新增方法 mime cache
1.1 管道,keepalve
2.0 帧 二进制 头压缩(gzip和维持一个表) 多工复用 服务器主动主动推送 - post和get 以及其他方法
- 端口号
- keep-alive
- content-type
- gzip
- 不同状态码的含义
200 - 请求成功
301- 资源(网页等)被永久转移到其它URL
302-临时移动 以后客户端应该继续使用原URL
305-必须使用代理访问
400-语法错误 服务器无法理解
401-要求身份认证
403-拒绝 服务器端理解需求 但是拒绝执行
404 - 请求的资源(网页等)不存在
405-客户端请求中的方法被禁止
500 - 内部服务器错误
- 不同版本的区别
数据库
隔离级别有几种,分别会产生什么样的问题
mysql
慢查询优化
MySQL数据是怎么写的,写入的底层原理是什么,涉及到哪些主键的交互,比如innodb写入时是先写入buffer pool。
binlog同步:MySQL主节点的binlog是同步的还是异步的。
主节点崩溃:如果MySQL主节点崩溃了,数据会不会丢失。
从节点写入:主节点挂了但向客户端返回成功,怎么保证从节点数据写入进去。
innodb索引:为什么innodb索引使用B+树。
数据量很大:数据量很大达到内存放不下时怎么解决。
1、索引(为何使用 b+树而不是使用别的数据结构? 索引下推?倒排索引?)
2、事务(ACID 隔离级别 幻读如何出现的 又是如何解决?)
3、锁(给一个 sql 问这条 sql 在不同隔离级别下是如何加锁的?)
4、mvcc 机制(实现原理以及 rr 和 rc 隔离级别下实现的区别?)
5、redolog undolog binlog(会问分别是用来做什么的 有什么共同点 区别?)
6、sql 优化(选择一个适合自己业务的 sql 场景 描述清楚自己如何通过 explain 命令来分析和优化的?)
redis
redis缓存:对redis作为缓存的理解是什么,用redis缓存和本地缓存,可以用本地缓存么(答了可以但不建议,然后面试官反问维护redis的成本呢)。
redis使用场景:redis的set和list的使用场景是什么。
redis set原理:redis set的原理是什么。
4、工作中,你们的ES和Mysql之间是怎么用的;
底层数据结构有哪些?跳表实现原理?为何不用红黑树?
2、Redis 的过期策略?
3、Redis 的持久化?
4、Redis 主从、哨兵、集群工作原理?三种部署方式的区别?
5、缓存穿透、击穿、打满、雪崩出现的原因与常用解决办法
6、热 key 的解决方案(如何发现 如何优化)
消息队列
2、项目里用了Kafka,那聊一下RocketMQ和Kafka的区别;
3、介绍一下Kafka集群、副本、选举;
、Kafka 基本工作原理?
2、Kafka 为何高吞吐?
3、Kafka 消息的可靠性、顺序性是如何实现的的?
4、Kafka 的 ISR 机制?
5、Kafka 与其他 MQ 的对比与选择
分布式
分布式就不多说了,什么 base 理论,raft 协议都需要知道。另外就是分布式锁、分布式事务相关的一些知识,大家用到过的可以讲讲,比较加分,没用到过的面试官一般也不会问到。
.go-micro微服务架构的水平部署及代码实现。
如何使用micro.
如何进行服务发现。
Rpc 远程调用的流程
为什么选用 Zookeeper 作为注册中心,注册中心作用是什么
项目中的 SPI 机制,介绍一下原理以及你做了哪些改进
一致性哈希的原理,虚拟结点
项目中的序列化方案,为什么序列化,你都了解哪些常用的序列化方法。
Zookeeper 作为注册中心,假如崩溃了怎么办?这里开始连环问了
你提到了 Zookeeper 的一致性,它是如何保证的?
ZAB 协议,选举的过程,这里问的很详细
Zookeeper 是强一致性吗?
网络分区了解吗,CAP 理论
Zookeeper 如何应对网络分区的,脑裂问题了解吗,如何解决?
假如我同一时间有大量服务发布,你提到了 Zookeeper 只有主节点负责写, 怎么解决?假如主节点崩溃了,新选举出的主节点仍然没办法面对我的大流量,也崩溃了,如何解决?
MQ 的原理,你知道哪些 MQ,各自有什么特点,什么时候需要用 MQ
后端接口通用
RESTful 风格API设计规范
比如返回值 动作 等待
项目
项目困难:在项目中遇到哪些困难的问题,项目的难点是什么。
迭代器 生成器