记录一次面经八股学习
-
jvm内存模型
java 内存模型主要包含线程私有的程序计数器、java虚拟机栈、本地方法栈和线程共享的堆空间、元数据区、直接内存。
- 程序计数器:记录当前线程执行的字节码行号,是唯一不会OOM的区域。
- 虚拟机栈:存储方法的局部变量、操作数栈等,方法执行时创建栈帧,栈深度溢出抛
StackOverflowError
,栈扩展失败抛出OOM。 - 本地方法栈:为 Native 方法服务,逻辑与虚拟机栈类似,也会抛
StackOverflowError
或 OOM。 - 堆空间:存储对象实例和数组,是垃圾回收的主要区域,易出现 OOM。
- 方法区(JDK8 后为元空间,位于本地内存):存储类信息、常量、静态变量等,元空间不足时会 OOM。
-
内存溢出会出现在哪里,出现的场景?
- 堆内存:创建大量对象(如集合泄漏、无限 new 对象),堆空间被占满。
- 虚拟机栈 / 本地方法栈:递归过深(无终止条件)或单个栈帧过大,导致栈内存不足(抛
StackOverflowError
或 OOM)。 - 方法区 / 元空间:加载大量类(如动态生成类、依赖过多第三方库),类元数据占用内存过多。
- 直接内存:使用 NIO 的
DirectByteBuffer
分配大量直接内存,本地内存不足时触发 OOM。
-
你认为哪个区域出现的概率比较大?
- 堆内存出现 OOM 的概率最高。因为堆是存储对象实例的核心区域,业务代码中易出现 “对象创建过多、大对象、集合泄漏” 等情况,导致堆内存被快速耗尽。
-
垃圾回收的方法
- 引用计数法(已废弃):通过引用计数器标记对象引用,缺点是无法解决循环引用。
- 可达性分析:以 “GC Roots” 为起点,不可达对象视为可回收。
- 分代收集:基于对象生命周期分新生代(复制算法,如 Eden+Survivor 区)和老年代(标记 - 清除 / 标记 - 整理算法)。
- 并发 / 增量收集:让 GC 与应用线程部分并发执行(如 G1、ZGC),减少 STW(Stop The World)时间。
-
Java集合的底层数据结构
- ArrayList:动态数组,查询快、增删(中间)慢。
- LinkedList:双向链表,查询慢、增删快。
- HashSet:基于
HashMap
(key 存元素,value 为固定空对象)。 - HashMap:JDK8 前是数组 + 链表,JDK8 后是数组 + 链表 + 红黑树(链表长度≥8 且数组≥64 时,链表转红黑树)。
- TreeMap/TreeSet:红黑树,支持有序遍历。
- LinkedHashMap:
HashMap
+ 双向链表,记录插入 / 访问顺序。
-
使用多线程带来的问题,如何解决?
- 问题:线程安全(数据不一致)、死锁、上下文切换开销、资源竞争(饥饿)。
- 解决:
- 线程安全:用
synchronized
/ReentrantLock
、线程安全集合(ConcurrentHashMap
)、原子类(AtomicInteger
)。 - 死锁:避免嵌套锁、固定锁顺序、用定时锁(
tryLock
)。 - 上下文切换:用线程池复用线程、减少锁持有时间、CAS 无锁并发。
- 资源竞争:公平锁、资源池化(如连接池)。
- 线程安全:用
-
redis的使用场景
- 缓存:热点数据缓存,减轻 DB 压力。
- 分布式锁:
SETNX
实现跨服务锁。 - 计数器:
INCR/DECR
实现点赞、阅读量计数。 - 消息队列:List(
LPUSH/RPOP
)、Pub/Sub、Stream。 - 限流:计数器 + 过期时间实现接口限流。
- 地理位置:Geo 命令(
GEOADD
/GEORADIUS
)实现 “附近的人”。 - 布隆过滤器:判断元素是否存在,防止缓存穿透。
-
redis的持久化机制
- RDB:通过 fork ⼦进程在特定时间点对内存数据进⾏全量备份,⽣成⼆进制格式的快照⽂件。其最⼤优势在于备份恢复效率⾼,⽂件紧凑,恢复速度快,适合⼤规模数据的备份和迁移场景。缺点是可能丢失两次快照期间的所有数据变更。
- AOF:会记录每⼀条修改数据的写命令。这种⽇志追加的⽅式让 AOF 能够提供接近实时的数据备份,数据丢失⻛险可以控制在 1 秒内甚⾄完全避免。缺点是⽂件体积较⼤,恢复速度慢。
- 表格对比。
-
redis事务
通过
MULTI
(开始)、EXEC
(执行)、DISCARD
(取消)、WATCH
(乐观锁)实现。特点:- 是 “批量操作”,但非严格原子性(单个命令失败不回滚,仅语法错误会导致事务失败)。
- 隔离性强(单线程执行,事务内命令连续执行)。
- 持久性依赖 RDB/AOF 配置。
-
redis的特性
- 基于内存:读写极快,单线程 QPS 达 10 万 +。
- 数据结构丰富:支持 String、Hash、List、Set、Sorted Set 等。
- 持久化:RDB(快照)和 AOF(日志)保证数据可靠性。
- 单线程模型:避免线程切换开销,IO 多路复用处理并发连接。
- 分布式支持:Redis Cluster 实现数据分片与高可用。
-
MySQL事务
满足ACID特性(原子性、一致性、隔离性、持久性),通过
START TRANSACTION
/COMMIT
/ROLLBACK
控制。- 隔离级别:读未提交(脏读)、读已提交(不可重复读)、可重复读(MySQL 默认,幻读)、串行化(无并发问题,性能差)。
-
乐观锁和悲观锁
- 悲观锁:假设会冲突,操作时直接加锁(如
synchronized
、数据库行锁),优点是一致性强,缺点是并发差。 - 乐观锁:假设不冲突,提交时通过版本号 / 时间戳 / CAS判断冲突,优点是并发高,缺点是冲突频繁时重试开销大。
- 悲观锁:假设会冲突,操作时直接加锁(如
-
红黑树和二叉树的区别
- 二叉搜索树(BST):左子 <父,右子> 父,但极端情况会退化成链表(查询 O (n))。
- 红黑树:是自平衡 BST,通过 “颜色规则 + 旋转” 保证树高为 O (log n),查询 / 插入 / 删除均为 O (log n),用于
TreeMap
、Linux 调度等场景。
-
InnoDB和MyISAM的区别
-
InnoDB 和 MyISAM 的最⼤区别在于事务⽀持和锁机制。InnoDB ⽀持事务、⾏级锁,适合⼤多数业务系统;⽽ MyISAM 不⽀持事务,⽤的是表锁,查询快但写⼊性能差,适合读多写少的场景。
-
另外,从存储结构上来说,MyISAM ⽤三种格式的⽂件来存储,.frm ⽂件存储表的定义;.MYD 存储数据;.MYI 存储索引;⽽ InnoDB ⽤两种格式的⽂件来存储,.frm ⽂件存储表的定义;.ibd 存储数据和索引。
-
从索引类型上来说,MyISAM 为⾮聚簇索引,索引和数据分开存储,索引保存的是数据⽂件的指针。InnoDB 为聚簇索引,索引和数据不分开。
-
更细微的层⾯上来讲,MyISAM 不⽀持外键,可以没有主键,表的具体⾏数存储在表的属性中,查询时可以直接返回;InnoDB ⽀持外键,必须有主键,具体⾏数需要扫描整个表才能返回,有索引的情况下会扫描索引。
-
更细微的层⾯上来讲,MyISAM 不⽀持外键,可以没有主键,表的具体⾏数存储在表的属性中,查询时可以直接返回;InnoDB ⽀持外键,必须有主键,具体⾏数需要扫描整个表才能返回,有索引的情况下会扫描索引。
-
有帮助的话,希望可以点赞❤️+收藏⭐,谢谢各位大佬~~✨️✨️✨️