1. JVM 的内存模型JMMJMMJava Memory Model是 Java 并发编程的基础它定义了线程与主内存之间的抽象关系主内存所有变量实例字段、静态字段、数组元素都存储在主内存中线程共享。工作内存每个线程拥有自己的工作内存保存了被该线程使用的变量的副本。线程对变量的所有操作读、写都必须在工作内存中进行不能直接读写主内存。happens-before 原则定义了操作之间可见性的规则如 volatile、synchronized、锁、线程启动/终止等。注意JMM 与 JVM 运行时数据区堆、栈等是两个不同概念但常被混淆。2. Integer a 1 存在哪部分Integer a 1 和 Integer a 100 有什么区别Integer a 1属于自动装箱编译器会调用Integer.valueOf(1)。在 Java 中Integer 缓存池IntegerCache默认缓存了 -128 到 127 之间的整数值。因此Integer a 1会直接从缓存中获取同一个对象而Integer a 100若 100 在缓存范围内也会从缓存获取。存储位置局部变量a本身在栈上它存储的是对象的引用真正的 Integer 对象在堆上如果是从缓存获取则对象在方法区的缓存池中但通常认为 Integer 对象仍在堆中。区别Integer a 1和Integer b 1是同一个对象引用 为 true。Integer a 100和Integer b 100如果超出 -128~127 范围每次装箱都会新建对象因此比较为 false。3. 有哪些垃圾回收算法及其原理① 标记-清除Mark-Sweep原理先标记所有需要回收的对象然后统一清除被标记的对象。缺点产生内存碎片效率随对象数量增加而下降。② 标记-复制Copying原理将内存分为两块如 Eden 和 Survivor只使用其中一块垃圾回收时将存活对象复制到另一块再清空原区域。优点不会产生碎片分配内存简单高效。缺点只能使用一半内存空间浪费若存活对象多复制开销大。③ 标记-整理Mark-Compact原理标记存活对象然后将所有存活对象向一端移动再清理边界以外的内存。优点无内存碎片适合老年代。缺点移动对象需要停顿。④ 分代收集Generational Collection现代 JVM 将堆分为新生代Young和老年代Old针对不同代使用不同算法新生代使用复制算法因为存活对象少老年代使用标记-清除或标记-整理。4. 有哪些垃圾回收器Serial单线程新生代复制老年代标记-整理适合客户端模式。ParNewSerial 的多线程版本与 CMS 配合使用。Parallel Scavenge新生代复制吞吐量优先可自适应调节。Parallel Old老年代标记-整理配合 Parallel Scavenge。CMSConcurrent Mark Sweep老年代并发标记-清除低停顿但会产生碎片且并发阶段可能占用 CPU。G1Garbage First将堆划分为多个 Region整体采用标记-整理可预测停顿时间逐步替代 CMS。ZGC / ShenandoahJDK 11 推出的低延迟垃圾回收器停顿时间不超过 10ms。5. Redis 为什么快基于内存数据存储在内存中读写速度远高于磁盘。单线程模型避免了多线程上下文切换和锁竞争Redis 6.0 后引入多线程 I/O但核心处理仍为单线程。I/O 多路复用使用 epoll 等机制单线程可以同时处理多个网络连接高效利用 CPU。I/O 多路复用一种同步 I/O 模型允许单个线程通过select/epoll等系统调用同时监听多个文件描述符socket当某个 socket 可读/可写时再进行处理。这样避免了为每个连接创建线程降低了开销。6. Redis 数据类型与 ZSet 底层结构数据类型String、Hash、List、Set、ZSet有序集合、Bitmap、HyperLogLog、Geo、Stream 等。ZSet 底层当元素较少时使用ziplist压缩列表节省内存。当元素较多时使用skiplist跳表 dict哈希表。跳表用于维护有序性dict 用于 O(1) 查找元素分值。跳表原理在有序链表基础上增加多层索引每个节点有多个指针指向后续节点。查找时从最高层开始跳过部分节点从而接近二分查找的效率O(log N)。实现比平衡树简单且支持范围查询。7. Redis 内存不足怎么办设置最大内存maxmemory配置。内存淘汰策略maxmemory-policynoeviction内存不足时写入返回错误。volatile-lru/allkeys-lru在设置了过期时间的键/所有键中淘汰最近最少使用的。volatile-lfu/allkeys-lfu淘汰最不经常使用的。volatile-ttl淘汰即将过期的。volatile-random/allkeys-random随机淘汰。数据持久化RDB 和 AOF 可以释放部分内存不会淘汰是在内存满时主动删除。集群通过 Redis Cluster 分片将数据分散到多个节点。8. Redis 分布式锁及 Redisson 原理基本实现使用SET key value NX EX seconds设置锁防止死锁。解锁时需校验 value 是否为自己持有防止误删使用 Lua 脚本保证原子性。Redisson 增强功能可重入通过hash结构记录锁持有者和重入次数。看门狗Watchdog当锁未释放时自动续期避免业务执行时间过长导致锁自动释放。默认每隔 30 秒续期一次。公平锁基于 Redis 队列实现保证等待队列中的线程按顺序获取锁。红锁RedLock多 Redis 实例下确保分布式锁安全。信号量、读写锁、闭锁等高级同步工具。9. Redis 持久化RDB快照在指定时间间隔将内存数据快照写入磁盘。适合备份、灾难恢复但可能丢失最后一次快照后的数据。AOFAppend Only File记录每个写操作到日志文件。支持三种同步策略always、everysec、no。AOF 文件重写可压缩。混合持久化Redis 4.0AOF 重写时采用 RDB 格式 增量 AOF结合两者优点重启恢复更快。10. MySQL 索引数据结构B 树InnoDB 默认所有数据都在叶子节点叶子节点之间通过双向链表连接支持范围查询高效非叶子节点只存键值扇出高树高较低。B 树数据和键都在内部节点范围查询需要中序遍历性能稍差但点查询可能稍快命中节点即可返回。为什么 B 树更优磁盘 I/O 次数更少树矮。范围查询优势叶子链表。全表扫描只需遍历叶子节点。11. 索引失效场景常见失效情况对索引列使用函数、表达式如where age120。隐式类型转换如字符串索引列用数字比较。使用!、、not in可能失效取决于数据分布。like %abc以通配符开头。联合索引未遵循最左前缀原则。使用or但两边不全是索引列或优化器认为全表扫描更优。类型转换问题字段是字符串类型使用数字查会将索引字段隐式转为数字导致索引失效。字段是整数类型使用字符串查会将字符串转为数字索引通常仍有效因为转换发生在查询值上而不是字段上。12. 数据库调优与索引优化索引优化选择合适的列建立索引高选择性、频繁查询、排序分组字段。避免冗余索引合理使用联合索引并注意顺序。覆盖索引减少回表。SQL 优化避免select *只取需要的列。使用explain分析执行计划关注type、key、rows、extra。分页优化延迟关联、子查询等方式避免大 offset。架构调整读写分离、分库分表。使用缓存Redis减少数据库压力。参数调优如innodb_buffer_pool_size。13. Undo Log、Redo Log、Binlog 及顺序Undo Log记录数据修改前的状态用于事务回滚和 MVCC。Redo LogInnoDB 特有的物理日志记录修改后的数据保证持久性崩溃恢复。BinlogMySQL Server 层的逻辑日志记录所有 DDL 和 DML 操作用于主从复制和数据恢复。一条记录插入的执行顺序简版先写Undo Log便于回滚。在内存中修改数据页同时写Redo Log先写 redo log buffer事务提交时刷盘。事务提交时Redo Log 持久化innodb_flush_log_at_trx_commit控制。然后写Binlog先写 binlog cache提交时刷盘。最后通过两阶段提交保证 Redo Log 和 Binlog 逻辑一致。
【面试问题】java小厂
发布时间:2026/6/1 21:51:41
1. JVM 的内存模型JMMJMMJava Memory Model是 Java 并发编程的基础它定义了线程与主内存之间的抽象关系主内存所有变量实例字段、静态字段、数组元素都存储在主内存中线程共享。工作内存每个线程拥有自己的工作内存保存了被该线程使用的变量的副本。线程对变量的所有操作读、写都必须在工作内存中进行不能直接读写主内存。happens-before 原则定义了操作之间可见性的规则如 volatile、synchronized、锁、线程启动/终止等。注意JMM 与 JVM 运行时数据区堆、栈等是两个不同概念但常被混淆。2. Integer a 1 存在哪部分Integer a 1 和 Integer a 100 有什么区别Integer a 1属于自动装箱编译器会调用Integer.valueOf(1)。在 Java 中Integer 缓存池IntegerCache默认缓存了 -128 到 127 之间的整数值。因此Integer a 1会直接从缓存中获取同一个对象而Integer a 100若 100 在缓存范围内也会从缓存获取。存储位置局部变量a本身在栈上它存储的是对象的引用真正的 Integer 对象在堆上如果是从缓存获取则对象在方法区的缓存池中但通常认为 Integer 对象仍在堆中。区别Integer a 1和Integer b 1是同一个对象引用 为 true。Integer a 100和Integer b 100如果超出 -128~127 范围每次装箱都会新建对象因此比较为 false。3. 有哪些垃圾回收算法及其原理① 标记-清除Mark-Sweep原理先标记所有需要回收的对象然后统一清除被标记的对象。缺点产生内存碎片效率随对象数量增加而下降。② 标记-复制Copying原理将内存分为两块如 Eden 和 Survivor只使用其中一块垃圾回收时将存活对象复制到另一块再清空原区域。优点不会产生碎片分配内存简单高效。缺点只能使用一半内存空间浪费若存活对象多复制开销大。③ 标记-整理Mark-Compact原理标记存活对象然后将所有存活对象向一端移动再清理边界以外的内存。优点无内存碎片适合老年代。缺点移动对象需要停顿。④ 分代收集Generational Collection现代 JVM 将堆分为新生代Young和老年代Old针对不同代使用不同算法新生代使用复制算法因为存活对象少老年代使用标记-清除或标记-整理。4. 有哪些垃圾回收器Serial单线程新生代复制老年代标记-整理适合客户端模式。ParNewSerial 的多线程版本与 CMS 配合使用。Parallel Scavenge新生代复制吞吐量优先可自适应调节。Parallel Old老年代标记-整理配合 Parallel Scavenge。CMSConcurrent Mark Sweep老年代并发标记-清除低停顿但会产生碎片且并发阶段可能占用 CPU。G1Garbage First将堆划分为多个 Region整体采用标记-整理可预测停顿时间逐步替代 CMS。ZGC / ShenandoahJDK 11 推出的低延迟垃圾回收器停顿时间不超过 10ms。5. Redis 为什么快基于内存数据存储在内存中读写速度远高于磁盘。单线程模型避免了多线程上下文切换和锁竞争Redis 6.0 后引入多线程 I/O但核心处理仍为单线程。I/O 多路复用使用 epoll 等机制单线程可以同时处理多个网络连接高效利用 CPU。I/O 多路复用一种同步 I/O 模型允许单个线程通过select/epoll等系统调用同时监听多个文件描述符socket当某个 socket 可读/可写时再进行处理。这样避免了为每个连接创建线程降低了开销。6. Redis 数据类型与 ZSet 底层结构数据类型String、Hash、List、Set、ZSet有序集合、Bitmap、HyperLogLog、Geo、Stream 等。ZSet 底层当元素较少时使用ziplist压缩列表节省内存。当元素较多时使用skiplist跳表 dict哈希表。跳表用于维护有序性dict 用于 O(1) 查找元素分值。跳表原理在有序链表基础上增加多层索引每个节点有多个指针指向后续节点。查找时从最高层开始跳过部分节点从而接近二分查找的效率O(log N)。实现比平衡树简单且支持范围查询。7. Redis 内存不足怎么办设置最大内存maxmemory配置。内存淘汰策略maxmemory-policynoeviction内存不足时写入返回错误。volatile-lru/allkeys-lru在设置了过期时间的键/所有键中淘汰最近最少使用的。volatile-lfu/allkeys-lfu淘汰最不经常使用的。volatile-ttl淘汰即将过期的。volatile-random/allkeys-random随机淘汰。数据持久化RDB 和 AOF 可以释放部分内存不会淘汰是在内存满时主动删除。集群通过 Redis Cluster 分片将数据分散到多个节点。8. Redis 分布式锁及 Redisson 原理基本实现使用SET key value NX EX seconds设置锁防止死锁。解锁时需校验 value 是否为自己持有防止误删使用 Lua 脚本保证原子性。Redisson 增强功能可重入通过hash结构记录锁持有者和重入次数。看门狗Watchdog当锁未释放时自动续期避免业务执行时间过长导致锁自动释放。默认每隔 30 秒续期一次。公平锁基于 Redis 队列实现保证等待队列中的线程按顺序获取锁。红锁RedLock多 Redis 实例下确保分布式锁安全。信号量、读写锁、闭锁等高级同步工具。9. Redis 持久化RDB快照在指定时间间隔将内存数据快照写入磁盘。适合备份、灾难恢复但可能丢失最后一次快照后的数据。AOFAppend Only File记录每个写操作到日志文件。支持三种同步策略always、everysec、no。AOF 文件重写可压缩。混合持久化Redis 4.0AOF 重写时采用 RDB 格式 增量 AOF结合两者优点重启恢复更快。10. MySQL 索引数据结构B 树InnoDB 默认所有数据都在叶子节点叶子节点之间通过双向链表连接支持范围查询高效非叶子节点只存键值扇出高树高较低。B 树数据和键都在内部节点范围查询需要中序遍历性能稍差但点查询可能稍快命中节点即可返回。为什么 B 树更优磁盘 I/O 次数更少树矮。范围查询优势叶子链表。全表扫描只需遍历叶子节点。11. 索引失效场景常见失效情况对索引列使用函数、表达式如where age120。隐式类型转换如字符串索引列用数字比较。使用!、、not in可能失效取决于数据分布。like %abc以通配符开头。联合索引未遵循最左前缀原则。使用or但两边不全是索引列或优化器认为全表扫描更优。类型转换问题字段是字符串类型使用数字查会将索引字段隐式转为数字导致索引失效。字段是整数类型使用字符串查会将字符串转为数字索引通常仍有效因为转换发生在查询值上而不是字段上。12. 数据库调优与索引优化索引优化选择合适的列建立索引高选择性、频繁查询、排序分组字段。避免冗余索引合理使用联合索引并注意顺序。覆盖索引减少回表。SQL 优化避免select *只取需要的列。使用explain分析执行计划关注type、key、rows、extra。分页优化延迟关联、子查询等方式避免大 offset。架构调整读写分离、分库分表。使用缓存Redis减少数据库压力。参数调优如innodb_buffer_pool_size。13. Undo Log、Redo Log、Binlog 及顺序Undo Log记录数据修改前的状态用于事务回滚和 MVCC。Redo LogInnoDB 特有的物理日志记录修改后的数据保证持久性崩溃恢复。BinlogMySQL Server 层的逻辑日志记录所有 DDL 和 DML 操作用于主从复制和数据恢复。一条记录插入的执行顺序简版先写Undo Log便于回滚。在内存中修改数据页同时写Redo Log先写 redo log buffer事务提交时刷盘。事务提交时Redo Log 持久化innodb_flush_log_at_trx_commit控制。然后写Binlog先写 binlog cache提交时刷盘。最后通过两阶段提交保证 Redo Log 和 Binlog 逻辑一致。