北京好未来面经详解

前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家:点击跳转到网站,对人工智能感兴趣的小伙伴可以点进去看看。

今天接着分享粉丝投稿的北京好未来的面经详解,希望对你有帮助:

北京好未来

面试纪要

  1. 自我介绍

  2. 被问了项目中使用的开发框架

3. 切片和数组的区别:

  • 数组的长度是固定的,在声明时就已经确定;切片的长度是可变的,可以动态增加或减少元素数量。
  • 数组是值类型,将一个数组赋值给另一个变量时会复制整个数组;切片是引用类型,赋值时只是复制了切片的引用。
  • 数组的长度是其类型的一部分,不同长度的数组属于不同类型;切片没有这个限制。

4. 切片的扩容机制:

1.7版本:如果当前容量小于1024,则判断所需容量是否大于原来容量2倍,如果大于,当前容量加上所需容量;否则当前容量乘2。

如果当前容量大于1024,则每次按照1.25倍速度递增容量,也就是每次加上cap/4。

1.8版本 :Go1.18不再以1024为临界点,而是设定了一个值为256的threshold,以256为临界点;超过256,不再是每次扩容1/4,而是每次增加(旧容量+3*256)/4;

  1. 当新切片需要的容量cap大于两倍扩容的容量,则直接按照新切片需要的容量扩容;
  2. 当原 slice 容量 < threshold 的时候,新 slice 容量变成原来的 2 倍;
  3. 当原 slice 容量 > threshold,进入一个循环,每次容量增加(旧容量+3*threshold)/4。

5. 如何创建一个通道?怎么创建 + 有缓冲通道 + 无缓冲通道 + 底层结构

  • 无缓冲通道:ch := make(chan int)
  • 有缓冲通道:ch := make(chan int, 5)
    通道的底层结构是一个包含数据存储区域、发送和接收操作的等待队列等部分的结构体。

6. GC(垃圾回收)?

垃圾回收机制是Go一大特(nan)色(dian)。Go1.3采用标记清除法 , Go1.5采用三色标记法 ,Go1.8采用三色标记法+混合写屏障

标记清除法

分为两个阶段:标记和清除

标记阶段:从根对象出发寻找并标记所有存活的对象。

清除阶段:遍历堆中的对象,回收未标记的对象,并加入空闲链表。

缺点是需要暂停程序STW(stop the world)。

三色标记法

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

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

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

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

一次完整的GC分为四个阶段:

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

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

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

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

7. 什么是内存泄漏?

在Go中内存泄露分为暂时性内存泄露和永久性内存泄露。

暂时性内存泄露

  • 获取长字符串中的一段导致长字符串未释放
  • 获取长slice中的一段导致长slice未释放
  • 在长slice新建slice导致泄漏

获取长slice中的一段导致长slice未释放:

  • 当从一个长slice中获取一段子slice时,如果没有正确处理,子slice可能会持有对原长slice的引用,导致原长slice无法被释放。
  • 这种情况下,子slice会持有原长slice的底层数组的引用,即使子slice被丢弃,原长slice的底层数组也无法被释放,造成内存泄漏。
  • 避免内存泄漏的方法是使用copy函数将子slice复制到一个新的slice中,而不是直接引用原长slice。

在长slice新建slice导致泄漏

  1. 当在一个长slice上再次使用切片操作创建一个新的slice时,新的slice会共享原长slice的底层数组,导致原长slice无法被释放。
  2. 这种情况下,新的slice会持有原长slice的底层数组的引用,即使新的slice被丢弃,原长slice的底层数组也无法被释放,造成内存泄漏。
  3. 避免内存泄漏的方法是使用copy函数将新的slice复制到一个新的slice中,而不是直接引用原长slice。

为避免这两种情况下的内存泄漏,需要注意在处理slice时避免共享底层数组的引用,而是使用copy函数创建新的slice。

确保在不需要使用的时候及时释放不再需要的内存,避免长期持有对底层数组的引用。

同时,合理使用defer语句、关闭资源、避免循环引用等方法也有助于避免内存泄漏的发生。

string相比切片少了一个容量的cap字段,可以把string当成一个只读的切片类型。获取长string或者切片中的一段内容,由于新生成的对象和老的string或者切片共用一个内存空间,会导致老的string和切片资源暂时得不到释放,造成短暂的内存泄漏.

永久性内存泄露

  • goroutine永久阻塞而导致泄漏
  • time.Ticker未关闭导致泄漏
  • 不正确使用Finalizer(Go版本的析构函数)导致泄漏

8. 避免和解决内存泄漏的方式:

  • 及时释放不再使用的资源,如手动释放内存、关闭文件描述符、取消订阅事件等。
  • 使用智能指针或具有自动内存管理功能的语言特性。
  • 进行内存泄漏检测工具的使用,定期检查和分析内存使用情况。

9. InnoDB 引擎和 MyISAM 引擎的区别:

  • 事务支持:InnoDB 支持事务,MyISAM 不支持。
  • 锁机制:InnoDB 支持行级锁,MyISAM 支持表级锁。
  • 数据完整性:InnoDB 保证数据的完整性和一致性,MyISAM 相对较弱。
  • 崩溃恢复:InnoDB 具有较好的崩溃恢复能力,MyISAM 较差。
  • 性能:MyISAM 在读取数据时性能较高,尤其在大量读取操作时;InnoDB 在写入和并发操作时性能较好。

10. 索引多的后果及索引的优缺点:

优点:

  • 提高数据查询速度,快速定位所需数据。

  • 可以保证数据的唯一性,约束数据的完整性。

缺点:

  • 增加数据插入、更新和删除的时间成本,因为每次操作都需要更新索引。
  • 过多的索引会增加存储空间的消耗。

11. Redis 的持久化方式:

RDB持久化(全量备份)

RDB持久化是指在指定时间间隔内将内存中的数据集快照写入磁盘。实际上fork子线程,先将数据集写入临时文件,写入成功后,在替换之前的文件,用二进制压缩文件,RDB是Redis默认的持久化方式,会在对应目录下生产一个dump.rdb文件,重启会通过加载dump.rdb文件恢复数据

RDB优点:

  1. 方便持久化:只有一个dump.rdb文件;
  2. 容灾性好:一个文件可以保存到安全的磁盘;
  3. 性能好:fork子线程来完成写操作,主线程继续处理命令;
  4. 效率高:如何数据集偏大,RDB启动效率比AOF高

RDB缺点:

  1. 数据安全性低:因为RDB是每隔一段时间进行持久化,可能会造成数据丢失。
  2. 由于RDB是通过fork子线程协助完成数据持久化工作的,因此如果数据集较大时,可能会导致整个服务停止服务几百毫秒,甚至一分钟。

AOF持久化(增量备份)

AOF持久化是以日志的形式记录记录每一个增删操作然后追加到文件中。AOF的出现是为了弥补RDB备份的不足(数据不一致性)。

与RDB持久化相比,AOF的持久化实时性更好。

**AOF的备份策略:**Redis的配置文件中存在三种不同的AOF持久化方式:

  1. appendfsync always:每次有数据修改发生时都会同步。
  2. appendfsync everysec:每秒同步一次
  3. appendsync no:让操作系统决定何时进行同步。

AOF优点:

  1. AOF实时性哈好,数据安全性更高;
  2. AOF通过append模式写文件,即使中途服务器宕机,也可以通过redis-check-aof工具解决数据一致性问题。
  3. AOF机制的rewrite模式(文件过大会对命令进行合并重写),可以删除其中某些命令(比如误操作的命令)

AOF缺点:

  1. AOF文件比RDB文件大,且恢复慢;
  2. 根据同步策略的不同,AOF在运行效率上往往会慢于RDB。

两者结合

将 RDB 和 AOF 合体使用,这个方法是在 Redis 4.0 提出的,该方法叫混合使用 AOF 日志和内存快照,也叫混合持久化。

混合持久化工作在 AOF 日志重写过程。

当开启了混合持久化时,在 AOF 重写日志时,fork 出来的重写子进程会先将与主线程共享的内存数据以 RDB 方式写入到 AOF 文件,然后主线程处理的操作命令会被记录在重写缓冲区里,重写缓冲区里的增量命令会以 AOF 方式写入到 AOF 文件,写入完成后通知主进程将新的含有 RDB 格式和 AOF 格式的 AOF 文件替换旧的的 AOF 文件。

也就是说,使用了混合持久化,AOF 文件的前半部分是 RDB 格式的全量数据,后半部分是 AOF 格式的增量数据。

这样的好处在于,重启 Redis 加载数据的时候,由于前半部分是 RDB 内容,这样加载的时候速度会很快。

加载完 RDB 的内容后,才会加载后半部分的 AOF 内容,这里的内容是 Redis 后台子进程重写 AOF 期间,主线程处理的操作命令,可以使得数据更少的丢失。

12. Redis 过期淘汰策略:

  • volatile-lru:从设置了过期时间的键中,移除最近最少使用的键。
  • volatile-ttl:从设置了过期时间的键中,移除即将过期的键。
  • volatile-random:从设置了过期时间的键中,随机移除键。
  • allkeys-lru:从所有键中,移除最近最少使用的键。
  • allkeys-random:从所有键中,随机移除键。
  • noeviction:不进行淘汰,当内存使用达到上限时,新的写入操作会报错。

欢迎关注 ❤

我们搞了一个免费的面试真题共享群,互通有无,一起刷题进步。

没准能让你能刷到自己意向公司的最新面试题呢。

感兴趣的朋友们可以加我微信:wangzhongyang2024,备注:csdn面试群。

相关推荐
Pandaconda1 小时前
【C++ 面试 - 新特性】每日 3 题(六)
开发语言·c++·经验分享·笔记·后端·面试·职场和发展
赤兔马百花袍1 小时前
php转职golang第一期
golang
chanTwo_001 小时前
go--知识点
开发语言·后端·golang
悟空丶1231 小时前
go基础知识归纳总结
开发语言·后端·golang
ifanatic2 小时前
[Go]-抢购类业务方案
开发语言·后端·golang
spiker_4 小时前
使用 RabbitMQ 和 Go 构建异步订单处理系统
golang·rabbitmq·ruby
什么都没学会5 小时前
GO环境安装
开发语言·vscode·golang·安装
涛涛酱100865 小时前
前端面试实录,(回答均为个人见解)
前端·面试·原型模式
get2005 小时前
golang context介绍
开发语言·后端·golang
蒙娜丽宁5 小时前
掌握Go语言中的映射、常量与指针
开发语言·ios·golang·go