常见面试题总结

Posted 神即道,道法自然,如来。

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了常见面试题总结相关的知识,希望对你有一定的参考价值。

微服务

1. Springboot加载配置文件顺序?

2. SpringBoot启动时都做了那些事

3. Mybatis的sql执行过程

4.SpringMVC的执行流程

5. Spring容器启动的执行过程

6. SpringIOC的过程

7. 微服务整体架构图

8. 如何拆分微服务

​ DDD领域驱动

9. 几个注册中心的比较?Eureka、Zookeeper、Nacos、Consul

10. 链路追踪和监控(如何实现全链路的业务监控?)

11. 说说springBean的一个加载流程吧,如何解决循环依赖问题

JVM

1. jvm内存结构

  • 线程共享
    • 方法区 MetaSpace
      • 运行时常量池
      • 类的信息,包括类名、参数、方法等
      • 类的常量
      • 类的静态变量
    • 堆 Heap
      • 老年代 Old Space
      • 新生代 Young Space
        • Eden -8
        • S1-1
        • S2-1
  • 线程私有的
    • 虚拟机栈
      • 本地变量表
      • 操作数栈
      • 动态链接 一些动态生成的链接地址等,类似于符号引用变直接引用
      • 返回地址(包括正常的和异常的)
    • 本地方法栈
    • 程序计数器

2. JVM垃圾回收算法,垃圾回收器,G1,CMS回收过程,虚拟机怎么保证分配内存时的线程安全

  • 标记清除
  • 标记整理
  • 标记复制

3. JVM类加载

类加载是指把.class 文件加载到jvm虚拟机的过程,主要包括以下几个步骤

  • 加载 Loading 根据类的全限定名从磁盘或者网络上加载二进制流(十六进制编码)

    此处主要是使用Java的ClassLoader 即类加载器进行加载,常用的类加载器分为以下几个部分

    • BootStrap ClassLoader 即根加载器,主要加载rt.jar的类
    • Extention ClassLoader 即扩展类加载器, 主要加载.ext下的jar
    • Application ClassLoader 即应用类加载器,加载当前classpath下的类
    • Custom ClassLoader 即自定义类加载器
  • 链接 Linking

    • 验证 Verification 验证类文件的格式,比如开头是否是cafebabe等 包括有对应的词法语法解析器
    • 准备 Preparing 给类的静态变量初始化空间并赋值初始值
    • 解析 Resovling 把一些符号引用转化为内存中的直接引用
  • 初始化 Initialization

    给类的静态变量赋值

    MethodNotFoundException、ClassNotFoundException、ClassDefNotFoundException分别发生在哪个步骤?

4. 垃圾收集器

  • Serial 新生代 使用标记复制 单线程

  • Serial Old 老年代 使用标记整理 单线程

  • ParNew 新生代 使用标记复制,基于Serial 多线程收集

  • Parallel Scavenge 新生代 使用标记复制,更关注吞吐量

  • Parallel Old 老年代 标记整理,关注吞吐量

  • CMS收集器 老年代 使用标记清楚算法 关注停顿时间,整个过程stw 两次

    • 先初始标记 stw (每一步具体做了什么???)

    • 然后并发标记

    • 重新标记 stw

    • 并发清除

      并发:用户线程和垃圾回收线程可以并行执行 成为并发

  • G1 并发收集器 1.9默认 可以设置具体的停顿时间,会更改jvm的内存区域,划分成大小相等的独立区域

    相比cms 是在cms 并发清楚的时候这一步进行筛选回收,主要是因为用户设置了停顿时间,所以只能选择性的回收。

    • 堆内存的存活率超过了50%
    • 对象的分配和晋升速度变化大
    • 垃圾回收时间长

5. JVM调优(你是怎么去做jvm调优的)

  • GC收集器:停顿时间和吞吐量

    • 停顿时间: 垃圾回收所使用的的时间
      • CMS G1 停顿时间较小 适用于web应用 并发类的收集器
    • 吞吐量: 吞吐量 = 运行用户代码的时间/(运行用户代码的时间+垃圾回收时间)
      • Parallel Scavenge Parallel Old 并行类的收集器
  • 内存的使用维度

6.如何设置JVM堆的大小

正常来说默认JVM堆的大小=老年代的大小+新生代的大小,默认比例为2:1, 老年代的大小可以设置成老年代FULL GC后存活对象总大小的3到4倍,元数据区域的大小可以设置为老年代存活对象总大小的1.2倍到1.5倍左右,可以通过JVM启动参数 加入GC日志来观察每次FULL GC后的对象总大小,或者通过JMAP -dump命令下载内存快照强制触发full gc 并通过快照分析。

7. 如果jsp的CPU占用过高如何去分析?

8. Jvm内存溢出怎么分析?

9. Happens Before

  1. Volatile 规则
  2. a happens before b , b happens before c 则 a happens before c
  3. 单线程的程序顺序规则
  4. 线程的start规则,线程start前面的程序一定对线程内部是可见的
  5. 线程的join规则,
  6. 锁规则,synchronize规则

10. 对象在堆中的组成结构

![截屏2020-10-10 10.56.08](/Users/wangqian/Desktop/截屏2020-10-10 10.56.08.png)

Java基础和JUC

1. valotile关键字

​ 原因:之所以出现是因为cpu级别的缓存和总线锁导致的数据可见性问题以及指令重排序问题

作用:valotile的作用是可以禁止指令重排序以及实现可见性,但并不是线程安全的,因为不具有原子性

可见性是如何实现的了?

​ 操作系统层面提供了三种内存屏障,即读屏障 写屏障 和全屏障,JVM基于操作系统层面的内存屏障实现了4种屏障类型,分别是读读屏障,读写屏障、写写屏障和写读屏障。volitile正是在代码执行之后加了一个storeLoad屏障,保证了此写入对后续读是可见的。

​ 为什么存在指令重排序?举例说明

2. 线程池的使用,各参数的作用,拒绝策略的执行

  • corePoolSize 核心线程数
  • maximumPoolSize 最大线程数
  • keepAliveTime 最大线程数持续时间
  • unit 持续单位
  • workQueue 线程队列
  • threadFacory 线程工厂
  • refectedHandler 拒绝策略(默认有几种拒绝策略?)

有时候会从源码级别考察,一个线程执行的过程

3. ThreadLocal原理,会出现什么问题?

​ 主要说明实现原理,可以顺带说他的哈希碰撞解决方案用了线性寻址法,以及内存泄露的原因和应该怎么去处理

4. 锁升级的过程

​ 对象在内存中包括 对象头 实例数据 填充数据

​ 对象头包括mark word 对象指针 数组长度

markword 32位或者64位 分别包括线程Id hashcode值 分代年龄 是否偏向锁标识 锁标识等epoch

偏向锁 cas 把当前线程的ThreadId存储在对象头中

轻量级锁 自旋 把锁对象的指针指向当前栈帧中的lockRecord,

重量级锁 锁 monitor monitorenter monitorexit

5. 线程的一些方法wait,notify,condition await() signal()

wait, notify 是属于Object类的方法 基于monitor 监视器锁实现等待和唤醒,后台维护了一个等待队列和一个阻塞队列

await signal是condition接口的方法,是基于ReentrantLock锁实现的 基于AQS等待和唤醒

6.讲讲为什么ConcurrentHashMap是并发安全的吧,既然有锁怎么去统计size呢

7. ArrayList和LinkedList的区别

  • arrayList是数组
  • LinkedList是链表

8. HashMap的底层数据结构

​ 数组加链表+红黑树

9. hashmap容量为什么是2的幂次?

​ 因为计算数组下标的时候是hashcode & (n-1) 如果n是2的幂次方 转换成二进制后 后面的全是1,做&运算的时候能保证散落的更加均匀

10. 你重写过hashcode和equals么,要注意什么?

11. ConcurrentHashMap怎么解决HashMap的并发问题(源码)

​ 主要是通过unsafe类的cas操作

12. CAS缺点和解决方案

​ cas 第一个缺点是大多数使用在自选中,但最后只有一个线程可以运行成功,效率较低

​ 第二个是ABA问题,即第一个线程要修改的值是A,然后第二个线程先把值修改成B 然后在修改成A, 这时候cas依然可以成功。

13. Synchronize锁和ReetranLock锁的区别?

​ 把你所知道的可以有逻辑的讲出来,mic的课程很清晰

14. CountDownLatch和CycleBarrier使用场景?

15. JDK动态代理和CGLIB动态代理

jdk动态代理基于接口,因为代理类本身已经继承了Proxy类, 实现Invocationhandler

Proxy.newInstance(ClassLoader, Class<?>[], Invocationhandler)

CGLIB动态代理是基于可继承的父类,通过Enhancer类加强器创建子类实现动态代理 实现MethodIntecetpor

16. 哈希冲突的四种解决办法

  • 开放寻址法 如果可以预知哈希的大小,可以完美散列 ThreadLocalMap
    • 线性探测
    • 二次探测
    • 随机数探测
  • 拉链法
  • 再哈希法
  • 建立公共溢出区

17. 一致性Hash算法

  1. 常规哈希取模
  2. 不带虚拟节点的哈希 通过服务器的信息(比如 host、port、name等)通过hash算法形成一个圈,每个服务器负责一段范围的请求,如果超过了这个返回就重新回到第一个服务器。这中算法的缺点是其中一个服务挂了之后,所有的请求会转发到下一个服务,容易导致雪崩
  3. 带虚拟节点的哈希 跟上一个不同点是,把每一个服务器分布在环的不同位置,从而避免雪崩。

中间件

redis如何主从同步?

  • 全量复制 通过rdb文件当slave机器连接的时候 master服务器 fork一个子线程 子线程生成一个快照,同步给slave去同步数据
  • 增量复制 通过心跳命令去增量同步数据,服务器会维护一个log的内存文件和已经同步的偏移量信息
  • 无磁盘复制 通过内存生成文件去复制,不通过磁盘

redis分布式锁注意事项?

redis的哨兵和集群模式?

redis的基本数据结构和底层实现

redis的持久化方案

分布式事务的解决方案?

分布式下redis如何保证线程安全?

单点登录怎么实现?

秒杀系统怎么来实现?

多路复用 IO NIO BIO

布隆过滤器 Bitmap

kafka的架构,如何用kafka保证消息的有序性

kafka和redis的区别

MQ

  1. 交换机类型

    1. 消息怎么删除

    2. 消息队列的使用场景

      1. 削峰
        1. 解耦
        2. 异步
        3. 广播
    3. 消息队列什么时候变成死信

      1. 消息消费的时候被拒绝,没有设置重新进入队列
        1. 消息过期
        2. 超过队列长度的时候 第一条消息会变成死信
    4. 多个消费者监听同一个队列,消息怎么分发

      1. 轮询
        1. 公平分发
      2. 比如有的消费时间比较久,占用时长高,这时候可以设置参数 大于多少个的时候不在分发消息
    5. 无法路由的消息去了哪里?一般被丢弃

    6. 延迟队列如何实现

      • 3.7+ 有一个延迟队列插件

      • TTL + 死信队列 可以通过消息的存活时间和死信队列来实现,指定死信交换机

  2. 消息的顺序执行

    1. 一个队列一个消费者

    2. 一个队列,然后使用内存队列排队,根据订单id哈希去把同一个订单的发送到同一个队列,然后使用多个线程去消费不同的队列

mysql

1. 事务的四大特性 ACID

  • 原子性 Atomicity

    我们对数据库的一系列操作要么都成功 要么都失败,如果转账场景,一个账户增加 另一个失败,要么都成功或者都失败

  • 一致性 Consistent

    一种是指数据库的完整性月数没有被破坏,比如主键唯一,事务的合法等,另一种是业务的一致性,比如转账场景,A账户余额减少1000,B账户余额增加500,这时候虽然两个操作都成功了,符合了原子性协议,但没有符合一致性

  • 隔离性 Isolation

    数据库中有很多事务同时去访问同一个表或者同一行数据,必然会产生一些并发的影响,这时候就要定义不同的事务让他们之间互不干扰,也是通过这种方式保证数据的一致性

  • 持久性 Durable

    对数据库的任意操作,只要成功了就应该是持久的

数据库的持久性是如何实现的?

通过redo log 和 double write双写缓冲来实现的,我们操作数据的时候,会先写道内存的buffer pool中,同时记录redo log,如果在刷盘之前出现异常,再重启后就可以读取redo log的内容,写入磁盘,保证数据的持久性,当然恢复成功的前提是数据页本身没有被破坏,是完整的 这个是通过双写缓冲来保证的

2. 性能优化

  • 通过开启slow query log 可以把慢查询日志记录下来,并用mysqlDumpSlow分析并导出对应需要的格式

    mysqlDumpSlow -s t -t 20 -g 'select' /usr/local/mysql/data/slow-query.log

  • 通过开启select @@profile 开关 可以查看sql执行所用时间 cpu占用信息等情况,

    select * from information_schema.profiling 表中存储了所有信息

  • 通过show processlist 或者select * from information_schema.processlist 查看所有正在执行的sql线程,看看是否有死锁线程或者等待队列已满等情况

3. EXPLAIN

  • id 笛卡尔积,2 * 3 * 4 = 6 * 4 会优于 3 * 4 * 2 = 12 * 2 因为中间产生的笛卡尔积临时表空间更少

    如果id大小不一样 查询顺序是从大到小进行,如果id大小是一样的,查询顺序是自上而下

  • select_type

    • PRIMARY
    • SUBQUERY
    • DERIVED
    • UNNION ALL
  • table

  • type

    • System 系统只有一行数据
    • const 只查询到一行数据
    • eq_ref 唯一性索引 是对于多表join查询,并且对于前表的每一条结果刚好能匹配到后表的每一条结果
    • ref 非唯一性索引,
    • range 范围查找
    • index full index scan 查询的字段是在索引上的字段
    • all
    • null
  • possible_keys 可能使用到的索引

  • key 实际使用到的索引,如果查询的字段存在于某个索引上,虽然这个索引不符合最左匹配原则,但实际还是会用到索引,此时 possible_keys为null key不为null,

  • rows 扫描到的大概行数

  • filtered 过滤的百分比 越接近100越好

  • extra 附加条件

    • using where 如果存储引擎层返回的数据不是我们最终需要的数据,需要经过server端过滤的时候

      需要索引查询到的数据回表查询,

    • using index 只是用了索引数据,不需要回表去查询数据

    • using index condition 索引下推 查询的条件语句经过索引进一步过滤之后 再返回给server端

    • using filesort 没有使用到对应的索引排序, 用了额外的排序规则

    • using temporary 临时表

4. 一个SQL语句的执行过程

连接--》分析器(词法分析器(把一个sql语句拆分成单个的单词去校验)和语法分析器(按照语法树去分析))--》预处理器--》优化器(query optimizer,会生成所有可能的执行计划,然后基于开销时间获取一个最佳的执行计划) --》执行计划(可以通过explain查看)-》执行引擎

5. mysql 存储文件

  • innodb:
    • .frm文件 表结构文件
    • ibd文件索引文件
  • myisam
    • frm文件 表结构文件
    • myi 索引文件
    • myd 数据文件

6. 存储引擎

  • MYISAM

    • 支持表级别的锁,不支持事务 ,所以插入和更显会锁表

    • 拥有较高的插入和查询速度,适合读多写少的情况

    • 存储了表的count行数,所以count速度更快

      怎么快速向数据库插入100万条数据了,可以先用MYISAM引擎插入数据,然后在修改为INNODB

  • INNODB

    • 支持事务,支持外键,
    • 支持航级别的锁和表级别的锁
    • 支持读写并发,写不阻塞读(MVCC)
    • 特殊的索引存放 可以减少IO

7. INNODB的内存结构和磁盘结构

1. 内存结构(Buffer Pool) 默认128M 可以缓存索引页和数据页

  • Buffer Pool 内存的缓冲池数据写满了怎么办?Innodb使用LRU算法,有一个Buffer Pool List分成Old (3/8)和young(5/8)代,如果查询的数据在缓冲池中已存在,则提升到连表头,如果不存在则放到中间,后面的数据进行末尾淘汰

  • Change Buffer 写缓冲,如果要写入的数据不是唯一索引,也就是说不存在数据重复的情况下,就不需要先从磁盘加载判断唯一性,直接先在缓冲池中更改,从而提升增删改的执行速度,5.5之前的版本叫Insert Buffer 现在可以支持更新和删除,所以更改为Change Buffer

    然后把change Buffer的数据merge,在访问这个数据页的时候merge,通过后台线程、或者数据库发生 shutdown的时候,以及 redo log 写满时触发

    如果数据库的大部分索引都是非唯一索引,并且业务是写多读少,不会在写后立刻读取数据,就可以调大change buffer的值,默认占用 Buffer Pool的25%空间

  • Adaptive Hash index

  • (redo)log buffer 为防止Change Buffer里的数据未同步到磁盘发生宕机,innodb 把所有的写操作都记录在 redo log 中,redo log在表空间中 默认有2个文件,每个文件默认48M,默认都是先写日志 在写磁盘,因为写入磁盘是随机IO,写入日志文件是顺序IO,所以速度更快。默认是每次事务提交的时候 都会把log buffer 写入到磁盘。

    redo log的文件大小是固定的,默认16M,里边有两个指针,一个写指针,一个check指针,如果两个指针重叠,说明文件满了,这个时候需要同步数据到磁盘中

2. 磁盘结构

  • 系统表空间 system tablespace
  • 独占表空间
    • segment 段 数据段 索引段
    • extent 区(簇) 大小是1M 包括64个页
    • page 页 每个页大小是16K
  • 通用表空间

8. 日志文件

  1. redo log 记录修改后的数据,用来更新数据日志 是在存储引擎层完成的
  2. undo log 记录之前的数据,用来恢复数据
  3. bin log 记录逻辑日志,只记录操作,不记录数据,主要用来做主从复制和数据恢复 是在server层完成的

一个更新语句的执行流程是:先经过server层,然后交给存储引擎层执行更新,将更新结果写入到Buffer Pool,记录redo log的记录并且设置状态为prepare的,然后返回给server层,server层写入bin log日志,此时再通知存储引擎层更改redo log的状态为commit,保持数据一致性

Innodb 辅助索引只存储索引和主键值 ,主键索引即聚簇索引是和数据放在一起的

Myisam 索引和数据是分开存储的

mysql innodb如果没有主键:第一个不为空唯一索引 --》row_id

9. 事务的几种数据特性 / 事务并发带来的几个问题

  • 脏读

    A事务读到了B事务更改后但未提交的值,导致两次读取到的数据不一样

  • 不可重复读

    A事务读到了B事务更改(特指修改或者删除)后提交的值,导致两次读取的数据不一样,

  • 幻读

    A事务读到了B事务更改(特指新增)后提交的值,导致两次读取的数据不一样

10. 事务的4个隔离级别

  • RU (Read Uncommitted)未提交读

  • RC (Read Committed) 已提交读

    通过MVCC实现读一致性 通过Record lock 实现写一致性,存在幻读问题

  • RR (Repeatable Read)可重复读 同一个事务里多次读取同样的数据结果是一样的,解决了不可重复读问题,并在INNODB中解决了幻读的问题,通过临键锁,

    通过MVCC实现读一致性,通过Record Lock,Gap Lock, Next-Key Lock实现写一致性

  • Serializable 串行化 所有的事务都是串行执行的,解决了所有问题,但效率降低

11. Mysql默认添加的几个字段

  • DB_TRX_ID 事务id
  • DB_ROLL_PTR 回滚id 指向undo log
  • DB_ROW_ID 行id

12. mysql锁

包括有排它锁 即Exclusive Lock 即X锁 写锁 和 共享锁 Share Mode Lock 即S锁 读锁

  • record lock 记录锁

    如果查询的数据正好是唯一索引或者主键索引的数据,就是记录锁,只锁当前这一行数据

  • gap lock 间隙锁

    间隙所是记录锁的n+1个,是指记录不错的区间锁,左开右开的,如果查询的记录不存在,则用间隙锁

  • Next-key Lock 临键锁 默认

    临键锁是INNODB默认的行锁,并通过此解决了在RR事务级别下的数据幻读问题,如果查询的是区间数据并且命中了某一个索引的值,则用间隙锁,锁住当前记录的前一个间隙锁和后一个间隙锁。

13. mysql几种日志文件格式及作用

  • redo log 重做日志 。是存储引擎层记录的,在数据更新到Buffer Pool之后记录在redo log中,用于记录数据的更新值
  • undo log 回滚日志,也是存储引擎层记录的,用于记录数据更新前的值,
  • bin log server层记录的,只记录增删改的这些sql日志,并不记录实际的值,用于主从同步和数据恢复的

14. MySQL多版本控制MVCC

​ Multi Version Concurrency, 基于快照实现, 通过数据库隐藏的字段 事务id和 回滚指针来完成,回滚指针指向undo log里的数据

15. 如何解决数据库读一致性的问题

  1. LBCC (Lock Based Conconrrency Control)

    在读取数据前,对其加锁,阻止其他事务对数据进行修改

  2. MVCC( Muti Version Concurrency Control )

    生成一个数据请求时间点的快照,并用这个快照来提供一定级别的一致性读取

16. 二叉搜索树 二叉平衡树(AVL)B树 B+树

  • 二叉搜索树:左子树小于根节点 右子树大于根节点 按照中序排列的话是一个有序的队列,缺点是树的深度太大

  • 二叉平衡树:基于二叉搜索树的一个平衡树 树的深度绝对值差不超过1

  • B树 :多路平衡树,度是关键字n的n+1个,叶子节点都存储数据和索引,度数多

  • B+树:非叶子节点只存储关键字,不存储索引和值,所以可以存储更多关键字,叶子节点中有一个指向下一个节点的指针,排序和范围查询更快,而且树的深度是固定的,所以IO次数是固定的

17. 死锁的四大必要条件(举例说明,如何发生一个死锁)

  • 互斥条件
  • 持有且等待
  • 不可剥夺
  • 循环依赖

18. 哪些情况不走索引

1、查询条件中使用了计算 比如 age+=20

2、查询条件中使用了函数

3、第三是like %

4、or操作

其他

1. tcp为什么断开连接时是四次挥手而建立连接时是三次握手

2. 设计模式7大原则

  • 开闭原则

  • 单一职责原则

  • 接口隔离原则 接口不要太大 尽可能暴露给客户端的都是需要的

  • 里氏替换原则 子类可以继承父类,但不能更改父类原有的功能

  • 依赖倒置原则 应该是下层依赖上层 细节依赖抽象

  • 迪米特法则 也即最少知道原则

  • 合成复用原则 尽量多远组合而不是继承

3. 常见的设计模式(举例说明你在项目中的实际使用场景)

必问话题

个人提前准备好对应的措辞,反复自问自答,多做练习

  • 自我介绍
  • 找一个你做过最复杂的项目说说
  • 找一个你解决过项目中最困难的问题说说
  • 找一个你解决的记忆最深刻的线上问题说说
  • 在上家公司个人成长
  • 为什么离开上家公司
  • 个人未来的职业规划
  • 对我们公司,你还有什么想要了解的吗(可以多问问岗位工作内容、技术栈方向)
  • 你期望的薪资、你目前的薪资
  • 你对我们公司的职级了解么?你期望达到什么职级

开发性话题

  • 设计一个系统你会考虑哪些方面?

  • 了解CAP、Base、Tcc吗?

  • 如何拆分微服务?DDD了解过吗?

  • 怎么去设计一个单点登录系统?

  • 如何去设计一个秒杀系统?

  • 如何去设计一个短连接处理系统?

中间件主要以MQ、REDIS、ZOOKEPER和KAFKA为主

以上是关于常见面试题总结的主要内容,如果未能解决你的问题,请参考以下文章

面试题Java基础篇-常见面试题总结p3

数据库常见面试题总结

面试题MySQL常见面试题合集

java常见面试题——java笔试题总结

Java 并发常见面试题总结(下)

Java集合常见面试题总结(上)