1. TCP 和 UDP 区别¶
可以这样说:
TCP 是面向连接、可靠传输的协议。发送前要三次握手,保证双方状态同步。它有确认机制、重传机制、流量控制和拥塞控制,保证数据不丢、不乱序。
UDP 是无连接的,不保证可靠性,也没有重传机制。发了就发了,不确认,也不保证顺序。
所以:
- TCP 适合对可靠性要求高的场景,比如登录、支付
- UDP 适合实时性高的场景,比如音视频、直播、游戏
一句话总结:
TCP 更安全更稳,但开销大
UDP 更快更轻,但可能丢包
2. DNS 是什么¶
DNS 本质是把域名解析成 IP 地址。
流程大概是:
-
浏览器先查本地缓存
-
再查系统缓存
-
再查本地 DNS 服务器
-
最后逐级向根域名服务器查询
它用的是 UDP,因为查询报文小,而且对时延要求高。
3. 为什么要三次握手¶
可以这样说:
三次握手的核心是“确认双方都具备收发能力”。
第一次:客户端说,我要连接
第二次:服务端说,我收到了,也同意
第三次:客户端确认,我也收到了你同意
如果只有两次,就无法确认客户端是否真的能接收数据。
三次握手的目的是:
-
同步双方初始序列号
-
确认双向通信能力
-
防止历史连接干扰
4. TCP 如何保证可靠性¶
你可以按点说:
-
序列号,保证有序
-
ACK 确认机制
-
超时重传
-
滑动窗口,做流量控制
-
拥塞控制,避免网络崩溃
核心思想:
发送方必须等确认,没确认就重发。
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. 虚拟内存¶
虚拟内存的作用:
- 让每个进程拥有独立地址空间
- 可以用磁盘空间当内存扩展
- 提高内存利用率
核心机制是:
分页 + 页表 + 页面置换算法
常见算法:
- 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. 死锁产生条件(四个)¶
必须全说出来:
- 互斥
- 请求与保持
- 不可剥夺
- 循环等待
少一个都不行。
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¶
- 请做一个简短的自我介绍,重点说明与客户端研发、移动端开发或项目实践相关的学习/实践经历。
- 你为什么应聘拼多多客户端研发工程师岗位?对“客户端研发”在用户体验中的核心价值有什么理解?
- 我对客户端研发的理解是,它是离用户最近的一层,也是体验的第一责任人。在电商场景下,首屏加载速度、滑动流畅度、交互反馈延迟,其实直接影响转化率。我很认同客户端不仅是“页面实现”,而是要在性能、资源管理、网络策略、缓存策略上做系统级优化。拼多多的业务规模大、流量高、场景复杂,对客户端架构和性能要求都很高,我希望能在这种高压环境下成长。
- 你认为客户端研发工程师的核心工作内容是什么?如何支撑电商App的用户体验与性能优化?
- 我理解客户端工程师核心有三块:第一是功能实现,把业务需求落地; 第二是架构设计,让模块解耦、可扩展; 第三是性能优化,保证流畅和稳定。在电商场景下,比如:首屏加载时间优化,列表滚动性能,图片加载策略,网络重试与缓存策略,都会直接影响用户体验。客户端不仅要写页面,还要和网络层、缓存层、线程模型配合,保证高并发场景下不卡顿、不崩溃。
- 你的专业背景(如计算机科学/软件工程)与客户端研发的关联性是什么?请具体说明知识应用场景(如数据结构在性能优化中的作用)。
- 数据结构在客户端性能优化中非常关键。比如在课程平台项目里,我用 MongoDB 索引和聚合优化查询结构,本质是用哈希索引和 B 树结构降低时间复杂度。在高并发场景下,我设计优先级队列和指数退避重试,其实是算法思想的应用。在客户端场景下,理解时间复杂度和内存占用,可以避免频繁创建对象、避免 O(n²) 操作,减少 GC 压力。计算机系统课程也让我理解了内存模型、线程调度和缓存机制,这对做性能分析和卡顿定位很有帮助。
- 你过往项目中,最能体现“架构设计能力”或“性能优化能力”的案例是什么?请描述技术难点及解决方案。
- 我觉得最能体现架构设计能力的是 DreamScope。当时面对的问题是:1. GPT 调用耗时不稳定 2. 用户请求量波动大 3. 需要区分付费和免费优先级。我做了几件事:第一,使用 BullMQ + Redis 构建分层优先级队列; 第二,引入指数退避重试,避免瞬间压力打爆外部 API; 第三,做 Redis TTL 缓存降低重复请求; 第四,引入断路器 Opossum 防止依赖服务雪崩;最终系统可以支持每日 1 万+ 请求,并且在峰值下仍然稳定。我觉得这个项目让我真正理解了高并发系统设计,而不仅仅是功能实现。
- 你如何理解“客户端性能优化”?移动端常见的性能瓶颈(如启动慢、卡顿、内存泄漏)有哪些?如何排查?
- 你认为自己的技术优势和劣势分别是什么?如何在客户端研发中发挥优势(如跨平台开发、架构设计)?
- 你对电商App的核心客户端场景(如商品详情页、购物车、支付流程)的技术实现有什么基础了解?
- 你熟练掌握哪种主流编程语言(如Java/Kotlin/Swift)?请说明在项目中如何运用该语言解决客户端开发问题。
- 你对计算机基础(数据结构、操作系统、网络)的掌握程度如何?请举例说明客户端开发中涉及的底层知识应用(如多线程调度)。