面试问题实录 2206251

157 阅读12分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

7.3:为啥使用Spring框架

Spring的核心功能IOC(控制反转,依赖注入),AOP(面向切面的编程)

IOC:我们在使用过程中不用关注于对象是怎么创建的,只用应用过去,sping自动帮我们完成注入,对象的创建,spring默认创建对象是单例,这样减少了频繁创建对象,让对象重复利用,所有的对象都是放在BeanFactory 工厂的

AOP:面向切面的编程,我们可以把一些公共的东西模块化,做成一个切面,在方法的运行过程中织入进去,好处是解耦,提高代码的重复利用率

7.4:限流如何实现,介绍细节

有几种方法可以实现限流操作

1.计数器限流。可以设置一秒钟通过指定数量的请求,每一个请求进来,请求数量+1,多余请求拒绝。等一秒钟结束后,请求数量归零,重新计算。 会出现突刺问题。前1mc请求达到最大数量,后999mc全部执行拒绝操作

2.漏桶算法。可以创建一个队列,保存暂时处理不了的请求,然后通过一个线程池定期从队列中获取请求来执行。

img

3.gateway+redis实现令牌桶限流算法 (☆)令牌生成速度恒定,请求没有拿到令牌,系统就执行拒绝

7.5:MySQL的binlog日志

binlog用于记录数据库执行的写入性操作(不包括查询)信息,以二进制的形式保存在磁盘中。binlog是mysql的逻辑日志,并且由Server层进行记录,使用任何存储引擎的mysql数据库都会记录binlog日志。

逻辑日志:可以简单理解为记录的就是sql语句。

binlog的主要使用场景有两个,分别是主从复制数据恢复

  1. 主从复制:在Master端开启binlog,然后将binlog发送到各个Slave端,Slave端重放binlog从而达到主从数据一致。
  2. 数据恢复:通过使用mysqlbinlog工具来恢复数据。 binlog的刷盘时机 mysql通过sync_binlog参数控制biglog的刷盘时机,取值范围是0-N
  • 0:不去强制要求,由系统自行判断何时写入磁盘;
  • 1:每次commit的时候都要将binlog写入磁盘;(最安全,默认的
  • N:每N个事务,才会将binlog写入磁盘。

7.6:什么情况不走索引

  1. 数据库有隐式类型转换,索引类型不一样,不走索引
  2. like查询前模糊匹配不走索引,后模糊匹配可能会走索引
  3. NULL列不走索引
  4. not,not in ,!=,or不走索引
  5. 在建的索列上有函数操作,都不走索引,比如EXPLAIN select FROM tb_test where substr(mobile,1,3)='159'
  6. 组合索引必须满足最左匹配原则,比如:abc,a,ab,abc,ac

7.7:redis如何实现分布式锁

redis分布式锁其实就是往redis设置一个key和value同时设置一个有效时间,并且redis是单线程的,不会并发操作,(执行任务完成后),再把redis的key删除掉(解锁),但是在使用的过程中可能有2个问题,使用过程中我们必须保证设置key和value和设置时间保证它的原子性(LUA),另外还是锁超时问题,比如:上锁2秒钟,但是任务执行超过2秒,我们一般用redission框架,它底层是lua脚本实现,可以保证设置值和时间的原子性,另外还有看门狗的机制,watch dog,我们上锁3秒,但是任务执行5秒,它会自动加时间

7.8: jVm内存模型,以及里面存的变量

7.9:垃圾回收器

简单介绍一下GC垃圾回收器:GC 就是负责回收程序不用的内存。一般的高级语言里面会自带 GC,比如 Java、Python、JavaScript 等,也有无 GC 的语言,比如 C、C++ 等

8.0:垃圾回收算法

标记清除策略:是JS中最常用的垃圾回收策略。 各大浏览器把回收效率进行修改后,进行使用。

整个标记清除算法大致过程就像下面这样:

  • 垃圾收集器在运行时会给内存中的所有变量都加上一个标记,假设内存中所有对象都是垃圾,全标记为0
  • 然后从各个根对象开始遍历,把不是垃圾的节点改成1
  • 清理所有标记为0的垃圾,销毁并回收它们所占用的内存空间
  • 最后,把所有内存中对象标记修改为0,等待下一轮垃圾回收 优点:实现非常简单;

缺点:

  • 内存碎片化,空闲内存块是不连续的,容易出现很多空闲内存块,还可能会出现分配所需内存过大的对象时找不到合适的块
  • 分配速度慢,因为即便是使用分配速度和效率最快的 First-fit 策略,其操作仍是一个 O(n) 的操作,最坏情况是每次都要遍历到最后,同时因为碎片化,大对象的分配效率会更慢

标记整理算法 垃圾回收结束后,会将活着的对象,向内存的一段移动,清除掉边界的内存 :

新生代老年代垃圾回收策略::::TODO

新生代(Young generation)

绝大多数最新被创建的对象会被分配到这里,由于大部分对象在创建后会很快变得不可达,所以很多对象被创建在新生代,然后消失。对象从这个区域消失的过程我们称之为 minor GC

新生代 中存在一个Eden区和两个Survivor区。新对象会首先分配在Eden中(如果新对象过大,会直接分配在老年代中)。在GC中,Eden中的对象会被移动到Survivor中,直至对象满足一定的年纪(定义为熬过GC的次数),会被移动到老年代

可以设置新生代老年代的相对大小。这种方式的优点是新生代大小会随着整个大小动态扩展。参数 -XX:NewRatio 设置老年代新生代的比例。

老年代(Old generation)

对象没有变得不可达,并且从新生代中存活下来,会被拷贝到这里。其所占用的空间要比新生代多。也正由于其相对较大的空间,发生在老年代上的GC要比新生代少得多。对象从老年代中消失的过程,可以称之为full GC

8.2:spring事务传播特性

目前,Spring在TransactionDefinition类中定义了以下7种传播特性,使用@Transactional注解

  • PROPAGATION_REQUIRED:如果不存在外层事务,就主动创建事务;否则使用外层事务
  • PROPAGATION_SUPPORTS:如果不存在外层事务,就不开启事务;否则使用外层事务
  • PROPAGATION_MANDATORY:如果不存在外层事务,就抛出异常;否则使用外层事务
  • PROPAGATION_REQUIRES_NEW:总是主动开启事务;如果存在外层事务,就将外层事务挂起
  • PROPAGATION_NOT_SUPPORTED:总是不开启事务;如果存在外层事务,就将外层事务挂起
  • PROPAGATION_NEVER:总是不开启事务;如果存在外层事务,则抛出异常
  • PROPAGATION_NESTED:如果不存在外层事务,就主动创建事务;否则创建嵌套的子事务

8.3:spingbean的作用域范围

  • singleton : 唯一 bean 实例,Spring 中的 bean 默认都是单例的。
  • prototype : 每次请求都会创建一个新的 bean 实例。
  • request : 每一次 HTTP 请求都会产生一个新的 bean,该 bean 仅在当前 HTTP request 内有效。
  • session : 每一次 HTTP 请求都会产生一个新的 bean,该 bean 仅在当前 HTTP session 内有效。
  • global-session: 全局 session 作用域,仅仅在基于 Portlet 的 web 应用中才有意义,Spring5 已经没有了。Portlet 是能够生成语义代码(例如:HTML)片段的小型 Java Web 插件。它们基于 portlet 容器,可以像 servlet 一样处理 HTTP 请求。但是,与 servlet 不同,每个 portlet 都有不同的会话。

8.4:项目周期

根据实际情况填写

8.5:springBoot底层原理

Spring Boot 底层实际上主要的有三个注解:

ConfigurationEnableAutoConfiguationComponentScan,

它会读取META-INF下的spring.factories文件信息,通过反射的机制把bean纳入到spring的管理。 约定大于配置

8.6:MySQL索引底层

8.7:Eureka的底层原理

Eureka主要是通过心跳检测去判断的,有一个发送者和客户端,发送者会每隔30秒发送一个心跳到eureka上去,服务端会把eureka上的客户端发送的数据进行一个接受并调用的过程,如果说生产者没有发送心跳到注册中心上,那么就将所有的接口都剔除掉,如果中途eureka发生了宕机,那么也是可以进行调用的,因为将原来的数据放到了一个缓存中去,

Eureka还有自我保护机制,如果在15分钟内检测到有85%的服务都宕机了,那么系统会认为是网络的问题导致的

image.png

8.8:Eureka宕机后还可以提供服务吗

可以调通

  1. Eureka 作为一个服务注册中心启动。
  2. S1 和 S2 分别作为服务启动,并且注册到 Eureka 上面去,以 S1 为例,S1 注册时会把自己的元数据信息告诉 eureka(例:我叫 S1,我的地址是 xx.xx.xx.xx,我的端口是 xx,我的 xx 是 xx)。同理,S2 也是如此。
  3. 接下来,S2 要调用 S1 的接口,但是它不知道 S1 的地址是什么,他只知道要调用的服务叫 S1,于是 S2 找到 eureka,从 eureka 上查询出来 S1 的具体地址和端口,这个具体的地址和端口,可能是一个,也可能是多个(集群化部署)。
  4. S2 获取到 S1 的地址和端口之后,接下来就直接去调用 S1 了。 虽然可以调通,但是有前提:那就是S1的地址不能改变

8.9:分布式事务如何解决

9.0:为什么使用mq

异步(发送手机验证码),

解耦(借款系统和风控系统),

削峰(秒杀,购买商品,放入消息队列排队)

9.1:spring如何解决循环依赖

使用三级缓存解决循环依赖

11a8d7009fc171e7a665f78f9b168ac.png

9.2:SpringMVC流程

  1. 客户端(浏览器)发送请求,直接请求到DispatcherServlet
  2. DispatcherServlet根据请求信息调用HandlerMapping,解析请求对应的Handler
  3. 解析到对应的Handler后,开始由HandlerAdapter适配器处理。
  4. HandlerAdapter会根据Handler来调用真正的处理器开处理请求,并处理相应的业务逻辑
  5. 处理器处理完业务后,会返回一个ModelAndView对象,Model是返回的数据对象,View是个逻辑上的View。
  6. ViewResolver会根据逻辑View查找实际的View
  7. DispaterServlet把返回的Model传给View。
  8. 通过View返回给请求者(浏览器)

image.png

9.3:hashmap的key存储一个Object对象会怎么

用自定义类作为key,必须重写继承自Object类的equals()和hashCode()方法。

分析hashMap的put方法可知:是把key进行hash,然后存储,如果key是Object,并且没有重写hashcode,那么它将用物理地址进行hash,否则将使用对象值进行hash。

总结:Object作为key时,不重写equals和hashCode函数,会将相同的对象值相同)存入。在获取时也是先找相同的hash,再找值相同的对象。 如果不重写HashCode,无论equals重写与否,都是在table数组两个不同链表中;重写HashCode,不重写equals,则在table数组的同一个链表中;只有两个函数都重写,才能保证唯一性

9.4:对称加密、非对称加密

对称加密: 信息的发送方和接收方使用同一个密钥去加密和解密数据。对称加密的特点是算法公开加密和解密速度快,适合于对大数据量进行加密。 常用算法:AESDES

image.png 非对称加密: 加密和解密不是同一个密钥,一般有一个公钥,一个私钥,公钥加密,私钥解密,算法有RSA

image.png

9.5:Arraylist和LinkedList区别

Arraylist是基于数组的,在查询效率比较高,插入删除效率比较低

Linkedlist是基于链表的,插入删除效率比较高,查询效率比较低

对于添加和删除的时候,linkedlist优于arraylist,因为arraylist在做数据的添加和删除的时候需要有数据的位置的移动

9.6:线程池底层原理

创建线程池的时候,开始一个线程也没有,随着任务的提交创建线程,当前线程如果小于corePoolSize核心线程数,继续创建线程, 否则(当前线程大于或等于核心线程数)放入LinkedBlockingQueue队列,如果队列没有满,继续放入,如果队列满了, 判断是否小于最大线程数,如果小于继续创建线程,否则拒绝策略(默认拒绝策略抛异常)

9.7:拒绝策略

1、使用线程解决

2、直接拒绝不抛异常

3、直接拒绝抛出异常(默认)

4、将最早的线程舍弃,将最新线程添加

9.8:jvm调优

① 分析 GC 日志及 dump 文件,判断是否需要优化,确定问题根源所在 ② 确定 JVM 调优量化目标和 JVM调优参数(JVM 调优参数根据历史 JVM 参数来调整) ③ 依次调优内存、延迟、吞吐量等指标 ④ 对比观察调优前后的差异,同时需要不断的分析和调整参数,直到找到合适的 JVM 参数配置; ⑤ 找到最合适的参数,将这些参数应用到所有服务器,并进行后续跟踪。

我们把上面的步骤总结一下其实也就是分三步:首先就是分析日志,确认是否需要调优;其次是确认调优目标及调优参数;最后一步就是反复测试并进行追踪

上面的操作步骤中,某些步骤是需要多次不断迭代完成的(比如调整参数、追踪调优后的幸能)。这里有一点是需要注意的,我们调优的时候一般是从满足程序的内存使用需求开始的,接下来是满足程序的时间延迟的要求,最后才是满足程序吞吐量的要求,我们要遵循这个步骤来不断优化,每一个步骤都是进行下一步的基础,绝对不能反其道而行之