今天要和大家分享的是我们训练营内部整理的腾讯校招的二面面经,之前发过一面的面经,也想学习一下的朋友可以点击这里。本次的面试重点为计算机网络、操作系统、数据结构、中间件及缓存等方面,同样,我已经把所有的问题和答案都整理好了:
堆和栈有什么区别
答案:堆和栈在多个方面存在区别。
- 内存分配方式:栈由程序自动创建和释放,用于存储函数调用时的临时变量、函数的返回地址等;堆则由程序员手动申请和释放,通常用于存储程序中需要动态分配的内存,如动态数组、对象等。
- 内存管理方式:栈按“后进先出”原则自动管理,栈中的内存管理由系统自动完成;堆需要程序员自行负责,使用完毕后必须手动释放,否则可能导致内存泄漏或其他问题。
- 内存大小:栈的容量一般较小,通常只有几百KB到几MB,具体容量由操作系统和编译器决定;堆的大小一般比栈大得多,并且可以动态扩展。
- 访问速度:栈的内存分配是系统自动完成的,所以访问速度相对堆更快;访问堆中的数据需要通过指针进行间接访问,会造成一定的时间损耗。
- 数据存储方式:堆通常用于存储动态分配的数据结构,如链表、树等;栈主要用于存储局部变量和函数调用的相关信息。
进程的进程空间是怎么样的?
答案:在Linux操作系统中,进程空间是一块虚拟内存空间,每个进程都有独立的进程空间。它主要被划分为以下几个段:
- 代码段:存放进程的可执行代码,通常是只读的,以防止程序在运行过程中意外修改自身的指令。
- 数据段:存放进程的全局变量和静态变量,包括已初始化和未初始化的数据。
- 堆段:用于动态分配内存,进程在运行过程中可以通过系统调用动态地向堆中申请内存空间,以满足程序运行时的动态内存需求。
- 栈段:存放函数调用及局部变量等信息,当函数调用时,栈会为函数的局部变量和调用信息分配内存空间,函数返回时自动释放。
进程,线程,协程的区别
答案:
- 进程:是具有一定独立功能的程序关于某个数据集合上的一次运行活动,是系统进行资源分配和调度的一个独立单位。每个进程都有自己独立的内存空间,不同进程之间的资源不会互相干扰,上下文进程间的切换开销比较大,但相对稳定安全,适用于需要高隔离性或长时间运行的任务。
- 线程:是进程的一个实体,是CPU调度和分派的基本单位,它自己基本上不拥有系统资源,可与同属一个进程的其他线程共享进程所拥有的全部资源。线程间通信主要通过共享内存,上下文切换很快,资源开销较少,但相比进程不够稳定容易丢失数据,适合需要快速切换并共享数据的任务。
- 协程:是一种用户态的轻量级线程,协程的调度完全由用户控制。协程拥有自己的寄存器上下文和栈,上下文切换非常快,允许多个任务在同一线程中异步执行,适合I/O密集型任务,但大多数协程实现是在单线程上运行的,不支持多核并行。
链表和线性表的区别
答案:线性表包括顺序表和链表等,这里主要说链表和顺序表的区别。
- 存储结构:顺序表是用一段连续的存储空间来存储数据元素,元素在内存中的物理位置是相邻的;链表是通过指针将各个节点链接起来,节点的存储位置可以是不连续的。
- 访问方式:顺序表可以随机访问,通过下标就能快速找到指定元素;链表只能从表头开始顺序访问,查找特定元素的时间复杂度较高。
- 插入和删除操作:顺序表在插入和删除元素时,可能需要移动大量元素,时间复杂度较高;链表插入和删除节点相对灵活,只需修改指针,时间复杂度较低。
有序双向链表如何高效查询?
答案:对于有序双向链表的高效查询,可以采用二分查找的思想。先找到链表的中间节点,比较要查找的元素和中间节点的值,如果相等则找到;如果要查找的元素小于中间节点的值,则在左半部分继续查找;如果大于中间节点的值,则在右半部分继续查找。每次查找都能排除一半的节点,这样可以减少比较次数,提高查询效率。不过在实现时,需要额外记录节点的前驱和后继指针,以便在查找过程中快速移动指针。
Kafka如何实现高吞吐的?
答案:Kafka实现高吞吐主要通过以下几种方式:
- 分区机制:将数据分散到多个分区中,不同的分区可以并行处理,提高了整体的处理能力。
- 批量发送:将多个小消息合并成一个大的批次进行发送,减少了网络开销和磁盘I/O次数。
- 零拷贝技术:减少了数据在内存中的拷贝次数,提高了数据传输效率。
- 异步复制:数据在主节点和从节点之间异步复制,不影响主节点的写入性能。
- 高效的磁盘存储结构:采用顺序写入磁盘的方式,相比于随机写入,大大提高了磁盘I/O的效率。
TCP和UDP的本质区别
答案:TCP和UDP是两种不同的传输层协议,它们的本质区别如下:
- 连接性:TCP是面向连接的协议,在数据传输之前需要先建立连接,数据传输完成后再释放连接;UDP是无连接的协议,发送数据之前不需要建立连接,直接将数据发送给目标主机。
- 可靠性:TCP是可靠的传输协议,通过确认、重传、排序等机制确保数据的准确可靠传输;UDP是不可靠的传输协议,不保证数据的可靠交付,数据可能会丢失、重复或乱序。
- 传输效率:由于TCP需要建立连接和进行可靠传输的机制,其传输效率相对较低;UDP没有这些额外的开销,传输效率相对较高。
- 应用场景:TCP适用于对数据传输可靠性要求较高的场景,如文件传输、电子邮件等;UDP适用于对实时性要求较高但对可靠性要求相对较低的场景,如视频直播、实时游戏等。
TCP可靠通信怎么实现的
答案:TCP通过以下机制实现可靠通信:
- 确认机制:接收方在收到数据后会向发送方发送确认消息,发送方只有收到确认消息后才会认为数据已经成功传输。如果发送方在一定时间内没有收到确认消息,会重传数据。
- 重传机制:当发送方检测到数据丢失或损坏时,会自动重传数据。常见的重传策略有超时重传、快速重传等。
- 排序机制:TCP会为每个字节分配一个序列号,接收方根据序列号对收到的数据进行排序,确保数据的顺序与发送方一致。
- 流量控制机制:通过滑动窗口机制,接收方可以告知发送方自己的接收能力,发送方根据接收方的反馈调整发送速度,避免接收方因接收数据过快而导致数据丢失。
- 拥塞控制机制:通过监测网络拥塞状况,动态调整发送方的发送窗口大小,避免网络拥塞导致数据丢失和传输效率下降。
说一说拥塞控制
答案:拥塞控制是为了避免网络拥塞而采取的一系列措施和算法。主要包括以下几种机制:
- 慢启动:在连接建立初期,发送方会以较小的拥塞窗口开始发送数据,然后逐渐增加拥塞窗口的大小,以探测网络的可用带宽。
- 拥塞避免:当拥塞窗口达到一定大小后,进入拥塞避免阶段,此时发送方会更加谨慎地增加拥塞窗口的大小,避免网络拥塞。
- 快速重传:当接收方收到乱序的数据包时,会立即向发送方发送重复的确认消息,发送方收到一定数量的重复确认消息后,会认为数据丢失并快速重传丢失的数据。
- 快速恢复:在快速重传之后,发送方会进入快速恢复阶段,适当调整拥塞窗口的大小,而不是像慢启动那样重新从较小的窗口开始发送数据,以尽快恢复数据传输。
滑动窗口的作用
答案:滑动窗口在TCP通信中有以下重要作用:
- 流量控制:接收方通过通告窗口大小告知发送方自己的接收能力,发送方根据接收方的窗口大小调整发送数据的速度,避免接收方因接收数据过快而导致数据丢失,实现了流量控制,保证了数据传输的稳定性。
- 提高传输效率:发送方可以在窗口范围内连续发送多个数据包,而不需要等待每个数据包的确认,减少了等待时间,提高了数据传输的效率。
- 可靠传输辅助:滑动窗口与确认机制相结合,发送方可以根据窗口内已发送但未确认的数据情况,及时重传丢失或损坏的数据,确保数据的可靠传输。
什么是粘包,怎么解决
答案:
- 粘包现象:在网络编程中,粘包是指发送方发送的多个数据包在接收方被粘在一起,导致接收方无法正确区分每个数据包的边界。粘包的原因主要是TCP协议的流式传输特性,以及发送方和接收方的处理速度不一致等。
解决方法:
- 定长法:发送方和接收方事先约定好每个数据包的固定长度,接收方按照固定长度来接收和解析数据包。
- 分隔符法:在每个数据包的末尾添加一个特定的分隔符,接收方通过查找分隔符来确定数据包的边界。
- 消息头法:在每个数据包的开头添加一个消息头,消息头中包含数据包的长度等信息,接收方先解析消息头,获取数据包的长度,然后再根据长度接收和解析数据包。
缓存击穿,雪崩,穿透
答案:
- 缓存击穿:指热点数据在缓存中过期或失效的瞬间,大量的并发请求直接访问数据库,导致数据库压力骤增,甚至可能导致数据库崩溃。解决方法包括设置热点数据永不过期、使用互斥锁或分布式锁来控制对数据库的访问、提前预热缓存等。
- 缓存雪崩:是指在短时间内,大量的缓存数据同时过期或失效,导致大量的请求直接访问数据库,使数据库的压力急剧增大,从而影响整个系统的性能和可用性。解决方法有设置缓存数据的过期时间时采用随机过期策略、构建多级缓存体系、对缓存进行实时监控和预警等。
- 缓存穿透:是指查询一个不存在的数据,由于缓存中也不存在该数据,所以每次请求都会直接穿透缓存访问数据库,导致数据库压力增大。解决方法有对查询结果为空的情况进行缓存、使用布隆过滤器来快速判断数据是否存在等。
缓存击穿解决方案
- 使用互斥锁:在访问数据库前加上互斥锁,如使用Redis的分布式锁,使得所有请求只有一个能获取锁并访问数据库获取数据并回设到缓存,其余请求等待,等数据写入缓存后再读取。
- 设置热点数据永不过期:对于访问频率极高的热点数据,设置其缓存永不过期,或采用适当策略动态刷新,如在数据更新时同步更新缓存。
- 分级缓存:设置一级缓存和二级缓存,当一级缓存失效时,二级缓存可以继续提供服务,避免请求直接访问数据库。
缓存雪崩解决方案
- 分散缓存失效时间:在设置缓存过期时间时加入随机偏移量,如原失效时间基础上增加1-5分钟随机值,使缓存失效时间分散,降低集体失效概率。
- 缓存预热:在系统启动或低峰期,主动加载一些关键的热点数据到缓存中,避免用户首次请求时大量缓存失效而直接访问数据库。
- 使用双重缓存机制:在Redis外部增加一层本地缓存,如Guava缓存,当Redis出现故障或大量缓存失效时,可以从本地缓存获取数据,减少对数据库的访问。
- 限流:使用限流组件,限制访问数据库的最大线程数或每秒请求数,防止数据库因瞬间高流量而宕机。
缓存穿透解决方案
- 缓存空对象:对查询结果为空的对象也进行缓存,若为集合则缓存空集合,若为单个对象则用字段标识区分,设置较短过期时间,后续若有数据再更新缓存。
- 单独过滤处理:对所有可能对应数据为空的key进行统一存放,在请求前做拦截,避免请求穿透到后端数据库,但实现相对复杂。
- 使用布隆过滤器:将所有可能存在的数据哈希到一个足够大的BitSet中,不存在的数据将会被拦截掉,从而避免了对底层存储系统的查询压力。
- 无效值加入redis:当用户查询不存在的数据时,将该key存入redis,用特殊value表示不存在的数据,但可能造成redis缓存空间浪费。
欢迎关注 ❤
我们搞了一个免费的面试真题共享群,互通有无,一起刷题进步。
没准能让你能刷到自己意向公司的最新面试题呢。
感兴趣的朋友们可以加我微信:wangzhongyang1993,备注:sf面试群。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。