版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
技术面试备考系列
Java后端开发面试题库
Spring·MyBatis·JVM
高频问题·深度解析·追问应对
2026年适用版
核心原理·源码视角·场景应用·答题话术·追问预判·复盘建议
目录
01面试准备与答题策略
1.后端面试考察维度分析
2.技术问题的答题框架
3.追问应对与避坑技巧
4.面试复盘与知识管理
02Spring高频问题
1.IoC容器与Bean生命周期
2.循环依赖与三级缓存
3.AOP原理与动态代理
4.声明式事务与失效场景
5.SpringMVC请求处理流程
6.SpringBoot自动装配原理
7.注解对比与常见陷阱
03MyBatis高频问题
1.核心组件与执行流程
2.#{}与${}的本质区别
3.Mapper接口代理机制
4.一二级缓存与失效策略
5.动态SQL与插件原理
6.与Spring整合原理
04JVM高频问题
1.运行时内存区域划分
2.对象创建与内存布局
3.类加载机制与双亲委派
4.垃圾判定与回收算法
5.垃圾收集器对比与选型
6.JVM调优参数与监控工具
7.OOM与内存泄漏排查
05Java基础与并发补充
1.集合框架高频问题
2.并发编程核心考点
3.线程池原理与参数配置
06场景题与系统设计
1.接口幂等设计方案
2.分布式锁实现对比
3.缓存穿透、击穿与雪崩
4.线上问题排查思路
07面试复盘与学习路线
1.面试后复盘清单
2.知识点查漏方法
3.源码阅读建议
PART01
面试准备与答题策略
一、后端面试考察维度分析
Java后端面试通常从四个维度考察候选人:基础知识的深度、源码层面的理解、实际场景的应用能力、以及解决问题的
思维方式。不同级别的岗位对这四个维度的侧重有所不同。
岗位级别知识深度源码要求场景能力思维考察
初级(1-3年)会用、能说清概念了解核心流程能解决常见问题逻辑清晰即可
中级(3-5年)理解原理、知道边界能说关键类和方法有独立排查经验有条理的排查思路
高级(5年以上)掌握设计取舍能画流程图、讲源码有架构级方案经验能权衡利弊做决策
二、技术问题的答题框架
四层答题法
1是什么(定义层面):用一句话说清概念,不要背概念,要说出它的本质。例如IoC不是"控制反转",
而是"对象的创建和依赖关系的管理交给容器"
2怎么做(机制层面):说清楚实现原理和关键流程。例如IoC的实现依赖于反射+工厂模式+配置文
件或注解
3为什么(设计层面):说清这个设计解决了什么问题。例如IoC解决了对象之间强耦合的问题,便于
替换实现和单元测试
4怎么用(实践层面):结合实际场景说明你的使用经验,包括踩过的坑和优化过程
三、追问应对与避坑技巧
追问应对要点:
1不会的题不要硬编:可以坦诚说"这块我了解得不够深入,我的理解是……",然后说出你知道的部分,
比编造细节更安全
2避免背书式回答:面试官能听出你是在背八股文。加入自己的理解和实际案例会更可信
3主动引导话题:回答时可以把话题引到你擅长的方向,比如"这个问题我在XX项目中遇到过,当时的
处理方式是……"
4承认知识边界:高级岗位面试官更看重你的知识边界是否清晰,而不是什么都懂
四、面试复盘与知识管理
每次面试后应整理三份清单:被问到的题目、答不上来的题目、答得不满意的题目。对于答不上来的题目,用"问题—
答案—追问预判—相关知识点"的格式整理,形成自己的题库。对于答得不满意但实际会的题目,分析是表达问题还是
理解问题,前者练话术,后者补知识。
PART02
Spring高频问题
一、IoC容器与Bean生命周期
QQ1:说说你对SpringIoC的理解
IoC即控制反转,本质是把对象的创建、依赖注入和生命周期管理交给Spring容器。传统写法中,一个Service依赖
一个Dao,我们需要在Service中手动newDao(),一旦Dao的构造方式或实现类发生变化,Service的代码也需
要修改。使用IoC之后,Service只需要声明它需要一个Dao,由容器负责查找并注入具体的实现。
IoC的实现基础是工厂模式+反射+配置元数据。Spring容器读取配置(XML、注解或JavaConfig)后,通过反射
实例化Bean对象,再将依赖关系注入到对象属性中。这样的设计带来了三个好处:一是解耦,Service不依赖具体实
现;二是便于测试,可以注入Mock对象;三是便于替换,更换实现类只需修改配置。
追问预判:BeanFactory和ApplicationContext有什么区别?前者是基础容器,提供Bean的实例化和依赖注入,采
用懒加载;后者是增强容器,提供事件发布、国际化、资源加载等企业级功能,默认预加载所有单例Bean。
QQ2:详细说一下SpringBean的生命周期
Bean生命周期是Spring面试最核心的问题之一。完整流程可归纳为以下关键节点:
1实例化:通过反射调用构造器创建Bean实例,此时对象只是一个空壳,属性都是默认值
2属性赋值:执行依赖注入,填充@Autowired、@Value等标注的属性
3BeanNameAware:如果Bean实现了BeanNameAware接口,回调setBeanName()
4BeanFactoryAware:回调setBeanFactory(),让Bean获取到BeanFactory引用
5ApplicationContextAware:回调setApplicationContext(),获取上下文引用
6BeanPostProcessor前置处理:执行所有后置处理器的postProcessBeforeInitialization(),例如
@PostConstruct就是在这里被处理的
7InitializingBean:如果实现了InitializingBean,回调afterPropertiesSet()
8自定义init-method:执行配置文件中指定的初始化方法
9BeanPostProcessor后置处理:执行postProcessAfterInitialization(),AOP代理对象的生成就在
这里
10Bean就绪:此时Bean完全可用,被放入单例池
11销毁流程:容器关闭时,依次调用@PreDestroy、DisposableBean.destroy()和自定义的destroy-
method
记忆口诀:实例化→填充属性→Aware接口→后置处理器前置→初始化方法→后置处理器后置→就
绪→销毁。
QQ3:Bean有哪些作用域?
Spring支持五种作用域:
作用域说明使用场景
singleton默认,容器中只有一个实例无状态的服务类、工具类
prototype每次获取都创建新实例有状态的Bean
request每个HTTP请求一个实例Web应用中保存请求上下文
session每个HTTP会话一个实例用户登录信息
application每个ServletContext一个实例全局配置
实战注意:单例Bean中不要持有可变的实例变量,否则会有线程安全问题。如果确实需要保留状态,应该改用
prototype或使用ThreadLocal。
二、循环依赖与三级缓存
QQ4:Spring如何解决循环依赖?三级缓存分别存什么?
循环依赖指A依赖B,B也依赖A。Spring通过三级缓存解决单例Bean的字段注入循环依赖,构造器注入的循环依
赖无法解决,会直接抛BeanCurrentlyInCreationException。
缓存层级名称存储内容
一级singletonObjects完全初始化好的单例Bean,可直接使用
二级earlySingletonObjects提前暴露的Bean,尚未完成属性填充和初始化
三级singletonFactoriesBean的ObjectFactory工厂,用于生成早期引用或代理对象
解决流程:创建A时,实例化后将A的工厂放入三级缓存;填充A的属性时发现依赖B,转而创建B;B实例化后填
充属性时发现依赖A,从三级缓存中调用工厂获取A的早期引用,放入二级缓存并删除三级缓存;B完成初始化后放
入一级缓存;回到A,注入B完成初始化,最后A也进入一级缓存。
关键提醒:三级缓存的核心作用不是"多一级缓存",而是延迟代理对象的生成时机。如果A需要AOP代
理,代理对象必须在A初始化之后才能创建,但循环依赖导致A需要提前暴露引用给B。三级缓存的工厂模
式使得在需要时能生成代理对象,避免B注入的是原始对象而不是代理对象。
追问预判:为什么不用二级缓存?如果只用二级缓存,那么所有Bean在实例化后都需要立即生成代理对象,这会破坏
Bean的正常生命周期(代理应该在初始化后才生成)。
三、AOP原理与动态代理
QQ5:说说SpringAOP的实现原理
AOP(面向切面编程)的核心目的是将横切关注点(如日志、事务、权限)从业务代码中剥离出来。SpringAOP的实
现依赖于动态代理,在运行时生成代理对象,将切面逻辑织入到目标方法的调用前后。
SpringAOP支持两种代理方式:
对比项JDK动态代理CGLIB动态代理
实现原理基于接口,实现InvocationHandler基于继承,生成目标类的子类
前提条件目标类必须实现接口目标类不能是final,方法不能是final/private
代理类$Proxy0,实现目标接口目标类$$EnhancerByCGLIB$$xxx
性能创建快,调用略慢创建慢,调用快
SpringBoot2.x之后默认使用CGLIB代理,因为大多数业务类不实现接口,且CGLIB在调用性能上更有优势。可以
通过xy-target-class=false强制使用JDK动态代理。
AOP关键术语:切面(Aspect)、连接点(JoinPoint)、切点(Pointcut)、通知(Advice,包括前置、后置、环
绕、异常、最终)、织入(Weaving)。
QQ6:AOP有哪些失效场景?
失效场景一:同类内部方法调用
同一个类中,方法A调用方法B,如果A和B都标注了切面,B的切面不会生效。原因是内部调用使用的是
this引用,不是代理对象。
解决方案:一是注入自身代理@AutowiredprivateXxxServiceself;;二是使用
AopContext.currentProxy()获取当前代理;三是将B方法拆分到另一个Bean中。
失效场景二:方法修饰符问题
private方法、static方法、final方法无法被代理。private方法不会被CGLIB继承,final方法不能被子类
覆盖,static方法与对象无关。
失效场景三:对象未交给Spring管理
自己new出来的对象没有经过Spring容器,自然不会有代理。常见于手动创建的工具类、在static代码
块中初始化的对象。
四、声明式事务与失效场景
QQ7:@Transactional有哪些失效场景?
@Transactional的失效场景是面试超高频题,也是最容易在实际工作中踩坑的地方。总结为以下九类:
失效场景原因解决方案
同类内部方法调用使用this引用,未走代理注入自身代理或拆分方法
方法不是publicSpring事务只对public方法生效改为public
异常被try-catch捕获代理无法感知异常catch中重新抛出或手动回滚
异常类型不匹配默认只回滚RuntimeException和Error指定rollbackFor=Exception.class
NOT_SUPPORTED、NEVER等不会开启确认传播行为为REQUIRED或
传播行为设置错误
事务REQUIRES_NEW
数据库引擎不支持MyISAM不支持事务使用InnoDB
多数据源未指定事务管
默认使用主数据源的事务管理器指定transactionManager
理器
类未被Spring管理new出来的对象没有代理交给容器管理
自定义切面在事务切面之前执行,异常被
切面顺序问题设置切面@Order值
拦截
实践建议:项目中统一使用@Transactional(rollbackFor=Exception.class),避免受检异常导致
的回滚失效。同时在方法内部不要随意try-catch,如果确实需要捕获,务必在catch中重新抛出异常或调用
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly()。
QQ8:Spring事务的七种传播行为分别是什么?
传播行为含义典型场景
REQUIRED默认值,有事务则加入,无则新建绝大多数业务场景
REQUIRES_NEW每次新建事务,挂起当前事务日志记录、独立提交的场景
SUPPORTS有事务则加入,无则以非事务方式执行查询操作
NOT_SUPPORTED以非事务方式执行,挂起当前事务批量操作不希望占用事务
MANDATORY必须在事务中执行,否则抛异常强制要求事务的场景
NEVER必须在无事务环境执行,否则抛异常明确不希望事务的场景
NESTED嵌套事务,通过保存点实现子事务失败不影响主事务
五、SpringMVC请求处理流程
QQ9:说说SpringMVC的完整请求处理流程
一次HTTP请求到达SpringMVC后,经历以下九个环节:
1请求到达DispatcherServlet:前端控制器接收请求,它是整个流程的调度中心
2查询HandlerMapping:根据请求URL查找匹配的处理器,返回HandlerExecutionChain(包含Handler
和拦截器链)
3执行拦截器preHandle:按顺序执行拦截器的前置方法,返回false则中断请求
4适配HandlerAdapter:根据Handler类型选择合适的适配器,例如处理@RequestMapping注解方法的
RequestMappingHandlerAdapter
5参数解析与绑定:通过HandlerMethodArgumentResolver解析方法参数,完成类型转换、数据绑定、参数校
验
6执行Controller方法:调用目标方法,得到返回值
7处理返回值:通过HandlerMethodReturnValueHandler处理,如果返回@ResponseBody则使用
HttpMessageConverter序列化为JSON
8执行拦截器postHandle:按逆序执行拦截器的后置方法
9视图渲染或响应返回:如果返回视图名,通过ViewResolver解析视图并渲染;如果是JSON,直接写回响应
10执行拦截器afterCompletion:无论是否异常都会执行
记忆口诀:DispatcherServlet接请求→HandlerMapping找方法→拦截器preHandle→
HandlerAdapter适配→参数解析→执行Controller→返回值处理→拦截器postHandle→视图渲染→
拦截器afterCompletion。
六、SpringBoot自动装配原理
QQ10:SpringBoot自动装配是怎么实现的?
自动装配是SpringBoot的核心特性,从@SpringBootApplication注解入手分析:
@SpringBootApplication
├──@SpringBootConfiguration//本质是@Configuration
├──@ComponentScan//扫描启动类所在包及子包
└──@EnableAutoConfiguration//开启自动装配
└──@Import(AutoConfigurationImportSelector.class)
核心流程:
1AutoConfigurationImportSelector的selectImports()方法被调用
2通过SpringFactoriesLoader加载所有jar包下META-INF/spring.factories文件中的配置类
3SpringBoot2.7之后改为读取META-
INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件
4得到候选自动配置类列表后,通过@Conditional系列注解进行过滤
5最终符合条件的配置类被加载到容器中
常用的条件注解:@ConditionalOnClass(类路径存在指定类)、@ConditionalOnMissingBean(容器中不存在
指定Bean)、@ConditionalOnProperty(配置文件中存在指定属性)。
追问预判:如何自定义一个Starter?需要创建一个starter模块和一个autoconfigure模块,在autoconfigure模块
中编写配置类和条件注解,并在AutoConfiguration.imports文件中注册配置类。
七、注解对比与常见陷阱
QQ11:@Autowired和@Resource有什么区别?
对比项@Autowired@Resource
来源Spring提供JDK提供(JSR-250)
注入策略默认按类型(byType)注入默认按名称(byName)注入,找不到再按类型
配合注解搭配@Qualifier指定名称通过name属性指定名称
支持位置构造器、字段、方法、参数类、字段、方法
required属性支持required=false不支持,找不到即报错
实践建议:官方推荐使用构造器注入配合@Autowired,可以保证依赖不可变、便于单元测试、避免循环依赖。如果
项目中统一使用@Resource也可以,但要保持团队一致。
QQ12:Spring中BeanFactory和FactoryBean有什么区别?
这两个名字相似但完全不同:
BeanFactory:是IoC容器的顶层接口,负责管理所有Bean的生命周期,提供getBean()等基础方法。
ApplicationContext是它的子接口
FactoryBean:是一个特殊的Bean,用于创建复杂对象。实现该接口的类需要提供getObject()、
getObjectType()、isSingleton()三个方法
使用场景:当创建一个对象的过程比较复杂,无法通过简单的构造器或setter完成时,可以使用FactoryBean。典型
例子是MyBatis的SqlSessionFactoryBean、Spring的ProxyFactoryBean。
注意:通过getBean("&factoryBeanName")获取的是FactoryBean本身,不加&获取的是getObject()返回
的对象。
PART03
MyBatis高频问题
一、核心组件与执行流程
QQ1:MyBatis的执行流程是怎样的?
一次完整的MyBatis查询经历以下阶段:
1加载配置:解析mybatis-config.xml和Mapper映射文件,构建Configuration对象
2创建SqlSessionFactory:通过SqlSessionFactoryBuilder构建,包含所有配置信息
3获取SqlSession:每次数据库操作需要从工厂获取一个SqlSession
4获取Mapper代理:通过sqlSession.getMapper(XxxMapper.class)获取JDK动态代理对象
5调用代理方法:代理对象拦截方法调用,根据方法全限定名找到对应的MappedStatement
6执行器处理:Executor根据MappedStatement和参数执行查询,涉及一级缓存、二级缓存的判断
7StatementHandler:创建PreparedStatement,通过ParameterHandler设置参数
8ResultSetHandler:处理结果集,映射为Java对象
9返回结果:最终返回给调用方
四大核心对象:Executor(执行器)、StatementHandler(语句处理器)、ParameterHandler(参数处理器)、
ResultSetHandler(结果集处理器)。这四个对象都通过Configuration.newXxx()方法创建,且都支持插件拦
截。
二、#{}与${}的本质区别
QQ2:#{}和${}有什么区别?为什么${}会有SQL注入风险?
对比项#{}${}
处理方式预编译,替换为?占位符字符串拼接,直接替换
安全性防止SQL注入存在SQL注入风险
底层实现PreparedStatement.setXxx()Statement拼接
参数类型支持任意类型,自动类型转换只能拼接为字符串,需要手工处理
适用场景绝大多数参数传值动态表名、动态列名、ORDERBY字段
SQL注入示例:如果SQL是SELECT*FROMuserWHEREname='${name}',当name传入'OR'1'='1
时,SQL变成SELECT*FROMuserWHEREname=''OR'1'='1',返回所有用户。
使用建议:能使用#{}的场景坚决不用${}。如果确实需要动态拼接表名或ORDERBY字段,必须在
业务层做白名单校验,只允许预定义的合法值通过。例如订单排序只允许create_time、amount等字
段,其他一律拒绝。
三、Mapper接口代理机制
QQ3:Mapper接口没有实现类,为什么能注入?
MyBatis通过JDK动态代理为Mapper接口生成代理对象。核心流程如下:
1扫描注册:通过@MapperScan或@Mapper注解扫描接口,将其注册为MapperFactoryBean
2创建代理:调用sqlSession.getMapper(XxxMapper.class)时,通过MapperProxyFactory创建
MapperProxy代理对象
3方法拦截:MapperProxy实现InvocationHandler,在invoke()方法中处理
4定位SQL:根据接口全限定名+方法名组装StatementID,从Configuration中取出MappedStatement
5执行查询:调用sqlSession.selectOne/selectList/update()等方法执行SQL
追问预判:如果接口有default方法怎么办?MyBatis会判断方法所属类型,如果声明类是Object或方法是default
方法,则直接调用原方法,不进行SQL处理。
四、一二级缓存与失效策略
QQ4:MyBatis的一级缓存和二级缓存有什么区别?
对比项一级缓存二级缓存
作用域SqlSession级别Mapper(Namespace)级别
默认状态默认开启,无法关闭默认关闭,需手动开启
存储结构PerpetualCache,底层是HashMapPerpetualCache,可对接第三方缓存
开启方式无需配置配置<cache/>或@CacheNamespace
失效场景SqlSession关闭、执行更新操作、手动清理更新操作、事务提交、定时清理
一级缓存失效的四种场景:SqlSession调用了close()方法;SqlSession执行了insert/update/delete操作;
SqlSession调用clearCache()手动清空;不同的SqlSession之间缓存不共享。
实践建议:MyBatis的二级缓存存在多个坑,例如多表关联查询时缓存隔离、跨命名空间缓存不共享、分
布式环境下缓存不一致。生产环境建议禁用二级缓存,改用Redis做应用层缓存,或者使用MyBatis-Plus等
增强框架提供的缓存方案。
五、动态SQL与插件原理
QQ5:MyBatis的插件是怎么实现的?
MyBatis插件基于责任链模式+JDK动态代理实现,可拦截四大核心对象的方法:
可拦截对象可拦截方法典型用途
Executorupdate、query、commit、rollback分页、缓存、SQL日志
StatementHandlerprepare、parameterize、batch、updateSQL改写、分页插件
ParameterHandlergetParameterObject、setParameters参数处理、加密解密
ResultSetHandlerhandleResultSets、handleOutputParameters结果映射、字段脱敏
实现步骤:实现Interceptor接口,标注@Intercepts和@Signature注解指定拦截点,在intercept()
方法中编写增强逻辑,最后通过@Bean或配置文件注册插件。
@Intercepts({
@Signature(type=Executor.class,method="query",
args={MappedStatement.class,Object.class,RowBounds.class,ResultHandler.class})
})
publicclassMyPluginimplementsInterceptor{
@Override
publicObjectintercept(Invocationinvocation)throwsThrowable{
//增强逻辑
returnceed();
}
}
六、与Spring整合原理
QQ6:MyBatis是如何与Spring整合的?
整合的核心是MapperFactoryBean和SqlSessionTemplate:
1扫描Mapper接口:通过@MapperScan注解,使用ClassPathMapperScanner扫描指定包下的接口
2注册BeanDefinition:将每个Mapper接口注册为MapperFactoryBean的BeanDefinition,接口全限定名
作为Bean名称
3创建代理对象:MapperFactoryBean实现FactoryBean接口,其getObject()方法通过
SqlSessionTemplate获取Mapper代理
4SqlSessionTemplate:是SqlSession的线程安全实现,内部持有SqlSessionInterceptor代理,保证每次操
作获取的SqlSession与事务同步
5事务同步:通过Spring的TransactionSynchronizationManager管理SqlSession,保证同一事务内使用同一
个SqlSession
PART04
JVM高频问题
一、运行时内存区域划分
QQ1:JVM运行时内存区域是如何划分的?
JVM内存区域分为线程私有和线程共享两大类:
区域线程归属存储内容异常类型
程序计数器私有当前线程执行的字节码行号无OOM
虚拟机栈私有栈帧(局部变量表、操作数栈、动态链接、方法出口)StackOverflowError/OOM
本地方法栈私有Native方法调用StackOverflowError/OOM
堆共享对象实例、数组OutOfMemoryError
方法区共享类元信息、常量、静态变量OutOfMemoryError
JDK版本差异:JDK7及以前,方法区的实现是永久代(PermGen),位于堆内存中;JDK8之后改为元空间
(Metaspace),位于本地内存,不再受堆大小限制,但可以通过-XX:MaxMetaspaceSize限制。
常见追问:字符串常量池在哪里?JDK7之前位于方法区,JDK7及之后移到堆内存。这也是
Sern()行为在不同版本有差异的原因。
二、对象创建与内存布局
QQ2:JVM中对象创建的过程是怎样的?
对象创建经历五个阶段:
1类加载检查:检查常量池中是否有该类的符号引用,并确认类是否已被加载、解析和初始化
2分配内存:根据内存规整性选择指针碰撞或空闲列表方式。分配时通过CAS+失败重试或TLAB保证线程安全
3初始化零值:将分配到的内存空间初始化零值,保证对象的实例字段可以不赋初值就使用
4设置对象头:设置MarkWord(哈希码、GC分代年龄、锁状态标志)和类型指针
5执行init方法:按照代码逻辑初始化对象,包括构造器、代码块、字段初始化
对象内存布局:对象头(MarkWord+类型指针,数组还有长度)+实例数据+对齐填充(保证是8字节的整数
倍)。
三、类加载机制与双亲委派
QQ3:说说类加载的过程和双亲委派模型
类加载的五个阶段:加载→验证→准备→解析→初始化。其中验证、准备、解析统称为连接。
阶段主要工作
加载通过全限定名获取二进制字节流,转换为方法区的运行时数据结构,生成Class对象
验证文件格式验证、元数据验证、字节码验证、符号引用验证
准备为静态变量分配内存并设置零值,final常量直接赋初值
解析将常量池中的符号引用替换为直接引用
初始化执行<clinit>()方法,为静态变量赋真实值,执行静态代码块
双亲委派模型:类加载器收到加载请求时,先委托父加载器尝试加载,父加载器无法完成时子加载器才尝试。加载器层
次为:Bootstrap→Extension/Platform→Application→自定义。
双亲委派的好处:一是保证核心类库的安全,避免自定义类覆盖JDK类;二是避免类的重复加载,同一个类在JVM中
只有一份。
打破双亲委派的场景:JDBC的SPI机制(父加载器需要调用子加载器加载的实现类)、Tomcat的Web应用隔离、
OSGi模块化。
四、垃圾判定与回收算法
QQ4:如何判断一个对象可以被回收?
主要有两种判定方法:
方法原理缺点
引用计数法对象被引用时计数加一,引用失效减一,为0时回收无法解决循环引用问题
可达性分析从GCRoots出发遍历引用链,不可达的对象可被回收需要StopTheWorld,有一定性能开销
GCRoots包括:虚拟机栈中局部变量表引用的对象、方法区中静态属性引用的对象、方法区中常量引用的对象、本地
方法栈中JNI引用的对象、活跃线程、同步锁持有的对象。
对象的"两次标记":可达性分析后不可达的对象,会先被标记一次,然后判断是否需要执行finalize()。如果对象
在finalize()中重新建立引用,可以自救,否则第二次标记后被回收。
QQ5:垃圾回收算法有哪些?
算法原理优点缺点
标记-清除标记存活对象,清除未标记对象实现简单产生内存碎片,效率低
标记-复制内存分两块,存活对象复制到另一块无碎片,效率高内存利用率低,需移动对象
标记-整理标记存活对象后向一端移动无碎片移动对象开销大
分代收集新生代用复制,老年代用标记-清除或标记-整理综合各算法优势实现复杂
分代收集的理论基础:弱分代假说(大多数对象朝生夕死)和强分代假说(越老的对象越难死)。新生代中98%的对
象会在第一次GC时被回收,因此使用复制算法只需保留10%的空间(Eden:Survivor=8:1:1)。
五、垃圾收集器对比与选型
QQ6:常见的垃圾收集器有哪些?如何选型?
收集器适用区域算法特点适用场景
Serial新生代复制单线程,STW长客户端应用、小内存
ParNew新生代复制多线程版Serial配合CMS使用
ParallelScavenge新生代复制关注吞吐量后台计算、批处理
SerialOld老年代标记-整理单线程客户端模式
ParallelOld老年代标记-整理多线程配合ParallelScavenge
CMS老年代标记-清除低延迟,并发收集互联网应用(JDK9已废弃)
G1整堆复制+标记-整理可预测停顿,Region化JDK9+默认,大内存
ZGC整堆染色指针+读屏障超低延迟(<10ms)超大内存、低延迟要求
Shenandoah整堆转发指针+读屏障低延迟低延迟场景
选型建议:JDK8且堆内存小于6G时,ParallelGC是默认选择;堆内存6G以上且关注延迟,选G1;
对延迟极度敏感(如金融交易),考虑ZGC;JDK11+且大内存场景,G1是稳妥选择。
六、JVM调优参数与监控工具
QQ7:常用的JVM调优参数有哪些?
参数含义推荐值
-Xms初始堆大小与-Xmx相同,避免动态扩容
-Xmx最大堆大小物理内存的50%-70%
-Xmn新生代大小堆的1/3到1/4
-XX:MetaspaceSize元空间初始大小128m
-XX:MaxMetaspaceSize元空间最大值256m到512m
-XX:SurvivorRatioEden与Survivor比值8(即8:1:1)
-XX:MaxTenuringThreshold晋升老年代年龄阈值15(默认)
-XX:+UseG1GC使用G1收集器JDK9+默认
-XX:MaxGCPauseMillisG1目标停顿时间200ms
-XX:+HeapDumpOnOutOfMemoryErrorOOM时自动dump生产环境必开
-XX:HeapDumpPathdump文件路径独立磁盘分区
常用监控工具:
jps:查看Java进程PID
jstat:查看GC统计信息,例如jstat-gcutilPID1000
jmap:导出堆快照,例如jmap-dump:format=b,file=heap.hprofPID
jstack:导出线程栈,用于分析死锁和CPU高占用
jinfo:查看和修改JVM参数
VisualVM/JProfiler/MAT:图形化分析堆快照
七、OOM与内存泄漏排查
QQ8:线上出现OOM或内存泄漏,你怎么排查?
排查步骤:
1保留现场:第一时间dump堆快照,如果已配置-XX:+HeapDumpOnOutOfMemoryError会自动生成
2查看GC日志:通过jstat-gcutilPID1000观察GC频率和老年代占用情况。如果FullGC后老年代占
用不下降,基本可以确定是内存泄漏
3分析堆快照:使用MAT或JProfiler打开hprof文件,查看Histogram找出占用内存最多的对象,通过
DominatorTree找到持有最多内存的对象
4定位引用链:通过PathtoGCRoots查看对象被谁持有,排除弱引用、软引用后找到强引用链
5修复代码:根据引用链定位到具体代码,修复泄漏点
6验证效果:重新压测,观察老年代占用是否稳定
常见泄漏场景:
场景原因修复方式
静态集合持有对象staticMap持续put但未remove使用弱引用或定期清理
ThreadLocal未清理线程池复用线程导致value累积finally中remove()
监听器未注销观察者模式中未反注册onDestroy中注销
连接未关闭数据库连接、IO流未关闭try-with-resources
缓存无上限自建缓存未设置淘汰策略使用Caffeine、GuavaCache
内部类持有外部类非静态内部类隐式持有外部引用改为静态内部类
QQ9:CPU飙高怎么排查?
标准排查流程:
1top找到CPU最高的Java进程PID
2top-HpPID找到该进程内CPU最高的线程TID
将TID转换为十六进制:printf"%x\n"TID
43jstackPID|grep-A30十六进制TID定位到具体线程栈
5根据栈信息定位到具体代码
常见原因:死循环、频繁FullGC(用jstat-gcutil确认)、大量线程上下文切换、正则表达式回溯、频繁序列
化/反序列化。
PART05
Java基础与并发补充
一、集合框架高频问题
QQ1:HashMap的底层结构和扩容机制?
HashMap底层是数组+链表+红黑树。数组称为table,每个元素是一个桶(bucket)。JDK8之后,当链表长度
达到8且数组长度≥64时,链表会转为红黑树,查询复杂度从O(n)降为O(logn)。
扩容机制:默认初始容量16,负载因子0.75。当元素数量超过容量×负载因子时,容量翻倍为原来的2倍。扩
容时重新计算哈希并对元素重新分布,JDK8使用高低位链表优化,不需要重新计算哈希值。
为什么容量是2的幂:哈希计算使用(n-1)&hash,n为2的幂时等价于取模但效率更高。同时扩容后重新分
布只需要判断hash的某一位是0还是1,简化了迁移过程。
为什么负载因子是0.75:这是空间和时间的一个折中。太小会导致频繁扩容,太大则冲突增多。0.75是泊松分布计算
出的冲突概率较低的平衡点。
QQ2:ConcurrentHashMap是如何保证线程安全的?
版本实现方式并发度
JDK7Segment分段锁,每段继承ReentrantLock默认16段
JDK8+CAS+synchronized锁单个桶桶级别,粒度更细
JDK8的核心改进:
锁粒度从Segment降为单个桶(Node),并发度大幅提升
空桶插入使用CAS无锁操作
非空桶使用synchronized锁住头节点
扩容时支持多线程协助迁移数据
size()使用baseCount+CounterCell数组分段计数
二、并发编程核心考点
QQ3:synchronized和ReentrantLock有什么区别?
对比项synchronizedReentrantLock
实现层面JVM内置,基于MonitorJDK实现,基于AQS
锁的释放自动释放必须finally中unlock
可中断不可中断支持lockInterruptibly()
超时获取不支持支持tryLock(timeout)
公平锁非公平支持公平和非公平
条件变量单一wait/notify支持多个Condition
性能JDK6后优化明显功能更多,略有开销
使用建议:如果能用synchronized就用synchronized,代码更简洁,JVM优化更充分。只有在需要可中断、超时、
公平锁、多条件变量等高级功能时才使用ReentrantLock。
QQ4:volatile关键字的作用和原理?
volatile保证可见性和有序性,但不保证原子性。
可见性:写volatile变量时,JVM会插入StoreLoad内存屏障,将工作内存中的值立即刷回主内存;读volatile变量
时,会从主内存重新读取,并让本地内存失效。
有序性:通过内存屏障禁止指令重排。volatile写之前的操作不会被重排到写之后,volatile读之后的操作不会被重排
到读之前。
不保证原子性:i++这类操作包含读、加、写三步,volatile无法保证整体原子性,需要用synchronized或
AtomicInteger。
典型应用:状态标志位、双重检查锁单例中的实例引用。
publicclassSingleton{
privatestaticvolatileSingletoninstance;
publicstaticSingletongetInstance(){
if(instance==null){
synchronized(Singleton.class){
if(instance==null){
instance=newSingleton();
}
}
}
returninstance;
}
}
三、线程池原理与参数配置
QQ5:线程池
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025-2026年四川省人教版初中物理第7单元实验操作测试卷
- 2025-2026年茶艺师服务礼仪与沟通技巧测试题
- 2025-2026年人工智能语音识别与合成专项训练题库
- 2025-2026年江苏省八年级英语第1单元升学模拟卷
- 2026年电子商务师考试电子商务市场调研与营销策略优化课件
- 教学能手工作总结(2篇)
- 2026秋统编版新教材九年级上册道德与法治10.1 民族复兴梦 教案
- Unit 2 Home Sweet Home Section B (1a~1e) 同步练习人教版英语八年级上册
- 外贸跟单员考试真题及答案
- 危险化学品泄漏应急处理考核试卷及答案
- 2026年基层医疗机构药品配备使用管理规范考试试卷试题及答案
- 2026年高中师德师风专题学习课件
- 肺动脉高压诊疗指南(2025版)
- 2026年税务系统遴选面试练习题附详细解析含答案(稽查版)
- 水发集团笔试试题及答案
- WJT9109-2026《工业电子雷管生产技术要求》
- 2026年无人机驾驶员初级模拟题
- 洗胃机急救操作完整流程
- 医院共青团工作制度制度
- 广铁机考题目
- 酒店好评培训
评论
0/150
提交评论