后端常见问题

一、基础

1.Java中的四种引用类型及其应用场景

1)强引用

**特点:**默认引用方式,对象不会被GC回收。

应用 :常规对象创建、缓存核心数据:用户信息,订单信息

2)软引用
java 复制代码
SoftReference<Object> ref = new SoftReference<>(new Object());

**特点:**只有内存不足时才回收。

应用:内存敏感的缓存,大对象缓存。内存充足时利用缓存提升性能,内存紧张时,自动释放资源避免oom

3) 弱引用

解决内存泄漏,存储临时关联数据

java 复制代码
WeakReference<Object> ref = new WeakReference<>(new Object());

**特点:**GC时必定回收

应用:

  • WeakHashMap,键为弱引用
  • 监听器/回调防止内存泄漏
  • ThreadLocal
4)虚引用

**特点:**每次GC回收。无法通过get()获取对象,需配合ReferenceQueue

应用:

  • 精确控制对象销毁时机
  • 代替finalize()进行资源管理
  • 直接内存管理(netty、DirectByteBuffer)

2.HashMap、 ConcurrentHashMap的底层实现原理

jdk1.8 ,数据结构:数组+链表+红黑树。

|-------|----------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| | hashMap | ConcurrentHashMap |
| 扩容时机 | 元素总数超过阈值(数组长度*0.75) | 写线程,发现目标桶头结点是ForwardingNode,表示正在迁移,则协助迁移 |
| put流程 | 如果数组为空,先初始化数组,默认长度为16 | 如果数组为空,先CAS初始化数组,默认长度为16 |
| put流程 | 计算桶的位置。hashCode高16位和低16位进行异或,然后与数组长度-1相与 ||
| put流程 | 桶为空,直接插入 | 桶为空,CAS插入。 |
| put流程 | 桶不为空,遍历链表/红黑树,判断key是否相同,相同则覆盖,不同则末尾插入 | 桶不为空(上一步CAS失败,或者本身不为空):synchronized锁住头结点。 |
| put流程 | | 判断头结点是否为forwardingNode,是则协助扩容。否则和左边一样的逻辑插入数据。 |
| put流程 | 判断桶数据是否大于8或者小于6?大于8则尝试转为红黑树。小于6则树转为链表。 ||
| put流程 | 判断是否需要扩容 ||
| 扩容流程 | 单线程执行 | 多线程并发执行 |
| 扩容流程 | 1)新建一个容量为原数组2倍的新数组 2)数据迁移,hash&数组长度,为0,数据在原位置;否则在原位置+原数组长度 3)迁移完成,将新数组引用替换旧数组引用(扩容期间整个map不可用,效率低) | 1)新建一个容量为原数组2倍的新数组 2)旧数组划分为多个区间(transferIndex),多个线程协同分批迁移数据 3)当线程进行put时,如果桶是 ForwardingNode,放下 put 的活,去 CAS 抢 transferIndex 领取区间,对区间中桶的头结点加锁,协助把旧数组的数据搬到新数组。 4)线程从后往前领取任务区间,步长根据cpu核数和数组长度动态计算。 5)当所有桶的头结点都变成forwardingNode,替换数组引用。 桶的头结点变为forwardingNode,读操作会调用find方法找到新位置 |

3.ArrayList和LinkedList的底层差异及使用场景

ArrayList底层是数组 ,而LinkedList底层是链表

ArrayList适用于随机访问 ,适合读多写少 的场景日常开发中最常用。在尾部插入时效率高,不用移动大量元素 。当数组容量不足时,会自动触发扩容,默认是原来的1.5倍。扩容时需要复制数组,有一定开销。

LinkedList适用于**写多读少,**需要在头部尾部频繁插入/删除的场景。头尾插入时,效率高。如果是其他节点,虽然不用迁移数据,但是需要定位节点,从头遍历。比较适合做队列,先进先出

5.反射机制的原理和使用注意事项

6.深拷贝和浅拷贝的实现方式

7.泛型擦除机制和类型擦除带来的问题

二、JVM与性能调优

1.JVM内存结构:堆、栈、方法区、元空间

2.垃圾回收算法:标记清除、复制、标记整理

3.CMS和G1垃圾收集器的原理和区别

4.JVM调优参数:Xms、Xmx、Xss、XX参数配置

5.类加载机制和双亲委派模型

6.内存泄漏的排查方法和工具使用

7.Java对象的内存布局

8.JVM线上问题排查:CPU飙升、内存泄漏、死锁

并发编程

1.synchronized和ReentrantLock的实现原理及区别2.volatile关键字的作用和内存语义

3.Java线程池的核心参数和工作原理

4.ThreadLocal的实现原理和内存泄漏问题

5.AQS原理

6.CountDownLatch、CyclicBarrier、 Semaphore的使用场景

7.线程间通信的几种方式

二、Spring框架生态

1.Spring Bean的生命周期

2.SpringlOc和DI的实现原理

3.SpringAOP的实现原理,JDK动态代理和CGLIB区别

4.Spring事务管理机制和传播特性

5.BeanFactory和ApplicationContext的区别6.Spring中的设计模式应用

7.Spring Boot自动配置原理

8.Spring Boot启动流程

9.Spring Boot Starter的工作原理

10.Spring Boot配置文件加载顺序和优先级

11.如何实现Spring Boot应用的优雅停机

12.Spring Boot中如何实现多数据源配置

三、数据库与存储MySQL

1.MySOL索引的数据结构(B+树原理)

2.聚簇索引和非聚簇索引的区别

3.最左前缀原则和索引下推

4.SOL优化经验和慢查询分析

5.MySOL事务隔离级别和MVCC原理

6.数据库锁机制:行锁、表锁、间隙锁

7.分库分表的方案和实践经验

8.主从复制原理和读写分离实现

9.explain执行计划的关键字段分析

Redis

1.Redis的数据结构和应用场景

2.Redis持久化机制:RDB和AOF对比

3.缓存穿透、缓存击穿、缓存雪崩的解决方案4.Redis内存淘汰策略

四、消息队列与中间件消息队列

1. Kafka、 RabbitMO、RocketMO的选型对比

2.如何保证消息不丢失

3.如何保证消息顺序性

4.消息堆积的处理方案

5.延迟消息的实现方式

6.消息队列的事务消息