stl容器总结:
- 各种容器的元素在内存中的储存方式
vector(向量):相当于数组,但其大小可以不预先指定,并且自动扩展。它可以像数组一样被操作,
由于它的特性我们完全可以将vector 看作动态数组。在创建一个vector 后,它会自动在内存中分配一块连续的
内存空间进行数据存储,初始的空间大小可以预先指定也可以由vector 默认指定,这个大小即capacity()函数的返回值。当存储的数据超过分配的空间时vector 会重新分配一块内存块,但这样的分配是很耗时的,效率非常低。deque(队列):它不像vector 把所有的对象保存在一块连续的内存块,而是采用多个连续的存储块,并且在一个映射结构中保存对这些块及其顺序的跟踪。向deque 两端添加或删除元素的开销很小,它不需要重新分配空间。
list(列表):是一个线性链表结构,它的数据由若干个节点构成,每一个节点都包括一个信息块(即实际存储的数据)、一个前驱指针和一个后驱指针。它无需分配指定的内存大小且可以任意伸缩,这是因为它存储在非连续的内存空间中,并且由指针将有序的元素链接起来。
set, multiset, map, multimap 是一种非线性的树结构,具体的说采用的是一种比较高效的特殊的平衡检索二叉树—— 红黑树结构。
- 各种容器优劣分析
- Vector:
- 优点:
A、支持随机访问,访问效率高和方便,它像数组一样被访问,即支持[ ] 操作符和vector.at()。
B、节省空间,因为它是连续存储,在存储数据的区域都是没有被浪费的,但是要明确一点vector 大多情况下并不是满存的,在未存储的区域实际是浪费的。 - 缺点:
A、在内部进行插入、删除操作效率非常低。
B、只能在vector 的最后进行push 和pop ,不能在vector 的头进行push 和pop 。
C、 当动态添加的数据超过vector 默认分配的大小时要进行内存的重新分配、拷贝与释放,这个操作非常消耗能。
- List:
- 优点:不使用连续的内存空间这样可以随意地进行动态操作,插入、删除操作效率高;
- 缺点:
A、不能进行内部的随机访问,即不支持[ ] 操作符和vector.at(),访问效率低。
B、相对于verctor 占用更多的内存。
- Deque:
- 优点:
A、支持随机访问,方便,即支持[ ] 操作符和vector.at() ,但性能没有vector 好;
B、可以在两端进行push 、pop 。
缺点:在内部进行插入、删除操作效率低。
综合:
vector 的查询性能最好,并且在末端增加数据也很好,除非它重新申请内存段;适合高效地随机存储。
list 是一个链表,任何一个元素都可以是不连续的,但它都有两个指向上一元素和下一元素的指针。所以它对插入、删除元素性能是最好的,而查询性能非常差;适合 大量地插入和删除操作而不关心随机存取的需求。
deque 是介于两者之间,它兼顾了数组和链表的优点,它是分块的链表和多个数组的联合。所以它有被list 好的查询性能,有被vector 好的插入、删除性能。 如果你需要随即存取又关心两端数据的插入和删除,那么deque 是最佳之选。
- 关联容器的特点是明显的,相对于顺序容器,有以下几个主要特点:
A. 其内部实现是采用非线性的二叉树结构,具体的说是红黑树的结构原理实现的;
B. set 和map 保证了元素的唯一性,mulset 和mulmap 扩展了这一属性,可以允许元素不唯一;
C. 元素是有序的集合,默认在插入的时候按升序排列。
基于以上特点
A. 关联容器对元素的插入和删除操作比vector 要快,因为vector 是顺序存储,而关联容器是链式存储;比list 要慢,是因为即使它们同是链式结构,但list 是线性的,而关联容器是二叉树结构,其改变一个元素涉及到其它元素的变动比list 要多,并且它是排序的,每次插入和删除都需要对元素重新排序;
B. 关联容器对元素的检索操作比vector 慢,但是比list 要快很多。vector 是顺序的连续存储,当然是比不上的,但相对链式的list 要快很多是因为list 是逐个搜索,它搜索的时间是跟容器的大小成正比,而关联容器 查找的复杂度基本是Log(N) ,比如如果有1000 个记录,最多查找10 次,1,000,000 个记录,最多查找20 次。容器越大,关联容器相对list 的优越性就越能体现;
C. 在使用上set 区别于vector,deque,list 的最大特点就是set 是内部排序的,这在查询上虽然逊色于vector ,但是却大大的强于list 。
D. 在使用上map 的功能是不可取代的,它保存了“键- 值”关系的数据,而这种键值关系采用了类数组的方式。数组是用数字类型的下标来索引元素的位置,而map 是用字符型关键字来索引元素的位置。在使用上map 也提供了一种类数组操作的方式,即它可以通过下标来检索数据,这是其他容器做不到的,当然也包括set 。(STL 中只有vector 和map 可以通过类数组的方式操作元素,即如同ele[1] 方式)。
- stl用过哪些容器?有没有看过源码?比如vector原理有没有了解,vector插入元素会发生什么?(提到allocator原理,追问二级分配器的内存池有几个空闲链表)
unordered_map和map区别,如何避免哈希冲突?
- 运行效率方面:unordered_map最高,而map效率较低但 提供了稳定效率和有序的序列。
- 占用内存方面:map内存占用略低,unordered_map内存占用略高,而且是线性成比例的。
- 需要无序容器,快速查找删除,不担心略高的内存时用unordered_map;有序容器稳定查找删除效率,内存很在意时候用map。
- map的内部实现是二叉平衡树(红黑树);hash_map内部是一个hash_table一般是由一个大vector,vector元素节点可挂接链表来解决冲突,来实现.
hash_map其插入过程是:
1)得到key
2)通过hash函数得到hash值
3)得到桶号(一般都为hash值对桶数求模)
4)存放key和value在桶内。
其取值过程是:
1)得到key
2)通过hash函数得到hash值
3)得到桶号(一般都为hash值对桶数求模)
4)比较桶的内部元素是否与key相等,若都不相等,则没有找到。
5)取出相等的记录的value。
避免哈希冲突的四种方法
1)链地址法:是将所有哈希地址为i的元素构成一个称为同义词链的单链表,并将单链表的头指针存在哈希表的第i个单元中,因而查找、插入和删除主要在同义词链中进行。 适合频繁进行插入和删除的情况。
2)开放地址法:当发生地址冲突时,按照某种方法继续探测哈希表中的其他存储单元,直到找到空位置为止。
3)再哈希法:这种方法同样是按照按某种方法探测哈希表中的其他存储单元,直到找到空位置为止。
4)建立公共溢出区:将哈希表分为基本表和溢出表两部分,凡是和基本表发生冲突的元素,一律填入溢出表。
熟悉linux的IO模型么?同步异步有何区别?同步和阻塞一回事么
数据库索引是怎么回事?用的啥数据结构?
操作系统怎么样,比如调度算法有没有了解?(说了解linux,重点讲了CFS调度算法)
进程间通信方式有哪些?熟悉哪个?
软件开发会经常用到多线程吧?你说说自旋锁怎么回事?和互斥锁有啥区别(睡眠锁与非睡眠锁)
熟悉哪些算法?说一下经典的排序算法?归并的时间和空间复杂度
Hashmap的原理,Hashmap为什么大小是2的幂次
介绍一下红黑树
Arraylist的原理
Arraylist的扩容机制
为什么arraylist扩容是1.5倍
场景题:设计判断论文抄袭的系统
堆排序的原理
基本算法
Linux命令的使用
操作系统(着重)
网络(着重)
数据库
编译技术
分布式
Linux使用
查看内存信息
free/vmstat/top/htop查看磁盘信息
iostat查看CPU信息
vmstat/top/htop查看网络信息
netstat -i 网卡信息
netstat -a 连接信息
lsof -i:PORT 查看端口占用信息proc目录的实现原理
procfs,内部使用sysctl实现
gdb调试,线程信息,线程栈info stack/bt 调用栈
info threads 线程信息
thread id 切换线程
watch expr 监控指定表达式
buffer、cache和total的含义IO写入时存放数据的内存page buffer
IO读取时存放数据的内存page cache进程和线程的区别
对于Linux内核而言,区别并不是很大。
进程的隔离级别更加高,可以使用命名空间等,IPC的代价稍大。
线程间共享进程的地址空间,所以数据交互更容易,代价也稍小,但是数据竞争更加严重,且一旦进程crash,所有线程也就不存在了。进程切换所做的事情
保存寄存器现场
切换栈空间
处理内存页(最好深入一下)
线程同步机制互斥锁、自旋锁、读写锁、条件变量
列举进程通讯的方式
PIPE、共享内存、Socket、文件、MQ等
其中共享内存最快,但是需要额外的工作才能保证数据的一致性命名空间及其效果
UTS、IPC、PID、NS、NET、USERcgroup的功能
限制CPU和内存的使用量/使用频率VFS文件系统
磁盘中数据的存放IO调度算法
为何需要IO调度?结合机械硬盘的工作原理,随机读写开销很大,因此可以通过汇总数据的方式实现顺序读写。
Anticipatory算法、Deadline算法、CFQ算法以及Noop(No Operation)算法
NOOP算法是最简单的IO调度算法,适合SSD,只需要将IO请求入队列即可。
虚拟地址、物理地址及其实现
进程中可见的地址是虚拟地址,物理地址对应真实的内存,虚拟地址通过MMU映射成物理地址,Linux采用的是4级页表(PGD、PUD、PMD、PTE),最终对应到CR3寄存器。
转换的方式为基址+“偏移”,由于页表保存在内存中,CPU访存代价过大,因此引入TLB缓存,每次映射时优先查找TLB。
网络
TCP状态图,结合握手和挥手
注意同时打开和同时关闭即可,其余结合TCP状态转移图进行理解。
异常状态的出现场景以及解决方案
大量SYN_RECV状态的连接,大量TIME_WAIT状态的连接等。
长连接和短连接
短连接:数据请求到达后即关闭连接
长链接:多次请求复用一次连接,需要清楚如何保证连接是否存活,如心跳信号等。
TIME_WAIT的等待时间,MSL的意思
MSL(Maximum Segment Lifetime)
TIME_WAIT后并不能保证最终的ACK能够安全到达对端,因此需要预留重传的时间,即等待2MSL。
可以通过SO_REUSEADDR规避2MSL的等待。
RTT如何估计
RTT(Round-Trip Time)
开启SO_REUSEADDR后可能会出现什么问题
可能接收到对端传回的FIN、RST等,造成莫名其妙的问题。
TCP如何保证其可靠性
重传机制、流量控制、拥塞控制
IP序号重合对上层的影响
由IP层负责在组包时解决,对上层无影响。
内核如何解决accept的惊群问题
使用了WQ_FLAG_EXCLUSIVE标记,确保只有一个进程会被唤醒。
用户态如何解决accept的惊群问题
多进程抢占自旋锁即可,使用pthread_spinlock或者共享内存+CAS自行实现。
进一步讨论,如何在这个阶段提供负载均衡。
数据库
悲观锁和乐观锁
SQL中如何使用锁
索引的实现方式和作用
加快数据检索的速度,但是写入时有相应的代价,所以适合读多写少的场景。
内存通常使用B+树实现。
编译相关
解释器和编译器
常见的误区:所谓变异,并非一定要求生成本地代码,只要从语言A转换到语言B,这个过程就是编译。
编译的流程
预处理-词法解析-文法解析-语义制导-抽象语法树(IR的一种)-三地址码(SSA)-优化(控制流图、数据流图)-汇编
动态链接库和静态链接库的区别
虚拟机如何重新加载脚本
关于热更新的讨论
分布式存储
redis、rockdb、leveldb的存储布局
SSD和HDD的区别使用场景
机械结构:主控+多FLASH芯片 vs 电机+磁盘+磁车
延迟:SSD的延迟至少比HDD低1个数量级
寿命:SSD的寿命主要由P/E次数决定,因此适合多读少写的环境
其实,对于顺序读写,hdd的开销也不是那么大,原因自己去计算,另外此处可以结合IO调度算法一起考察。
锁和MVCC的适用场景
视场景而定,如果数据竞争少,则优先使用MVCC,否则老老实实用锁。
HDFS的读写操作
metadata、membership的管理
etcd或者zookeeper,真的很方便哦~
副本一致性(RSM)
通过最终一致性算法,如Paxos、2PC、3PC、Raft等
主从使用push和pull的优缺点
1.进程和线程的区别
2.什么叫线程安全?举例说明
3.OSI七层模型,包括TCP,IP的一些基本知识
4.数据库的锁
5.DFS,BFS算法
6.还有一些诸如collection framework的Java基础
7、http中,get post的区别
基础知识:
算法和数据结构
数组、链表、二叉树、队列、栈的各种操作(性能,场景)
二分查找和各种变种的二分查找
各类排序算法以及复杂度分析(快排、归并、堆)
各类算法题(手写)
理解并可以分析时间和空间复杂度。
动态规划(笔试回回有。。)、贪心。
红黑树、AVL树、Hash树、Tire树、B树、B 树。
图算法(比较少,也就两个最短路径算法理解吧)
计算机网络
OSI7层模型(TCP4层)
每层的协议
url到页面的过程
HTTP
http/https 1.0、1.1、2.0
get/post 以及幂等性
http 协议头相关
网络攻击(CSRF、XSS)
TCP/IP
三次握手、四次挥手
拥塞控制(过程、阈值)
流量控制与滑动窗口
TCP与UDP比较
子网划分(一般只有笔试有)
DDos攻击
(B)IO/NIO/AIO
三者原理,各个语言是怎么实现的
Netty
Linux内核select poll epoll
数据库(最多的还是mysql,Nosql有redis)
索引(包括分类及优化方式,失效条件,底层结构)
sql语法(join,union,子查询,having,group by)
引擎对比(InnoDB,MyISAM)
数据库的锁(行锁,表锁,页级锁,意向锁,读锁,写锁,悲观锁,乐观锁,以及加锁的select sql方式)
隔离级别,依次解决的问题(脏读、不可重复读、幻读)
事务的ACID
B树、B 树
优化(explain,慢查询,show profile)
数据库的范式。
分库分表,主从复制,读写分离。
Nosql相关(redis和memcached区别之类的,如果你熟悉redis,redis还有一堆要问的)
操作系统:
进程通信IPC(几种方式),与线程区别
OS的几种策略(页面置换,进程调度等,每个里面有几种算法)
互斥与死锁相关的
linux常用命令(问的时候都会给具体某一个场景)
Linux内核相关(select、poll、epoll)
编程语言(这里只说Java):
把我之后的面经过一遍,Java感觉覆盖的就差不多了,不过下面还是分个类。
Java基础(面向对象、四个特性、重载重写、static和final等等很多东西)
集合(HashMap、ConcurrentHashMap、各种List,最好结合源码看)
并发和多线程(线程池、SYNC和Lock锁机制、线程通信、volatile、ThreadLocal、CyclicBarrier、Atom包、CountDownLatch、AQS、CAS原理等等)
JVM(内存模型、GC垃圾回收,包括分代,GC算法,收集器、类加载和双亲委派、JVM调优,内存泄漏和内存溢出)
IO/NIO相关
反射和代理、异常、Java8相关、序列化
设计模式(常用的,jdk中有的)
Web相关(servlet、cookie/session、Spring
一个url到页面全过程(让我能说多详细说多详细,最好从OSI七层的每一层去扩展)
http的请求头格式(这个真的记不太清了,只说了几个有印象的标志位)
getpost区别,post可不可以用url的方式传参。
说到了url有最大长度,就问长度有限制是get的原因还是url的原因,为什么长度会有限制,是http数据包的头的字段原因还是内容字段的原因,详细说明。(在他一步步追问下答了个差不多)
关于幂等性的详细介绍。
幂等性是http层面的问题吗,还是服务器要处理和解决的内容。(就是看你对幂等性的定性是怎么理解的)
后台服务器对于一个请求是如何做负载均衡的,有哪些策略,会出现什么样的问题,怎么解决。(说了一致性hash算法,分布式hash的特性,具体的应用场景,又非要问我知不知道这个最早在哪个公司使用的...我说这个真不知道。好像是amazon?)
说说http的缺点,无状态,明文传输。
那https是怎么做的,如何实现的?ca认证机构。
然后问我https ssl tcp三者关系,其中哪些用到了对称加密,哪些用到了非对称加密,非对称加密密钥是如何实现的。(还好我项目中涉及到了一些加密)
关于加密的私钥和公钥各自如何分配(客户端拿公钥,服务器拿私钥)
那客户端是如何认证服务器的真实身份,详细说明一下过程,包括公钥如何申请,哪一层加密哪一层解密。
java的优先级队列,如果让你设计一个数据结构实现优先级队列如何做?
用TreeMap复杂度太高,有没有更好的方法。
hash方法,但是队列不是定长的,如果改变了大小要rehash代价太大,还有什么方法?
用堆实现,那每次get put复杂度是多少(lgN)
(思想就是并不一定要按优先级排队列的所有对象,复杂度太高,但每次保证能取最大的就行,剩下的顺序不用保证,用堆调整最为合适)
在线编程题:敲一个字串匹配问题,写了常规代码。问kmp的代码思想,最后问了下正则中用的改进后的BM算法。(还有个比较新奇的Sunday算法,有兴趣的同学也可以看一下)