南京莱斯-Java开发- 二面 面经

1、介绍一下你做过的最有挑战性的项目

项目背景:

  • 业务场景:电商平台秒杀系统,需要支持10万+并发抢购
  • 技术挑战:高并发、超卖防控、系统稳定性、用户体验
  • 团队规模:5人开发团队,我负责核心交易模块

技术方案设计:

  • 前端限流:按钮置灰、请求合并、本地倒计时
  • 网关层:Nginx限流,单IP每秒最多10次请求
  • 应用层:Redis预减库存,Lua脚本保证原子性
  • 数据层:MySQL行锁+乐观锁,防止超卖
  • 消息队列:RabbitMQ削峰,异步处理订单

遇到的问题:

  • 问题1:Redis缓存击穿导致数据库压力骤增 解决:使用互斥锁,只允许一个线程查询数据库
  • 问题2:分布式环境下库存扣减不一致 解决:引入Redisson分布式锁,保证原子性操作
  • 问题3:订单创建失败但库存已扣减 解决:引入本地消息表+定时任务补偿机制

项目成果:

  • 性能指标:支持峰值QPS达到8万,响应时间P99<200ms
  • 业务指标:超卖率降为0,用户投诉下降70%
  • 技术沉淀:形成秒杀系统技术文档,可复用到其他业务

2、JVM内存模型是怎样的,各区域的作用是什么

线程共享区域:

  • 堆(Heap):作用:存储对象实例和数组,GC主要工作区域分代:新生代(Eden+S0+S1,8:1:1)、老年代配置:-Xms初始堆大小,-Xmx最大堆大小特点:所有线程共享,是内存管理的核心区域
  • 方法区(Method Area):作用:存储类信息、常量、静态变量、JIT编译后的代码JDK7:永久代PermGen,容易OOMJDK8:元空间Metaspace,使用本地内存配置:-XX:MetaspaceSize、-XX:MaxMetaspaceSize

线程私有区域:

  • 程序计数器(PC Register):作用:记录当前线程执行的字节码行号特点:唯一不会OOM的区域,占用内存极小线程切换:保存和恢复执行位置
  • 虚拟机栈(VM Stack):作用:存储局部变量表、操作数栈、动态链接、方法出口栈帧:每个方法对应一个栈帧,方法调用时入栈,返回时出栈异常:StackOverflowError(递归过深)、OOM(栈空间不足)配置:-Xss设置栈大小,默认1MB
  • 本地方法栈(Native Method Stack):作用:为Native方法服务特点:与虚拟机栈类似,HotSpot合并实现

直接内存(Direct Memory):

  • 不属于JVM运行时数据区,但被频繁使用
  • NIO的ByteBuffer.allocateDirect()分配
  • 避免Java堆和Native堆之间的数据拷贝
  • 配置:-XX:MaxDirectMemorySize

内存溢出场景:

  • 堆溢出:创建大量对象且无法回收
  • 栈溢出:递归调用层次过深
  • 方法区溢出:动态生成大量类(CGLib)
  • 直接内存溢出:NIO使用不当

3、垃圾回收算法有哪些,各自的优缺点是什么

标记-清除算法(Mark-Sweep):

  • 执行过程: 标记阶段:从GC Roots遍历,标记所有可达对象清除阶段:回收未标记的对象
  • 优点:实现简单,不需要移动对象
  • 缺点:产生内存碎片,分配大对象时可能触发Full GC
  • 应用场景:CMS收集器的老年代回收

标记-复制算法(Mark-Copy):

  • 执行过程: 将内存分为两块,只使用其中一块GC时将存活对象复制到另一块,清空当前块
  • 优点:无内存碎片,分配内存时只需移动指针
  • 缺点:可用内存减半,存活对象多时效率低
  • 应用场景:新生代回收(Eden和Survivor区)
  • 优化:不按1:1划分,而是8:1:1(Eden:S0:S1)

标记-整理算法(Mark-Compact):

  • 执行过程: 标记阶段:标记存活对象整理阶段:将存活对象移动到内存一端,清理边界外内存
  • 优点:无内存碎片,不浪费空间
  • 缺点:需要移动对象,STW时间较长
  • 应用场景:老年代回收(Serial Old、Parallel Old)

分代收集算法(Generational Collection):

  • 理论基础: 弱分代假说:大部分对象朝生夕死强分代假说:熬过多次GC的对象难以回收跨代引用假说:跨代引用相对少
  • 新生代:使用复制算法,Minor GC频繁但快速
  • 老年代:使用标记-清除或标记-整理,Full GC慢但频率低
  • 晋升机制:对象年龄达到15或Survivor空间不足时晋升

增量收集算法(Incremental Collection):

  • 核心思想:将GC过程分成多个小步骤,与应用交替执行
  • 优点:减少单次STW时间
  • 缺点:总体GC时间增加,线程切换开销
  • 应用:CMS的并发标记阶段

三色标记算法(用于并发标记):

  • 白色:未访问的对象
  • 灰色:已访问但其引用未扫描完的对象
  • 黑色:已访问且引用已扫描完的对象
  • 问题:并发标记时可能漏标(对象消失问题)
  • 解决:增量更新(CMS)或原始快照(G1)

4、Spring的IOC和AOP原理是什么

IOC控制反转原理:

  • 核心思想:对象创建权交给Spring容器管理依赖关系由容器注入,而非对象自己创建降低耦合度,提高可测试性
  • 实现机制:反射:通过Class.forName()和Constructor.newInstance()创建对象工厂模式:BeanFactory和ApplicationContext作为Bean工厂依赖注入:构造器注入、Setter注入、字段注入
  • 容器初始化流程:步骤1:加载配置文件或扫描注解步骤2:解析Bean定义,生成BeanDefinition步骤3:注册BeanDefinition到BeanDefinitionRegistry步骤4:实例化Bean(反射调用构造器)步骤5:属性填充(依赖注入)步骤6:初始化Bean(调用init-method、@PostConstruct)步骤7:放入单例池(一级缓存)
  • 三级缓存解决循环依赖:一级缓存singletonObjects:完整的Bean对象二级缓存earlySingletonObjects:早期暴露的Bean对象三级缓存singletonFactories:Bean工厂,用于生成代理对象解决原理:提前暴露未完全初始化的对象引用

AOP面向切面编程原理:

  • 核心概念:切面(Aspect):横切关注点的模块化,如日志、事务连接点(JoinPoint):程序执行的某个点,如方法调用切点(Pointcut):匹配连接点的表达式通知(Advice):在切点执行的代码,分为前置、后置、环绕等织入(Weaving):将切面应用到目标对象的过程
  • 实现方式:JDK动态代理: 要求:目标对象必须实现接口原理:Proxy.newProxyInstance()生成代理类调用:InvocationHandler.invoke()拦截方法CGLIB动态代理: 要求:目标对象不能是final类原理:通过字节码技术生成子类调用:MethodInterceptor.intercept()拦截方法选择策略:有接口用JDK,无接口用CGLIB
  • 织入时机:编译期织入:AspectJ编译器修改字节码类加载期织入:类加载时修改字节码运行期织入:Spring AOP采用,通过动态代理实现
  • 通知类型:@Before:方法执行前@After:方法执行后(无论是否异常)@AfterReturning:方法正常返回后@AfterThrowing:方法抛异常后@Around:环绕通知,可控制方法是否执行
  • 实际应用场景:事务管理:@Transactional注解权限校验:@PreAuthorize注解日志记录:统一记录方法入参出参性能监控:统计方法执行时间异

剩余60%内容,订阅专栏后可继续查看/也可单篇购买

Java面试圣经 文章被收录于专栏

Java面试圣经,带你练透java圣经

全部评论
考虑多多吗,部门核心,hc多多,感兴趣点我主页了解详情哈
点赞 回复 分享
发布于 昨天 17:27 上海

相关推荐

03-15 18:21
吉林大学 Java
这一周,我哭了好多次可能有人会想,一个大男人,哭毛线往下看,或许你会有收获我是27届的985本科生,软件工程,这个专业这个年龄,被时代推着走,看见各个学长拿到的年包50,年包60的工作,我也很羡慕,暗暗心里发誓也要这样的工作,遂开始了自己的旅途我开始学找工作需要的东西,java,我学的很快,大三上开始准备,两个项目速成,背背八股,找到了寒假时候的一份中厂实习,同花顺,金融公司,做大模型后端,但实际并没学到太多什么东西,我当时泡在温柔乡里,觉得自己一个人住,吃喝无所谓该玩就玩很爽,每天上班看看代码,偷点产出支撑我面试中回答,下班吃点好吃的,跟同学打打游戏,中间去了西湖,真觉得很不错年前离职,过个年,年后开始找实习,本来是主暑期,日常作为面试练手的一种策略,误打误撞拿了高德offer,入职了,我觉得业务很好,京东信息安全也发来offer,我没日没夜的愁选哪个,一边是高德可转正但看hc情况和日常表现且业务好、环境好认可度高,另一边是京东业务一般但转正稳、有舍友在一块能做个照应的生活,我一度因为选项而焦虑,深夜自己哭,想家,尤其是每次我妈给我发来关心的话,更让我绷不住但我看到了pyq中一个水滴筹,高中同学得癌症了,化疗阶段,我瞬间就想通了很多身体真的很重要,捐了钱后,我做了很多决定,找好了房子,买好了生活用品,打算在高德先干着了,人生活一世,不用焦虑这些可有可无的东西,东子就算转正了又如何,不是我喜欢的业务转正也不会去,对秋招帮助又不大,反正都要秋招拿offer,为何不选点好业务最后秋招拿ssp呢,就算秋招大失败,回长春进一汽干个研发,一月一万来块钱离家还近,不也挺好的吗,我不再惧怕未来,有这样一段好业务的垂直经历我还怕秋招拿不到offer了?再说了,暑期我还可以继续面试,大不了到时候再去一个转正率高的就是了仔细想想,我要是一直焦虑下去,都得给自己弄的躯体化了,说真的,没必要每一步都走的很完美,走在钢丝线上,难免会把自己逼得什么都干不好,最后混的一场空,你想想,每天焦虑的刷手机刷自己焦虑的内容,得到的结果只会是越来越焦虑,越来越迷茫,最后活也没干好,想法也没决定,留给自己的只剩下随时随地的胡思乱想我明白,自己或许是个贪心的人,既想要好title,又想要好业务,还想要好ld和mt和组,还想要保转正,但其实,这几点里面占两三点就已经很好了,并且我这现在又不是完全转不了正,焦虑那么多干什么呢,我也明白,自己就是想跟别人争个高低,觉得大家都是一个起跑线的,凭什么你比我强呢,或许我真的是个卷狗,但又没那能力人,还是要和自己和解,未来的路那么多,大不了回家找个班上,开开心心的陪陪父母,不也挺好的吗,说不定哪天就中个大奖,又说不定哪天就得个大病,如果这些情况也在你心中发芽,是不是每天活的都得担惊受怕呢,所以呀,不是劝大家不内耗,句句都在理的话未免他就能深入人心,我只想用一些真诚的文字来给大家分享一下我的经历,谢谢
远航_随风:感谢楼主用心的文字,哭一场把心中的情绪宣泄释放出来,心头就没那么堵了。有时候大半夜下班走在空旷的马路上,我会经常和自己说话,排解心中的焦躁和寂寞。 实习工作的生活也是一个不断自洽的过程,是啊,咱们需要和自己和解。未来,是属于我们的
点赞 评论 收藏
分享
评论
点赞
2
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务