无法跳转到题目请在右侧目录索引中输入对应题目的序号点击即可
| 项目 | Java(JVM) | C++ |
|---|---|---|
| 内存分区 | 堆、栈、方法区、元空间等 | 手动划分,堆/栈 |
| 内存分配 | 自动由 JVM 管理 | 开发者手动管理(new/delete) |
| 垃圾回收 | 有 GC,自动回收 | 无 GC,容易内存泄漏 |
| 内存泄漏 | 少,除非引用未释放 | 易发生,需要手动释放 |
多态是面向对象的三大特性之一,指同一操作作用于不同对象时表现出不同的行为。
- 主要体现:方法重写(子类重写父类方法)
- 运行时通过动态绑定决定调用哪个方法
- 例子:父类引用指向子类对象,调用方法时执行子类重写的方法
- HashMap 是非线程安全的
- 结构是数组+链表(Java 8+ 有树化)
- 多线程并发写会导致数据不一致,甚至死循环
- 线程安全场景需要外部同步或使用 ConcurrentHashMap
- 线程安全的哈希表
- Java 8 以后采用CAS + synchronized + 链表/红黑树结合策略
- 支持分段锁(以前版本)或锁粒度更细的同步
- 多线程读写性能高,支持并发操作
- 类加载器按层级关系委托加载任务
- 当前类加载器先委托给父加载器加载
- 避免重复加载和保证核心类唯一性
- 有三个主要加载器:启动类加载器、扩展类加载器、应用类加载器
- 堆:存储对象实例,所有线程共享,垃圾回收主要针对堆
- 栈:每个线程独有,存储方法调用的局部变量和执行状态
- 栈内存随线程创建销毁自动释放,堆内存由 GC 管理
- 方法区存类信息、常量池等,属于堆的逻辑部分
- 线程池是预先创建好一定数量的线程,重复利用,避免频繁创建销毁带来的开销
- 主要参数有核心线程数、最大线程数、任务队列和拒绝策略
- 优点:提高性能、控制最大并发数、资源复用、便于管理线程生命周期
- 原子性(Atomicity):事务要么全部完成,要么全部不做
- 一致性(Consistency):事务执行前后,数据必须保持一致状态
- 隔离性(Isolation):并发执行的事务互不干扰
- 持久性(Durability):事务提交后,结果持久保存,不会丢失
- 多个线程相互等待对方持有的资源,形成循环等待,导致所有线程无法继续执行
- 四个必要条件:互斥、占有且等待、不可剥夺、循环等待
- 解决办法:破坏上述条件,比如资源排序、超时重试、死锁检测
- 悲观锁:假设会发生冲突,操作前先加锁,保证独占资源
- 乐观锁:假设不会冲突,操作前不加锁,操作时通过版本号或时间戳检查是否冲突,冲突则重试
- 乐观锁适合读多写少场景,悲观锁适合写多或高竞争场景
- 多机房部署,负载均衡(DNS 轮询、反向代理、硬件LB)
- 服务冗余,故障自动切换(主备切换)
- 数据库主从复制,分布式缓存,消息队列解耦
- 健康检查、自动扩缩容、容灾备份
- 监控报警和自动恢复机制
- 低耦合:模块间依赖少,改变一个模块影响小
- 高内聚:模块内部功能相关度强,职责单一明确
- 目标是提高代码可维护性和复用性
- 设计模式是解决软件设计中常见问题的最佳实践
- 用过单例、工厂、观察者、策略等模式
- 单例模式保证类只有一个实例,提供全局访问点
- 好处:节省资源,保证共享状态一致性,方便管理
- 选择高选择性的字段建索引
- 使用覆盖索引,避免回表
- 避免在索引列上用函数导致失效
- 合理设计联合索引,遵循最左前缀原则
- 用 EXPLAIN 查看执行计划,确认索引是否被用到
- 避免过多索引影响写性能
- 定期维护索引,如重建和优化
- 多个事务互相等待对方持有的锁,形成环路
- 解决办法:减少锁持有时间、统一加锁顺序、使用合理事务隔离级别、检测并重试死锁事务、优化SQL避免长事务
- 开启慢查询日志定位慢SQL
- 用 EXPLAIN 分析执行计划
- 检查索引使用情况,避免全表扫描
- 优化SQL写法,减少复杂查询
- 关注服务器资源瓶颈
- innodb_buffer_pool_size:缓冲池大小
- max_connections:最大连接数
- innodb_log_file_size:日志文件大小
- slow_query_log:慢查询日志开关
- wait_timeout:连接超时设置
- character_set_server:字符集设置
- RDB(快照):定时将数据写入磁盘
- AOF(追加文件):记录所有写操作日志,恢复更完整
- 混合持久化:结合RDB和AOF优点,提高恢复速度和完整性
- 跳表(skip list):支持快速有序范围查询
- 哈希表(hash table):存元素和分值映射,支持快速查找
- 跳表是多层链表,支持平均O(log n)查找
- 实现简单,便于并发
- 红黑树实现复杂,不易并发操作
- Redis选择跳表兼顾性能和实现复杂度
- 多级缓存分散压力
- 使用限流和降级策略
- 本地缓存减少访问压力
- 一致性哈希分片负载均衡
- 异步处理热点请求
- 惰性删除:访问时检查过期并删除
- 定期删除:周期性扫描删除过期键
- 问题:过期键不能及时清理,造成内存压力
- 解决:合理调整删除频率,监控内存,结合主动过期机制
- noeviction:内存满时写操作失败
- allkeys-lru:淘汰所有键中最近最少使用的
- volatile-lru:淘汰设置过期的键中最近最少使用的
- allkeys-random:随机淘汰键
- volatile-random:随机淘汰设置过期的键
- volatile-ttl:淘汰即将过期的键
- 压缩列表(ziplist):内存占用小,适合元素少(默认128个元素以下)且元素较小(字符串少于64字节)时使用
- 跳表(skiplist):支持快速有序范围查询,适合元素多且更新频繁的场景
- 为什么不用红黑树:跳表实现简单,插入删除效率高,且更容易支持并发;红黑树实现复杂,且Redis设计偏好跳表
- serialVersionUID:用于版本控制,序列化时用来验证类版本一致,避免反序列化失败
- 不序列化字段:常用
transient关键字,标记该字段不参与序列化 - 其他方案:使用自定义序列化方法(
writeObject/readObject),或者使用@JsonIgnore(针对JSON序列化)
-
浅拷贝:复制对象的引用,引用对象未复制
-
深拷贝:复制对象及其引用的所有对象,完整拷贝一份
-
实现深拷贝的方式:
- 实现
Cloneable接口并重写clone(),递归克隆所有引用对象 - 序列化反序列化(对象写入流再读出)
- 使用第三方库(如Apache Commons SerializationUtils)
- 手动构造新对象并复制字段
- 实现
- 秒杀系统考虑点:高并发控制、库存原子扣减、流量削峰(限流/排队)、缓存击穿穿透、异步消息队列解耦、数据一致性、系统可用性和扩展性
- 保险品退保返保费注意:资金流水准确、并发控制、防止重复退费、退费时数据回滚、审计日志完善、异步补偿机制
- 过期策略:惰性删除、定期删除
- 局限性:过期键不能保证实时删除,可能造成内存压力峰值,导致缓存雪崩
- 解决方案:合理配置过期时间,配合内存淘汰策略,使用主动删除和监控,分散大量key过期时间
- 反序列化时如果类结构变化(字段增删改),可能导致数据丢失或异常
- 使用
serialVersionUID控制版本兼容性 - 可使用自定义序列化方法处理兼容问题
- 推荐设计时保持序列化兼容性,避免频繁修改类结构
-
核心参数:核心线程数
corePoolSize,最大线程数maximumPoolSize,任务队列workQueue,线程存活时间keepAliveTime,线程工厂ThreadFactory,拒绝策略RejectedExecutionHandler -
常见问题:
- 线程池线程数配置不合理,导致资源浪费或阻塞
- 队列选择不当,可能导致任务积压或OOM
- 拒绝策略未配置或配置不合理,任务丢失
- 线程泄漏或没有及时关闭线程池
核心三件套:
- HDFS:分布式文件系统,负责存储大规模数据
- YARN:资源调度框架,管理集群资源与任务调度
- MapReduce:分布式计算模型,进行批量数据处理
常见生态组件:
- Hive:类SQL查询工具,建表、查询、分析方便
- HBase:NoSQL数据库,适合存储非结构化/海量数据
- Sqoop:数据在关系型数据库和HDFS之间导入导出
- Flume:日志采集工具,实时收集数据写入HDFS/Hive
- Oozie:任务调度工具,管理定时和依赖任务
- Zookeeper:分布式协调服务
- Spark:替代MapReduce的更快计算框架(后起之秀)
数仓建模三层规范(Kimball 模型):
- ODS(操作数据层):原始数据汇总,不加工
- DWD(明细数据层):清洗后的业务明细
- DWS(宽表数据层):面向分析的汇总表
- ADS(应用数据层):最终用于展示、BI、报表的表
数据治理:
- 数据标准化、质量管理、权限管理、血缘追踪、元数据管理
- 加入数据稽核、校验规则、数据版本控制
异常监控:
- 配置指标阈值报警(如某字段值突降)
- 数据校验(行数对账、字段非空、唯一值判断)
- 定期数据探查与回滚机制
实时画像:根据用户行为数据(如点击、浏览)实时更新用户画像标签,如“活跃用户”“潜在转化”等
实时性定义:一般指分钟级、秒级(如T+0)的数据更新时效
保证实时准确性:
- 数据链路要有“Exactly once”语义(Flink 支持)
- 严格依赖 Kafka/Flink 的 checkpoint + watermark 机制
- 使用幂等写入、去重、延迟窗口处理机制
- 数据补偿:支持历史数据回补,确保最终一致性
常见工具:
- BI 工具:如 Superset、FineBI、Tableau、DataV、Quick BI、Metabase
- 可视化工具:如 Echarts、AntV、DataV 大屏
秒级数据更新关键点:
- 实时数据来源:如 Kafka + Flink + Redis/ES
- 中间层缓存(Redis、Druid)提供快速查询
- 前端定时拉取/订阅式刷新(WebSocket/Polling)
评价标准:
- 数据标准统一、质量稳定,能支撑复杂报表/分析
- 建模清晰、可复用、文档完备
- 查询快、时效高、支持多业务线
- 维护成本低、权限清晰、监控完善
- 能推动业务改进和决策
设计步骤:
- 需求调研:明确业务目标、数据来源、核心指标
- 数据采集:Flume/Sqoop/CDC 采集 MySQL、日志、埋点等
- 建模分层:ODS → DWD → DWS → ADS,维表、宽表建模
- ETL调度:使用 Airflow/Oozie/Flink 定时处理数据
- 数据存储:离线层(Hive/HDFS)、实时层(Kafka/Redis/ES)
- 质量监控:数据校验、报警、血缘分析
- 服务支撑:API 接口、BI 查询、看板展示
- 文档管理:元数据、建模文档、数据字典
- Synchronized:JVM 层面实现的内置锁,属于悲观锁,自动加解锁,粒度较粗
- Lock(如 ReentrantLock):JDK 提供的显式锁,可以灵活控制加锁、释放、可重入、公平锁、可中断等
本质区别:
- Synchronized 依赖 JVM + Monitor 对象 实现,不能中断,适合简单场景
- Lock 是 Java代码层面实现,提供更多控制能力(如 tryLock、可中断、公平锁)
锁的优化过程(轻到重):
- 无锁:对象刚创建,处于无锁状态
- 偏向锁:线程第一次访问时加锁,偏向该线程,后续加锁无需操作
- 轻量级锁:有多个线程竞争时,使用 CAS 方式尝试加锁
- 重量级锁:CAS 自旋失败,升级为重量级锁,线程进入阻塞,JVM 使用 OS 层面的 Monitor
-
信号量(Semaphore):控制并发线程数量的工具
- 典型用法:限流、限资源访问
- acquire() 获取许可,release() 释放许可
- 可实现多个线程抢占固定资源(比如连接池)
-
volatile:用于线程之间变量可见性,并禁止指令重排
- 不能保证原子性,仅解决可见性+有序性问题
- 用于状态标志、双重检查锁单例等轻量同步场景
有,比如用户表、订单表数据量非常大,单表查询慢、主从压力大
常见分库分表策略:
- 分库:按业务维度,比如按地区、租户、业务类型
- 分表:按范围(时间/数值)或哈希(mod)
- 中间件:ShardingSphere、MyCat
- 注意全局 ID、跨分片事务、聚合查询、分布式事务一致性问题
悲观锁适用秒杀场景:对库存等关键数据强一致要求
- 使用数据库的
select ... for update加锁操作 - 适用于低并发、确保强一致、无锁冲突则效率高
- 容易造成阻塞,吞吐量低,高并发场景推荐乐观锁 + 限流 + 异步削峰方案
是的,布隆过滤器(Bloom Filter)是一个概率型数据结构,用于判断某个元素是否存在
特点:
- 通过多个哈希函数映射到位数组上
- 判断为“不存在”一定准确,“存在”可能有误判
- 空间效率高、插入查询都很快
使用场景:
- 防止缓存穿透(先查布隆过滤器)
- 爬虫、去重、黑名单判断等
因为它能快速过滤掉不存在的数据请求,避免请求直接打到数据库:
- 请求到来 → 查布隆过滤器
- 不存在 → 拦截,直接返回空
- 存在 → 再查缓存或数据库
误判会放行,但不会漏拦;能显著减少数据库压力。
Java线程模型:
- Java 使用 “一对一”线程模型(1 Java线程 ≈ 1 内核线程)
- 线程由 JVM 管理,但实际调度由 OS 内核完成
底层实现:
- Thread 类底层通过 native 方法(如
start0())调用 JVM 层接口 → 最终由操作系统创建内核线程
线程 vs 内核线程关系:
- Java 每个线程 ≈ 对应一个内核线程(HotSpot虚拟机默认策略)
优缺点:
- ✅ 优点:调度灵活、性能高、可并行
- ❌ 缺点:创建销毁线程开销大,频繁上下文切换影响性能
Java 语句执行流程(以对象创建为例):
- 源码编译 →
.java→.class(字节码) - 类加载(类加载器加载类)
- 字节码执行(解释执行 or JIT 编译)
- JVM执行引擎 → 映射到系统指令 → 执行语句
类加载流程(类加载机制):
- 加载(load)
- 验证(verify)
- 准备(prepare)
- 解析(resolve)
- 初始化(init)
启动类加载器加载 JDK 内置类:
- JVM 启动时加载
rt.jar包中核心类(如java.lang.*),由启动类加载器(BootstrapClassLoader)负责 - 它是 JVM 的一部分,由 C/C++ 实现,不是 Java 类
垃圾回收机制核心概念:
- 新生代、老年代、元空间(替代永久代)
- 判断垃圾:引用计数 / 可达性分析
- 回收策略:Minor GC(新生代)、Major GC(老年代)、Full GC(全堆)
常见垃圾回收器:
| 回收器 | 特点 |
|---|---|
| Serial | 单线程,新生代,适合单核 |
| ParNew | 多线程版 Serial |
| CMS | 并发标记清除,低停顿 |
| G1 | 分区式回收,低延迟,替代 CMS |
| ZGC | 极低延迟,支持大内存 |
| Shenandoah | 类似 ZGC,JDK11+支持 |
SQL 执行流程:
- 客户端发 SQL → 连接层
- 查询缓存(8.0已废弃)
- 分析器 → 语法/词法解析
- 优化器 → 确定执行计划
- 执行器 → 执行语句
- 存储引擎(如 InnoDB)访问数据
为什么要有 Redo Log?
- B+ 树存储在磁盘页中,若直接写磁盘效率低且不具备事务持久性保证
- Redo Log 是 WAL(Write Ahead Logging)机制,先记录操作日志,再批量刷盘,提高性能
- Undo Log 用于 回滚数据,支持事务的 原子性 和 一致性
主要功能:
- 回滚:当事务失败或被手动回滚时,Undo Log 记录的数据可还原之前状态
- MVCC:多版本并发控制,用 Undo Log 生成旧版本数据,实现 RC、RR 等隔离级别下的一致性读
Class 生命周期分为 5 个阶段:
- 加载(Loading):将
.class文件读入内存,生成 Class 对象 - 验证(Verification):确保字节码合法、安全
- 准备(Preparation):为类的静态变量分配内存,赋默认值
- 解析(Resolution):将符号引用转为直接引用
- 初始化(Initialization):执行
<clinit>方法,初始化静态变量等
JNI(Java 调用 C/C++)效率低的常见原因:
- Java/Native 来回切换成本高
- 数据结构转换损耗
- 频繁调用小函数
优化方式:
- 减少 Java ↔ Native 调用频率(批处理)
- 缓存
jclass/jmethodID避免每次查找 - 避免使用
NewStringUTF等频繁字符串转换 - 用 DirectByteBuffer 减少内存复制
- 用 registerNatives 提高方法绑定效率
取决于数据存储在哪里:
-
数据库中:
create table new_table as select distinct * from old_table;- 分批查询 + 去重后插入临时表
- 添加索引提高 distinct 效率
-
大数据平台(Hive、Spark):
- 使用
distinct+ 分区处理 - MapReduce:Hash分组去重
- 使用
-
内存中(文件/数据流):
- Bloom Filter 过滤
- HashSet 去重(可配合外部排序 / 分块处理)
- Hadoop/Spark 分布式去重
| 对比项 | 乐观锁 | 悲观锁 |
|---|---|---|
| 核心思想 | 默认无冲突,冲突时重试 | 默认会冲突,先加锁阻止冲突 |
| 实现方式 | CAS(版本号、时间戳) | select ... for update,加行锁 |
| 性能 | 高,适合读多写少 | 低,适合写多读少 |
| 使用场景 | 秒杀、抢购、分布式场景 | 银行转账、库存扣减 |
分类:
- 创建型:单例、工厂、建造者
- 结构型:代理、装饰器、适配器、桥接
- 行为型:策略、模板方法、观察者、责任链、状态机
常见例子:
- 单例模式:Spring 中 Bean 默认是单例
- 工厂模式:Spring 的 BeanFactory / FactoryBean
- 策略模式:支付方式 / 排序策略切换
- 责任链模式:Spring Filter / Servlet Filter 链
三种主流方案:
- 两阶段提交(2PC):强一致,但性能差,常用于 XA
- TCC:Try-Confirm-Cancel 三步操作,每个服务实现补偿逻辑
- 本地消息表 + 最终一致性:如阿里方案,用消息队列确保事务达成
常见工具:
- Seata、DTM、LCN、RocketMQ 事务消息
核心流程:
- 定义接口:如
HelloService - 服务注册:将服务地址注册到注册中心(或 Map)
- 客户端代理:生成代理对象,封装网络调用
- 网络传输:使用 Netty/Socket 建立 TCP 通信
- 序列化协议:使用 JSON、Hessian、protobuf
- 服务端暴露:监听端口,接收请求,反射调用实现类
- 结果返回:序列化响应返回给客户端