Skip to content

1. TCP 和 UDP 区别

可以这样说:

TCP 是面向连接、可靠传输的协议。发送前要三次握手,保证双方状态同步。它有确认机制、重传机制、流量控制和拥塞控制,保证数据不丢、不乱序。

UDP 是无连接的,不保证可靠性,也没有重传机制。发了就发了,不确认,也不保证顺序。

所以:

  • TCP 适合对可靠性要求高的场景,比如登录、支付
  • UDP 适合实时性高的场景,比如音视频、直播、游戏

一句话总结:

TCP 更安全更稳,但开销大
UDP 更快更轻,但可能丢包


2. DNS 是什么

DNS 本质是把域名解析成 IP 地址。

流程大概是:

  • 浏览器先查本地缓存

  • 再查系统缓存

  • 再查本地 DNS 服务器

  • 最后逐级向根域名服务器查询

它用的是 UDP,因为查询报文小,而且对时延要求高。


3. 为什么要三次握手

可以这样说:

三次握手的核心是“确认双方都具备收发能力”。

第一次:客户端说,我要连接
第二次:服务端说,我收到了,也同意
第三次:客户端确认,我也收到了你同意

如果只有两次,就无法确认客户端是否真的能接收数据。

三次握手的目的是:

  • 同步双方初始序列号

  • 确认双向通信能力

  • 防止历史连接干扰


4. TCP 如何保证可靠性

你可以按点说:

  1. 序列号,保证有序

  2. ACK 确认机制

  3. 超时重传

  4. 滑动窗口,做流量控制

  5. 拥塞控制,避免网络崩溃

核心思想:

发送方必须等确认,没确认就重发。


5. 慢启动

慢启动是拥塞控制的一部分。

一开始发送窗口很小,比如 1 个 MSS
每收到一个 ACK,窗口翻倍增长
直到到达阈值后,进入线性增长

目的:

避免刚开始就把网络打爆。


6. select / poll / epoll 区别

这是高频题。

select:

  • 有 fd 数量限制
  • 每次都要遍历所有 fd
  • 效率低

poll:

  • 没有数量限制
  • 还是要遍历

epoll:

  • 事件驱动
  • 内核维护就绪队列
  • 不需要遍历所有 fd
  • 支持边缘触发

总结:

select/poll 是“遍历型”
epoll 是“回调型”


7. 多线程如何保证线程安全

可以这样讲:

核心是避免多个线程同时修改共享数据。

方法有:

  • 加锁(mutex)
  • synchronized
  • 读写锁
  • CAS 原子操作
  • 使用线程安全容器

原则是:

尽量减少共享
缩小锁粒度
避免死锁


8. 读写共享资源用什么锁

如果读多写少:

用读写锁(ReadWriteLock)

读线程可以并发
写线程独占

如果写多:

普通互斥锁即可。


9. 程序崩溃如何排查

面试可以说:

第一步看 crash log
第二步看堆栈信息
第三步看是否空指针、数组越界
第四步用日志定位
第五步用 core dump + gdb 分析

除了打断点,还可以:

  • 日志埋点
  • 性能分析工具
  • 内存分析工具
  • 抓包分析

10. MMU 是什么

MMU 是内存管理单元。

它负责:

把虚拟地址转换为物理地址。

每个进程看到的是独立的虚拟地址空间,MMU 通过页表映射到真实物理内存。


1️1. 虚拟内存

虚拟内存的作用:

  1. 让每个进程拥有独立地址空间
  2. 可以用磁盘空间当内存扩展
  3. 提高内存利用率

核心机制是:

分页 + 页表 + 页面置换算法

常见算法:

  • LRU
  • FIFO

12. 虚函数

可以这样说:

虚函数是 C++ 里支持运行时多态的一种机制。
当父类的函数被声明为 virtual,子类重写之后,通过父类指针或引用调用时,会执行子类版本。

核心是:
运行时动态绑定,而不是编译期绑定。


13. 虚函数表(vtable)

你可以讲:

编译器在有虚函数的类中维护一个虚函数表。
对象内部会有一个指针指向这个表。
调用虚函数时,实际上是通过这个指针找到对应函数地址再调用。

所以它实现的是:

“运行时根据对象真实类型查表调用”。


14. 虚继承

虚继承是为了解决菱形继承问题。

比如:

A
B 继承 A
C 继承 A
D 同时继承 B 和 C

如果不用虚继承,D 会有两份 A。
用了 virtual 继承后,D 只保留一份 A。


15. 多态是什么

一句话:

多态就是同一个接口,不同实现。

在 C++ 里,多态一般是通过:

  • 继承

  • 虚函数

  • 父类指针指向子类对象

实现的。


16. 系统是什么

这个有点抽象,但面试一般想听:

系统是多个模块协作完成目标的整体。
它通常包括输入、处理、输出,以及资源管理。

如果是操作系统角度,可以说:

操作系统负责管理硬件资源,提供进程调度、内存管理、文件系统等功能。


17. 进程和线程区别

标准回答:

进程是资源分配的基本单位。
线程是调度执行的基本单位。

进程有独立地址空间。
线程共享进程内存。

线程切换成本低于进程。


18. 死锁产生条件(四个)

必须全说出来:

  1. 互斥
  2. 请求与保持
  3. 不可剥夺
  4. 循环等待

少一个都不行。


19. 怎么避免死锁

可以从破坏条件入手:

  • 按顺序申请锁
  • 一次性申请所有资源
  • 使用超时机制
  • 减少锁粒度

20. 手撕 LRU

面试标准答案:

用双向链表 + 哈希表。

哈希表 O(1) 找节点
双向链表维护访问顺序

访问时:

  • 移动到头部
  • 超容量删除尾部

21. 如果要求数组下标写法优化

说明他们想考你:

“用数组模拟双向链表”。

你可以说:

  • 用数组存 next 和 prev

  • 用 index 代替指针

  • 减少 map 和对象分配开销


22. 用 map + priority queue 的问题

priority_queue 不能 O(1) 删除中间元素
更新访问顺序会有问题
时间复杂度会变成 O(log n)

LRU 正确复杂度是 O(1)。

Behavioral Questions

  1. 请做一个简短的自我介绍,重点说明与客户端研发、移动端开发或项目实践相关的学习/实践经历。
  2. 你为什么应聘拼多多客户端研发工程师岗位?对“客户端研发”在用户体验中的核心价值有什么理解?
    • 我对客户端研发的理解是,它是离用户最近的一层,也是体验的第一责任人。在电商场景下,首屏加载速度、滑动流畅度、交互反馈延迟,其实直接影响转化率。我很认同客户端不仅是“页面实现”,而是要在性能、资源管理、网络策略、缓存策略上做系统级优化。拼多多的业务规模大、流量高、场景复杂,对客户端架构和性能要求都很高,我希望能在这种高压环境下成长。
  3. 你认为客户端研发工程师的核心工作内容是什么?如何支撑电商App的用户体验与性能优化?
    • 我理解客户端工程师核心有三块:第一是功能实现,把业务需求落地; 第二是架构设计,让模块解耦、可扩展; 第三是性能优化,保证流畅和稳定。在电商场景下,比如:首屏加载时间优化,列表滚动性能,图片加载策略,网络重试与缓存策略,都会直接影响用户体验。客户端不仅要写页面,还要和网络层、缓存层、线程模型配合,保证高并发场景下不卡顿、不崩溃。
  4. 你的专业背景(如计算机科学/软件工程)与客户端研发的关联性是什么?请具体说明知识应用场景(如数据结构在性能优化中的作用)。
    • 数据结构在客户端性能优化中非常关键。比如在课程平台项目里,我用 MongoDB 索引和聚合优化查询结构,本质是用哈希索引和 B 树结构降低时间复杂度。在高并发场景下,我设计优先级队列和指数退避重试,其实是算法思想的应用。在客户端场景下,理解时间复杂度和内存占用,可以避免频繁创建对象、避免 O(n²) 操作,减少 GC 压力。计算机系统课程也让我理解了内存模型、线程调度和缓存机制,这对做性能分析和卡顿定位很有帮助。
  5. 你过往项目中,最能体现“架构设计能力”或“性能优化能力”的案例是什么?请描述技术难点及解决方案。
    • 我觉得最能体现架构设计能力的是 DreamScope。当时面对的问题是:1. GPT 调用耗时不稳定 2. 用户请求量波动大 3. 需要区分付费和免费优先级。我做了几件事:第一,使用 BullMQ + Redis 构建分层优先级队列; 第二,引入指数退避重试,避免瞬间压力打爆外部 API; 第三,做 Redis TTL 缓存降低重复请求; 第四,引入断路器 Opossum 防止依赖服务雪崩;最终系统可以支持每日 1 万+ 请求,并且在峰值下仍然稳定。我觉得这个项目让我真正理解了高并发系统设计,而不仅仅是功能实现。
  6. 你如何理解“客户端性能优化”?移动端常见的性能瓶颈(如启动慢、卡顿、内存泄漏)有哪些?如何排查?
  7. 你认为自己的技术优势和劣势分别是什么?如何在客户端研发中发挥优势(如跨平台开发、架构设计)?
  8. 你对电商App的核心客户端场景(如商品详情页、购物车、支付流程)的技术实现有什么基础了解?
  9. 你熟练掌握哪种主流编程语言(如Java/Kotlin/Swift)?请说明在项目中如何运用该语言解决客户端开发问题。
  10. 你对计算机基础(数据结构、操作系统、网络)的掌握程度如何?请举例说明客户端开发中涉及的底层知识应用(如多线程调度)。