我们又更新了一波大厂面经!

简介: 我们又更新了一波大厂面经!

Slice扩容

  1. slice切片扩容机制?为什么不一直用2倍扩容?

go1.18版本之后,slice扩容使用了更加平滑的方式,不再使用1024作为临界点,而是使用threshold作为临界点(threshold 设定为256)。 当slice容量 < threshold 时,每次扩容为原来的两倍。

当slice容量 > threshold 时,每次增加 (oldcap + 3*threshold)*3/4的容量。

如果每次扩容都用两倍扩容,那这是一个2的指数级增加,后面扩容会扩非常大,浪费很大的空间。

内存分配

  1. Go内存分配机制?多级缓存?组件?(mspan/mcache/mcentral...)

Go 内存会自己管理,释放的内存不会立马归还给操作系统,而是尽量对内存进行复用,减少和操作系统的沟通(涉及到内核态和用户态的转换,消耗较大)。

Go内存管理非常高效,每一个线程M独享一个mcache,在申请内存时优先从mcache上获取,如果mcache没有足够内存,则向mcentral获取内存到mcache中,如果mcentral中也没有足够的内存则向mheap申请,如果mheap也没有足够的内存的话就向操作系统申请了(mcache->mcentral->mheap->OS)。

高效体现在从mcache获取内存和释放都是无锁的,速度很快,向mcentral和mheap申请内存虽然需要竞争锁,但是mcentral 和mheap通过 span class 进行分类(设置桶锁而不是一把大锁),锁的粒度更小,申请内存时都是通过span class 找到对应的桶获取内存,锁竞争会减少很多,性能也得到了提升。

Go垃圾回收 GC原理

  1. go垃圾回收?三色标记法?读写屏障?STW?

go1.18之后垃圾回收采用三色标记法和混合写屏障。

三色标记法:

将对象标记为白色,灰色或黑色。

白色:不确定对象(默认色);黑色:存活对象。灰色:存活对象,子对象待处理。

标记开始时,先将所有对象加入白色集合(需要STW)。首先将根对象标记为灰色,然后将一个对象从灰色集合取出,遍历其子对象,放入灰色集合。同时将取出的对象放入黑色集合,直到灰色集合为空。最后的白色集合对象就是需要清理的对象。

这种方法有一个缺陷,如果对象的引用被用户修改了,那么之前的标记就无效了。因此Go采用了写屏障技术,当对象新增或者更新会将其着色为灰色。 一次完整的GC分为四个阶段:

  1. 准备标记(需要STW),开启写屏障。
  2. 开始标记
  3. 标记结束(STW),关闭写屏障
  4. 清理(并发)

基于插入写屏障和删除写屏障在结束时需要STW来重新扫描栈,带来性能瓶颈。混合写屏障分为以下四步:

  1. GC开始时,将栈上的全部对象标记为黑色(不需要二次扫描,无需STW);
  2. GC期间,任何栈上创建的新对象均为黑色
  3. 被删除引用的对象标记为灰色
  4. 被添加引用的对象标记为灰色

总而言之就是确保黑色对象不能引用白色对象,这个改进直接使得GC时间从 2s降低到2us。

CSP并发模型

  1. channel使用场景?CSP思想?优势?

在并发编程中,两个goroutine之间使用channel进行通信。

CSP思想是指通过通信来共享内存,而不是通过共享内存来实现通信。

CSP思想优势是使用非常灵活,缺点是容易造成死锁。

互斥锁和读写锁

  1. go的锁有哪些?互斥锁的读写锁的区别?

互斥锁和读写锁。

互斥锁是有一个goroutine获取到互斥锁后,其他goroutine必须要等待锁被释放才能获取到锁。

而读写锁是可以允许多个goroutine获取到读锁执行读操作,但是写锁被获取了就和互斥锁一样了,其他goroutine必须等待写锁释放才能获取到读锁或者写锁。

sync

  1. sync包还有什么?sync.map介绍一下?如何保证并发安全?

sync.waitgroup, sync.map ,sync.Lock, sync.RWLock,sync.Pool....

sync.map 底层有两个map,一个是read,一个是dirty, 在读操作时优先在read中查找,read没有就去dirty中查找,写操作时如果read中有则利用CAS机制尝试更新value值,read中没有则写入 dirty 中。在 misses >= len(dirty)时,同步read 和 dirty的数据。

在read中不需要加锁,在dirty中需要加锁

sync.map通过read和dirty实现读写分离来减少锁时间来提高并发效率

协程池

  1. sync.Pool?了解线程池吗?优势亮点?未工作的线程如何等待?协程池?

sync.Pool 其实就是一个线程安全的对象池,用于保存和复用临时对象,在大批量申请和释放相同类型的临时对象时使用 sync.Pool 可以减少很多内存分配和回收操作,减小GC压力。

线程池是一种并发编程的技术,可以管理和复用线程,提供一种高效的方式来处理并发。

线程池优势亮点:

  1. 提高性能,线程池通过复用线程,减少线程频繁地创建和销毁,避免线程的频繁创建和销毁的开销。
  2. 控制并发度,线程池可以控制并发任务的数量,通过设计线程池的大小来控制并发度,避免激烈的锁竞争导致系统性能的下降。
  3. 提高响应速度, 线程池可以提前创建好线程,在任务到来时可以立即处理任务,提高系统的响应速度。
  4. 资源管理,线程池可以更好的对线程进行调度和管理,避免线程资源的浪费。

未工作的线程可以通过以下方式等待:

  1. 使用条件变量:线程可以使用条件变量来等待某个条件的发生。当线程需要等待时,它可以调用条件变量的等待函数,将自己置于等待状态,直到条件满足时被唤醒。
  2. 使用信号量:线程可以使用信号量来进行等待操作。线程在需要等待时,可以调用信号量的等待操作,将自己阻塞,直到其他线程释放信号量时被唤醒。
  3. 使用锁和条件变量组合:线程可以使用锁和条件变量的组合来实现等待操作。线程在需要等待时,可以先获取锁,然后检查条件是否满足,如果条件不满足,则调用条件变量的等待函数将自己置于等待状态,直到条件满足时被唤醒。

协程池和线程池基本差不多,可以更好地管理和复用协程,提高系统的性能和资源利用率。

协程泄露

  1. 防止Go协程泄露/未关闭?(waitgroup,context)

通过管道channel通知关闭,使用waitgroup监控协程全部退出,使用contex上下文来设置超时或者手动cancle关闭协程

select的用法?执行顺序?

select{
 case <-ctx.Done():
return
 case <-
 default:
}

执行顺序是随机的

map

  1. 什么可以作为map的键?结构体可以吗?

实现了 == 操作的可以比较的就可以作为map的键(基本数据类型、数组等),结构体中如果所有字段都可以比较那么久可以作为键,否则不行。

微服务

  1. 微服务之间通信方式 rpc、grpc、http等

介绍一下grpc?protobuf/json区别与优势?

GRPC是一种高性能、开源的远程过程调用(RPC)框架,由Google开发并基于HTTP/2协议实现。它允许在不同的计算机之间进行跨语言和跨平台的通信,使得构建分布式系统变得更加简单和高效。

GRPC使用Protocol Buffers(简称Protobuf)作为默认的序列化机制,而不是使用JSON。

Protobuf是一种轻量级的数据交换格式,具有以下优势:

  1. 效率高:Protobuf使用二进制编码,相比于文本格式的JSON,它的编码和解码速度更快,传输的数据量更小,节省了带宽和存储空间。
  2. 可读性好:虽然Protobuf是二进制格式,但它的定义文件是可读的,易于理解和维护。相比之下,JSON是一种文本格式,可读性较好,但在大型数据结构的情况下,Protobuf的定义文件更加清晰和简洁。
  3. 跨语言支持:Protobuf支持多种编程语言,包括Java、C++、Python等,这使得在不同语言之间进行通信变得更加方便。
  4. 版本兼容性:Protobuf支持向后和向前兼容的数据格式演化,这意味着可以在不破坏现有客户端和服务端的情况下,对数据结构进行扩展和修改。

相比之下,JSON是一种常用的文本格式,具有以下特点:

  1. 可读性好:JSON使用文本格式,易于阅读和理解,对于调试和开发过程中的数据交换非常方便。
  2. 平台无关性:JSON是一种独立于编程语言的数据格式,几乎所有的编程语言都支持JSON的解析和生成。
  3. 灵活性:JSON支持动态的数据结构,可以轻松地添加、删除和修改字段,适用于一些需要频繁变动的数据。

总的来说,GRPC使用Protobuf作为默认的序列化机制,相比于JSON,Protobuf在性能、可读性和跨语言支持方面具有优势。然而,选择使用GRPC还是JSON取决于具体的应用场景和需求。

介绍一下Gorm优势?

简单易用、支持多种数据库、自动迁移、支持事务、

Redis

1.常见数据结构

答:(字符串、set、zset、hash、list...) hash和zset底层使用 skiplist 和 ziplist,同时满足两个条件时使用 ziplist,否则 skiplist

  1. 集合元素个数小于redis.conf 中 zset-max-ziplist-entries 属性的值,其默认值为128
  2. 每个集合元素大小都小于 redis.conf 中 zset-max-ziplist-value 属性的值,其默认值为 64 字节

list 底层使用了快表(quicklist),quickList 本质上是 zipList 和 linkedList 的混合体。其将 linkedList 按段切分,每一段使用 zipList 来紧凑存储若干真正的数据元素,多个 zipList 之间使用双向指针串接起来。对于每个zipList 中最多可存放多大容量的数据元素,在配置文件中通过 list-max-ziplist-size 属性可以指定。

2.zset介绍一下?zset底层借助那些数据结构实现?跳表介绍一下?查找过程?复杂度?

zset是redis中的有序集合,借助了跳表和哈希表(哈希表存储成员和对应的分数)实现有序集合,跳表是一种有序的链表结构,它通过在每个节点中维护多个指针,使得在查找和插入操作时可以跳过部分节点,从而提高了查找的效率。跳表的高度是通过随机函数决定的,因此在平均情况下,查找的时间复杂度为O(log n),其中n是跳表中的节点数量。

3.set介绍?

set 底层使用哈希表。

4.压缩列表介绍?

压缩列表的设计目标是在尽可能节省内存的同时,提供高效的插入、删除和访问操作。它通过使用连续的内存块来存储数据,减少了指针和额外的元数据开销,从而节省了内存空间。

5.渐进式rehash?

渐进式rehash是Redis在进行哈希表扩容时采用的一种策略。当哈希表需要扩容时,Redis会创建一个新的哈希表,并将原有哈希表中的数据逐步迁移到新的哈希表中。这个过程是逐步进行的,每次只迁移一小部分数据,以避免在一次性迁移过程中对系统性能造成较大的影响。

渐进式rehash的过程如下:

  1. Redis会创建一个新的空哈希表,大小是原有哈希表的两倍。
  2. Redis会将原有哈希表中的一个桶(bucket)中的键值对逐个迁移到新的哈希表中。这个迁移过程是逐个键值对进行的,而不是一次性迁移整个桶。
  3. 在每次迁移过程中,Redis会将新哈希表中对应的桶指向原有哈希表中的桶,以保持对原有哈希表的访问能力。
  4. 迁移完成后,Redis会将新哈希表设置为当前使用的哈希表,并释放原有哈希表的内存空间。

通过渐进式rehash,Redis可以在不中断服务的情况下进行哈希表的扩容。这种方式可以避免在一次性迁移过程中对系统性能造成较大的影响,因为每次只迁移一小部分数据。同时,渐进式rehash还可以保持对原有哈希表的访问能力,确保在迁移过程中数据的一致性。

相关文章
|
JavaScript
Layui数据表格嵌套文件上传按钮
Layui数据表格嵌套文件上传按钮
615 0
|
存储 SQL NoSQL
HarmonyOS学习路之开发篇—数据管理(分布式数据服务)
分布式数据服务(Distributed Data Service,DDS) 为应用程序提供不同设备间数据库数据分布式的能力。通过调用分布式数据接口,应用程序将数据保存到分布式数据库中。通过结合帐号、应用和数据库三元组,分布式数据服务对属于不同应用的数据进行隔离,保证不同应用之间的数据不能通过分布式数据服务互相访问。在通过可信认证的设备间,分布式数据服务支持应用数据相互同步,为用户提供在多种终端设备上最终一致的数据访问体验。
|
7月前
|
XML 前端开发 开发者
CSS学习知识点大全(一)
教程来源 https://app-a7illrp9pngh.appmiaoda.com/ 梳理CSS核心知识:从基础语法、四大引入方式,到选择器(基础/组合/属性/伪类/伪元素)及优先级规则;详解盒模型(标准/怪异)、属性设置与外边距折叠。助力新手构建完整体系,也为开发者提供深度参考。
|
7月前
|
人工智能
当我用openclaw打造高效生活教练
ENTP型人格常陷于思辨而行动滞后,openclaw成为其专属生活教练:基于私有数据生成简洁SOP,覆盖每日5时段、每周7主题、审美/月度等系统,将决策自动化,释放精力专注创造。✅已落地执行!
|
6月前
|
存储 缓存 搜索推荐
《QClaw备份指南:一次配置终身复用,重装系统再也不用从零开始》
本文针对QClaw用户重装系统后配置与技能全部归零的普遍痛点,深度剖析了其区别于普通软件的分布式文件存储架构,明确划分出模型配置、技能数据、上下文记忆、个人偏好四大核心备份模块。文章基于长期深度使用的实践经验,详细讲解了选择性备份关键文件、技能关联数据同步、跨平台路径适配、增量备份与多位置自动备份的具体方法,同时给出了规范的恢复顺序与避坑要点。全文无冗余理论,所有方法均经过实测验证,可帮助用户实现一次配置终身复用,彻底解决重装系统从头配置的繁琐问题。
686 2
|
12月前
|
JSON API 数据格式
蚂蚁百宝箱开放平台 ✖️ 创建知识库
调用接口可在指定空间内创建知识库,需提供唯一名称及描述,通过Authorization验证身份,成功返回知识库ID,支持JSON格式请求。
450 0
|
安全 网络安全 API
什么是软件定义安全SDSec
软件定义安全(Software Defined Security,SDSec)是一种从软件定义网络(SDN)引申而来的概念,其核心原理是将网络安全设备与其接入模式、部署方式、实现功能进行解耦。这种解耦使得底层的网络安全设备可以抽象为安全资源池中的资源,而顶层则通过软件编程的方式进行智能化、自动化的业务编排和管理,以完成相应的安全功能,实现灵活的安全防护 。
619 1
|
人工智能 自然语言处理 Cloud Native
【攻略】Bolt.diy 云端部署与应用实战:快速生成你的创意助手
随着AI应用从实验室走向大众,构建低门槛、高效率的AI助手平台成为开发者关注焦点。阿里云推出的Bolt.diy解决方案,开源灵活且部署快捷,支持函数计算FC与百炼大模型服务集成,大幅降低全栈AI应用开发难度。本文分享了实际部署Bolt.diy的全过程,并通过创建个人AI项目助理演示其强大功能。无论是生成项目计划、技术文档,还是搭建工具页面,Bolt.diy都能助力开发者快速实现创意,提升效率。文章还探讨了使用中的小问题及优化建议,适合对AI开发感兴趣的读者体验尝试。
572 10
|
算法 数据可视化 PyTorch
IoU已经out了,试试这几个变体:GIoU、DIoU和CIoU介绍与对比分析
本文探讨了目标检测中常用的交并比(IoU)及其变体,包括广义交并比(GIoU)、距离交并比(DIoU)和完全交并比(CIoU)。这些指标不仅提高了模型在处理不重叠、距离较远或形状差异大的边界框时的表现,还为模型的学习过程提供了更深入的洞察。文章详细解释了各指标的计算方法及应用场景,并提供了相应的代码示例,帮助读者更好地理解和应用这些先进的评估指标。
1624 7
IoU已经out了,试试这几个变体:GIoU、DIoU和CIoU介绍与对比分析
|
移动开发 前端开发 JavaScript
原生JavaScript+canvas实现五子棋游戏_值得一看
本文介绍了如何使用原生JavaScript和HTML5的Canvas API实现五子棋游戏,包括棋盘的绘制、棋子的生成和落子、以及判断胜负的逻辑,提供了详细的代码和注释。
681 1
原生JavaScript+canvas实现五子棋游戏_值得一看

热门文章

最新文章