美文网首页
Java面试问题

Java面试问题

作者: 以码为荣 | 来源:发表于2023-02-15 17:29 被阅读0次

强引用:Object obj = new Object(),系统不会回收

软引用:描述一些有用但是非必要的对象

弱引用:非必须对象,生存到下一次垃圾回收之前

虚引用:只是为了能在这个对象被收集器回收时收到一个系统通知

// 懒汉模式 + synchronized 同步锁 + double-check

public final class Singleton {

    private volatile static Singleton instance= null;// 不实例化

    public List<String> list = null;//list 属性

    private Singleton(){

      list = new ArrayList<String>();

    }// 构造函数

    public static Singleton getInstance(){// 加同步锁,通过该函数向整个系统提供实例

        if(null == instance){// 第一次判断,当 instance 为 null 时,则实例化对象,否则直接返回对象

          synchronized (Singleton.class){// 同步锁

            if(null == instance){// 第二次判断

                instance = new Singleton();// 实例化对象

            }

          }

        }

        return instance;// 返回已存在的对象

    }

}

// 懒汉模式 内部类实现

public final class Singleton {

    public List<String> list = null;// list 属性

    private Singleton() {// 构造函数

        list = new ArrayList<String>();

    }

    // 内部类实现

    public static class InnerSingleton {

        private static Singleton instance=new Singleton();// 自行创建实例

    }

    public static Singleton getInstance() {

        return InnerSingleton.instance;// 返回内部类中的静态变量

    }

}

1.spring的理解

spring是一个开源框架,04年发布1.0版本,到现在6.0版本,从完全基于配置文件到现在基于注解的方式。主要核心是:

控制反转(IOC),传统的 java 开发模式中,当需要一个对象时,我们会自己使用 new 或者 getInstance 等直接或者间接调用构造方法创建一个对象。而在 spring 开发模式中,spring 容器使用了工厂模式为我们创建了所需要的对象,不需要我们自己创建了,直接调用spring 提供的对象就可以了,这是控制反转的思想。

依赖注入(DI),spring 使用 javaBean 对象的 set 方法或者带参数的构造方法为我们在创建所需对象时将其属性自动设置所需要的值的过程,就是依赖注入的思想。

面向切面编程(AOP),在面向对象编程(oop)思想中,我们将事物纵向抽成一个个的对象。而在面向切面编程中,我们将一个个的对象某些类似的方面横向抽成一个切面,对这个切面进行一些如权限控制、事物管理,记录日志等公用操作处理的过程就是面向切面编程的思想。AOP 底层是动态代理,如果是接口采用 JDK 动态代理,如果是类采用CGLIB 方式实现动态代理。JDK动态代理不需要基于第三方实现,只需要实现 InvocationHandler 接口,使用 Proxy.newProxyInstance() 方法生成代理对象。它只能为接口创建动态代理。

2.spring使用的设计模式有哪些

(1)工厂模式:Spring使用工厂模式,通过BeanFactory和ApplicationContext来创建对象

(2)单例模式:Bean默认为单例模式

(3)策略模式:例如Resource的实现类,针对不同的资源文件,实现了不同方式的资源获取策略

(4)代理模式:Spring的AOP功能用到了JDK的动态代理和CGLIB字节码生成技术

(5)模板方法:可以将相同部分的代码放在父类中,而将不同的代码放入不同的子类中,用来解决代码重复的问题。比如RestTemplate, JmsTemplate, JpaTemplate

(6)适配器模式:Spring AOP的增强或通知(Advice)使用到了适配器模式,Spring MVC中也是用到了适配器模式适配Controller

(7)观察者模式:Spring事件驱动模型就是观察者模式的一个经典应用。

(8)桥接模式:可以根据客户的需求能够动态切换不同的数据源。比如我们的项目需要连接多个数据库,客户在每次访问中根据需要会去访问不同的数据库

3.Autowired和Resource的区别

1).来源不同:@Autowired 来自 Spring 框架,而 @Resource 来自于(Java)JSR-250;

2).依赖查找的顺序不同:@Autowired 先根据类型再根据名称查询,而 @Resource 先根据名称再根据类型查询;

3).支持的参数不同:@Autowired 只支持设置 1 个参数,而 @Resource 支持设置 7 个参数;

4).依赖注入的用法支持不同:@Autowired 既支持构造方法注入,又支持属性注入和 Setter 注入,而 @Resource 只支持属性注入和 Setter 注入;

5).编译器 IDEA 的提示不同:当注入 Mapper 对象时,使用 @Autowired 注解编译器会提示错误,而使用 @Resource 注解则不会提示错误。

4.介绍下Spring中的常用注解

@Autowired @Resource @Controller @Service

5.谈谈你对循环依赖的理解

A对象创建需要依赖B对象,B对象创建需要依赖A对象

6.Spring中如果解决循环依赖的

通过三级缓存方式解决。在 Spring 中,只有同时满足以下两点才能解决循环依赖的问题:

依赖的 Bean 必须都是单例。

依赖注入的方式,必须不全是构造器注入,且 beanName 字母序在前的不能是构造器注入。

7.Spring中构造注入的循环依赖问题

在 Spring 中创建 Bean 分三步:

实例化,createBeanInstance,就是 new 了个对象。

属性注入,populateBean, 就是 set 一些属性值。

初始化,initializeBean,执行一些 aware 接口中的方法,initMethod,AOP代理等。

明确了上面这三点,再结合我上面说的“不完整的”,我们来理一下。

如果全是构造器注入,比如A(B b),那表明在 new 的时候,就需要得到 B,此时需要 new B 。

但是 B 也是要在构造的时候注入 A ,即B(A a),这时候 B 需要在一个 map 中找到不完整的 A ,发现找不到。

为什么找不到?因为 A 还没 new 完呢,所以找到不完整的 A,因此如果全是构造器注入的话,那么 Spring 无法处理循环依赖。

8.Spring循环依赖三级缓存问题

一级缓存,singletonObjects,存储所有已创建完毕的单例 Bean (完整的 Bean)。

二级缓存,earlySingletonObjects,存储所有仅完成实例化,但还未进行属性注入和初始化的 Bean。

三级缓存,singletonFactories,存储能建立这个 Bean 的一个工厂,通过工厂能获取这个 Bean,延迟化 Bean 的生成,工厂生成的 Bean 会塞入二级缓存。

这三个 map 是如何配合的呢?

首先,获取单例 Bean 的时候会通过 BeanName 先去 singletonObjects(一级缓存) 查找完整的 Bean,如果找到则直接返回,否则进行步骤 2。

看对应的 Bean 是否在创建中,如果不在直接返回找不到,如果是,则会去 earlySingletonObjects (二级缓存)查找 Bean,如果找到则返回,否则进行步骤 3

去 singletonFactories (三级缓存)通过 BeanName 查找到对应的工厂,如果存着工厂则通过工厂创建 Bean ,并且放置到 earlySingletonObjects 中。

如果三个缓存都没找到,则返回 null。

正常代理对象的生成是基于后置处理器,是在被代理的对象初始化后期调用生成的,所以如果你提早代理了其实是违背了 Bean 定义的生命周期。

所以 Spring 先在一个三级缓存放置一个工厂,如果产生循环依赖,那么就调用这个工厂提早得到代理对象。

9.Spring中bean的生命周期

实例化-》属性注入-》初始化-》使用-》销毁

10.Spring中支持几种作用域

singleton,prototype,request,session,global session

11.谈谈sping中的事务隔离级别

Isolation.DEFAULT:默认的事务隔离级别,以连接的数据库的事务隔离级别为准。

Isolation.READ_UNCOMMITTED:读未提交,可以读取到未提交的事务,存在脏读。

Isolation.READ_COMMITTED:读已提交,只能读取到已经提交的事务,解决了脏读,存在不可重复读。

Isolation.REPEATABLE_READ:可重复读,解决了不可重复读,但存在幻读(MySQL 数据库默认的事务隔离级别)。

Isolation.SERIALIZABLE:串行化,可以解决所有并发问题,但性能太低。

13.谈谈spring中的事务实现方式

(1)编程式事务管理对基于 POJO 的应用来说是唯一选择。我们需要在代码中调用beginTransaction()、commit()、rollback()等事务管理相关的方法,这就是编程式事务管理。

(2)基于 TransactionProxyFactoryBean的声明式事务管理

(3)基于 @Transactional 的声明式事务管理

(4)基于Aspectj AOP配置事务

14.谈谈spring中的事务本质

Spring 事务的本质,其实就是通过 Spring AOP 切面技术,在合适的地方开启事务,接着在合适的地方提交事务或回滚事务,从而实现了业务编程层面的事务操作。

15.BeanFactory和applicationContext的理解

BeanFactory负责读取bean配置文档,管理bean的加载,实例化,维护bean之间的依赖关系,负责bean的声明周期。

ApplicationContext除了提供上述BeanFactory所能提供的功能之外,还提供了更完整的框架功能:

a. 国际化支持

b. 资源访问:Resource rs = ctx. getResource(“classpath:config.properties”), “file:c:/config.properties”

c. 事件传递:通过实现ApplicationContextAware接口。

16.springmvc

17.springboot的自动装配原理

18.谈谈import注解的理解

19.Spring 有哪几种事务传播行为?

PROPAGATION_REQUIRED(默认) 如果当前存在事务,则用当前事务,如果没有事务则新起一个事务

PROPAGATION_SUPPORTS 支持当前事务,如果不存在,则以非事务方式执行

PROPAGATION_MANDATORY 支持当前事务,如果不存在,则抛出异常

PROPAGATION_REQUIRES_NEW 创建一个新事务,如果存在当前事务,则挂起当前事务

PROPAGATION_NOT_SUPPORTED 不支持当前事务,始终以非事务方式执行

PROPAGATION_NEVER 不支持当前事务,如果当前存在事务,则抛出异常

PROPAGATION_NESTED 如果当前事务存在,则在嵌套事务中执行,内层事务依赖外层事务,如果外层失败,则会回滚内层,内层失败不影响外层。

## 19.Integer ==比较,有时候相等,有时候不相等?

在Integer类装入内存中时,会执行其内部类中静态代码块进行其初始化工作,做的主要工作就是把 [-128,127]之间的数包装成Integer类并把其对应的引用存入到cache数组中,这样在方法区中开辟空间存放这些静态Integer变量,同时静态cache数组也存放在这里,供线程享用,这也称静态缓存

## 20.CUP飙高排插

1.上下文切换频繁,为了保证线程状态,消耗资源

2.创建线程太多,导致资源消耗过快

根据top命令查看,一种是一直是一个线程,可能是死循环,可以jstack dump查看,一种是多个线程,挑选几个jstack dump查看

## 21.常见的分布式锁

### 21.1.Redis方案

通过set key value nx px millo** 命令实现 但是会出现锁到期了,服务还没执行,其他服务获取到锁,导致服务A释放服务B锁的情况

通过Redisson实现,有watchdog实现锁自动续期,但是会出现主从锁丢失问题,获取到锁,还没同步就宕机了,导致其他线程也获取到锁,操作相同资源

可以用RedLock,但是极端情况下,比如时间跳跃会导致锁释放,其他服务获取到锁

### 21.2.zookeeper临时顺序节点实现

1、每个获取锁的线程会在zk的某一个目录下创建一个临时有序的节点。

2、节点创建成功后,判断当前线程创建的节点的序号是否是最小

3、如果序号是最小的,那么获取锁成

4、如果序号不是最小的,则对他序号的前一个节点添加事件监听。如果前一个节点被删了(锁被释放了),那么就会唤醒当前节点,则成功获取到锁。

zookeeper CP原则

优点:

1、不用设置过期时间

2、事件监听机制,加锁失败后,可以等待锁释放

缺点:

1、性能不如redis

2、当网络不稳定时,可能会有多个节点同时获取锁问题。例:node1由于网络波动,导致zk将其删除,刚好node2获取到锁,那么此时node1和node2两者都锁。

Redis AP原则

优点:性能上比较好,天然的支持高并发

缺点:

1、获取锁失败后,得轮询的去获2、大多数情况下redis无法保证数据强一致性

## 22.基于Redis和Mysql的架构,如果保证数据的一致性

1.基于rocketMQ的可靠性消息通信,实现最终的一致性

2.通过canal组件监控mysql中的binlog日志,将数据同步到redis,实现最终一致性

## 23.ArrayList扩容机制

oldCapacity >> 1    右移运算符  原来长度的一半 再加上原长度也就是每次扩容是原来的1.5倍

然后通过Arrays.copyof(elementData,newCapacity) 完成数据迁移

## 24 依赖注入的方式有几种

1.构造器注入,缺点:构造器参数列表将会很长,不够灵活

2.setter方法注入,缺点:依赖对象初始化完成后由于尚未注入被依赖对象,因此还不能使用

3.接口注入,缺点:代码入侵太强

## 25 Java 创建对象有几种方式?

1.通过new()创建 2.反射创建 3.使用 clone 方法 4.使用反序列化创建对象,调用 ObjectInputStream 类的 readObject() 方法。

## 26 Consul和Eureka

Consul:强一致性(CP):

1️服务注册相比Eureka会稍慢一些。因为Consul的Raft协议要求必须过半的节点都写入成功才认为注册成功。

2️Leader挂掉后,重新选举期间整个Consul不可用。保证了强一致性,但牺牲了可用性。

Eureka:高可用性和最终一致性(AP):

1️服务注册相对要快,因为不需要等注册信息复制到其他节点,也不保证注册信息是否复制成功。

2️当数据出现不一致的时候,虽然A,B上的注册信息不完全相同,但是每个Eureka节点依然能够正常对外提供服务,这会出现查询服务信息时如果请求A查不到,但请求B就能查到。如果保证了可用性但牺牲了一致性。

## 27 Cassandra和MongoDB的区别

1.高可用性策略

Cassandra可以部署多个主节点,如果一个或多个主节点过来,至少有一个主节点可用服务就可用。

MongoDb只有一个主节点,通过故障转移实现可用,在MongoDB集群中设置一个主节点。如果主站发生故障,从站节点将自动转变为新的主站点。这确保了连续性,但这不会立即发生,通常需要将近一分钟

2.写入速度

Cassandra有多个主节点,可以并行写入,效率更高

3.存储结构

Cassandra是基于列式存储,跟传统的SQL很像,MongoDb是文档存储结构,可以存储对象

## 28 分布式ID生成方案

1. UUID

算法的核心思想是结合机器的网卡、当地时间、一个随记数来生成UUID。

优点:本地生成,生成简单,性能好,没有高可用风险

缺点:长度过长,存储冗余,且无序不可读,查询效率低

2.数据库自增ID

使用数据库的id自增策略,如 MySQL 的 auto_increment。并且可以使用两台数据库分别设置不同步长,生成不重复ID的策略来实现高可用。

优点:数据库生成的ID绝对有序,高可用实现方式简单

缺点:需要独立部署数据库实例,成本高,有性能瓶颈

3.Redis生成ID

Redis的所有命令操作都是单线程的,本身提供像 incr 和 increby 这样的自增原子命令,所以能保证生成的 ID 肯定是唯一有序的。

优点:不依赖于数据库,灵活方便,且性能优于数据库;数字ID天然排序,对分页或者需要排序的结果很有帮助。

缺点:如果系统中没有Redis,还需要引入新的组件,增加系统复杂度;需要编码和配置的工作量比较大。

4.Twitter的snowflake算法

优点:高性能,低延迟,按时间有序,一般不会造成ID碰撞

缺点:需要独立的开发和部署,依赖于机器的时钟

## 29 常见的幂等性解决方案

1. 唯一索引,防止新增脏数据

2. token机制,防止页面重复提交

处理流程:

数据提交前要向服务申请token,token放到redis或内存,token有效时间。提交后后台校验token,同时删除token,生成新的token返回

token特点:

要申请,一次有效性,可以限流

3. 悲观锁

获取数据的时候加锁获取

select * from table_xxx where id=‘xxx’ for update;

注意:id字段一定是主键或者唯一索引,不然是锁表,会死人的

悲观锁使用时一般伴随事务一起使用,数据锁定时间可能会很长,根据实际情况选用

4. 乐观锁

乐观锁只是在更新数据那一刻锁表,其他时间不锁表,所以相对于悲观锁,效率更高。

乐观锁的实现方式多种多样可以通过version或者其他状态条件:

通过版本号实现

update table_xxx set name=#name#,version=version+1 where version=#version#

5. 分布式锁

6. select + insert

7. 对外提供接口的api如何保证幂等

## 30 mabatis一级缓存,二级缓存

一级缓存:

每个 SqlSession 中持有了 Executor,每个 Executor 中有一个 LocalCache。当用户发起查询时,MyBatis 根据当前执行的语句生成 MappedStatement,在 Local Cache 进行查询,如果缓存命中的话,直接返回结果给用户,如果缓存没有命中的话,查询数据库,结果写入 Local Cache,最后返回结果给用户

二级缓存

一级缓存中,其最大的共享范围就是一个 SqlSession 内部,如果多个 SqlSession之间需要共享缓存,则需要使用到二级缓存。开启二级缓存后,会使用 CachingExecutor 装饰Executor,进入一级缓存的查询流程前,先在 CachingExecutor 进行二级缓存的查询。二级缓存开启后,同一个 namespace 下的所有操作语句,都影响着同一个 Cache,即二级缓存被多个 SqlSession 共享,是一个全局的变量。

## 31 了解文本相似度 TF-IDF吗

TF = Term Frequency 词频,一个词在这个文档中出现的频率。值越大,说明这文档越匹配,正向指标。

IDF = Inverse Document Frequency 反向文档频率,简单点说就是一个词在所有文档中都出现,那么这个词不重要。比如“的、了、我、好”这些词所有文档都出现,对检索毫无帮助。反向指标。

## 32 Kafka消息重复解决方案

主要原因是offset的提交失败,它本身有个机制,每5秒提交一次,然后也有balance机制,消息会均衡分不到不同partion,如果在相应时间没消费完,会触发balance,导致offset提交失败

1.提高消费端的处理性能避免触发Balance

2.使用ConsumerRebalanceLister,再均衡监听器

3.使用消息幂等性 prop.put()

4.去重表,将消息生成md5然后保存到mysql或者redis,在处理消息之前先去查mysql或者redis

## 33 如何确保Kafka消息不丢失

生产者:ack机制 将发送消息的确认机制设置为-1,使得所有节点确认后再发送下一条数据即可

消费者:如果消息在处理完成前就提交了offset,就有可能造成数据的丢失。解决办法是在后台提交位移前确保消息已经被正常处理了,然后手动提交offset

## 35 索引失效

1.对索引使用左或者左右模糊匹配,特殊情况 name like “%a”,如果表里面没有非索引字段,比如只有一个id主键和一个索引name字段,也是走索引,会是全扫描二级索引的 B+ 树的方式查询到数据

2.对索引使用函数 where lenght(name) = 10

3.对索引使用表达式 where a+1=2

4.对索引隐式类型转换 phone是varchar类似 where phone = 10000

5.联合索引非最左匹配 where a = 1 and c = 2,特殊情况,如果只有id,(a,b,c)联合索引,一共四个字段,where c = 1也会走索引

6.WHERE 子句中的 OR。在 WHERE 子句中,如果在 OR 前的条件列是索引列,而在 OR 后的条件列不是索引列,那么索引会失效。

count(*)=count(1)>count(主键索引)>count(字段) count(*)其实就是count(0),count(字段)会全表扫描,效率低

## 34 where a = 1 and c = 3 ,符合最左匹配吗?(索引截断)

MySQL 5.5 的话,前面 a 会走索引,在联合索引找到主键值后,开始回表,到主键索引读取数据行,Server 层从存储引擎层获取到数据行后,然后在 Server 层再比对 c 字段的值。

从 MySQL 5.6 之后,有一个索引下推功能,可以在存储引擎层进行索引遍历过程中,对索引中包含的字段先做判断,直接过滤掉不满足条件的记录,再返还给 Server 层,从而减少回表次数。

索引下推的大概原理是:截断的字段不会在 Server 层进行条件判断,而是会被下推到「存储引擎层」进行条件判断(因为 c 字段的值是在 (a, b, c) 联合索引里的),然后过滤出符合条件的数据后再返回给 Server 层。由于在引擎层就过滤掉大量的数据,无需再回表读取数据来进行判断,减少回表次数,从而提升了性能。

## 35 count(*),count(1),count(主键字段),count(字段)

count(1)、 count(*)、 count(主键字段)在执行的时候,如果表里存在二级索引,优化器就会选择二级索引进行扫描,因为二级索引存储的没有主键索引多。

所以,如果要执行 count(1)、 count(*)、 count(主键字段) 时,尽量在数据表上建立二级索引,这样优化器会自动采用 key_len 最小的二级索引进行扫描,相比于扫描主键索引效率会高一些。

再来,就是不要使用 count(字段) 来统计记录个数,因为它的效率是最差的,会采用全表扫描的方式来统计。如果你非要统计表中该字段不为 NULL 的记录个数,建议给这个字段建立一个二级索引。

针对快照读(普通 select 语句),是通过 MVCC 方式解决了幻读。

针对当前读(select ... for update 等语句),是通过 next-key lock(记录锁+间隙锁)方式解决了幻读。

第一个例子:对于快照读, MVCC 并不能完全避免幻读现象。因为当事务 A 更新了一条事务 B 插入的记录,那么事务 A 前后两次查询的记录条目就不一样了,所以就发生幻读。

第二个例子:对于当前读,如果事务开启后,并没有执行当前读,而是先快照读,然后这期间如果其他事务插入了一条记录,那么事务后续使用当前读进行查询的时候,就会发现两次查询的记录条目就不一样了,所以就发生幻读。

所以,MySQL 可重复读隔离级别并没有彻底解决幻读,只是很大程度上避免了幻读现象的发生。

要避免这类特殊场景下发生幻读的现象的话,就是尽量在开启事务之后,马上执行 select ... for update 这类当前读的语句,因为它会对记录加 next-key lock,从而避免其他事务插入一条新记录。

## 36 redis大key的影响

1.当 AOF 写回策略配置了 Always 策略,如果写入是一个大 Key,主线程在执行 fsync() 函数的时候,阻塞的时间会比较久,因为当写入的数据量很大的时候,数据同步到硬盘这个过程是很耗时的。

2.客户端超时阻塞。由于 Redis 执行命令是单线程处理,然后在操作大 key 时会比较耗时,那么就会阻塞 Redis,从客户端这一视角看,就是很久很久都没有响应。

3.引发网络阻塞。每次获取大 key 产生的网络流量较大,如果一个 key 的大小是 1 MB,每秒访问量为 1000,那么每秒会产生 1000MB 的流量,这对于普通千兆网卡的服务器来说是灾难性的。

4.阻塞工作线程。如果使用 del 删除大 key 时,会阻塞工作线程,这样就没办法处理后续的命令。

5.内存分布不均。集群模型在 slot 分片均匀情况下,会出现数据和查询倾斜情况,部分有大 key 的 Redis 节点占用内存多,QPS 也会比较大。

## 37 springcloud由以下⼏个核⼼组件构成:

Eureka:各个服务启动时,Eureka Client都会将服务注册到Eureka Server,并且Eureka Client还可以反过来从Eureka Server拉取注册表,从⽽知道其他服务在哪⾥

Ribbon:服务间发起请求的时候,基于Ribbon做负载均衡,从⼀个服务的多台机器中选择⼀台

Feign:基于Feign的动态代理机制,根据注解和选择的机器,拼接请求URL地址,发起请求

Hystrix:发起请求是通过Hystrix的线程池来⾛的,不同的服务⾛不同的线程池,实现了不同服务调⽤的隔离,避免了服务雪崩的问题

Zuul:如果前端、移动端要调⽤后端系统,统⼀从Zuul⽹关进⼊,由Zuul⽹关转发请求给对应的服务

## 38 synchronized关键字

synchronized关键字实现对象锁,不管是实例方法还是静态方法,我们都知道对象本身包括三部分吧,对象头:Mark Word,用于存储对象自身运行时的数据,如哈希码(Hash Code),GC分代年龄,锁状态标志,偏向线程ID、偏向时间戳等信息,它会根据对象的状态复用自己的存储空间。它是实现轻量级锁和偏向锁的关键。

实例数据:存放类的属性数据信息,包括父类的属性信息。对齐填充,主要是内存对齐,非必须。通过反编译可以发现,synchronized底层通过monitor对象实现的。它会关联一个monitor对象,monitorenter,线程进入就进入数+1,拥有者可以重复进入,执行monitorexit,进入数就减1,直到进入数为0,放弃对monitor对象的拥有权

## 39 如何判断一个方法区的类是无用的类

类需要同时满足下面 3 个条件才能算是 “无用的类”,虚拟机可以对无用类进行回收。

1.该类所有的实例都已经被回收,也就是 Java 堆中不存在该类的任何实例。

2.加载该类的 ClassLoader 已经被回收。

3.该类对应的 java.lang.Class 对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。

## 40 springboot 自动装配

springBoot在启动的时候会调用run()方法去刷新容器,刷新容器的时候,会扫描classpath目录下的META-INF/spring.factories文件,找到key为EnableAutoConfiguration的配置,并将其下面的配置类都加载到importselector中,随后AutoConfigurationImportSelector会将ImportSelector中的所有标明的类加载到IOC容器中

## 41Java OOM处理

1.一次性申请对象太多:更改申请对象数量,分页之类的

2.内存资源耗尽未释放:找到未释放的对象释放

3.本身资源不够:jmap -heap查看堆信息

Java visualVM

最快oom方法,new一个list,然后死循环往里面插入数据

1.线上出现oom

通过启动参数-xx:+heapDumponoutofmemoryerror -xx:heapdumppath =

2.线上没有出现oom

通过jmap dump:format=b,file=**.hprof 进程id

通过visuakvm查看最多跟业务有关对象->找到GCRoot->查看线程栈

java堆用于存储对象实例,我们只要不断的创建对象,并且保证GC Roots到对象之间有可达路径来 避免垃圾回收机制清除这些对象,就会在对象数量达到最大堆容量限制后产生内存溢出异常。

出现这种异常,一般手段是先通过内存映像分析工具(如Eclipse Memory Analyzer)对dump出来的 堆转存快照进行分析,重点是确认内存中的对象是否是必要的,先分清是因为内存泄漏(Memory Leak)还是内存溢出(Memory Overflow)。

如果是内存泄漏,可进一步通过工具查看泄漏对象到GCRoots的引用链。于是就能找到泄漏对象是 通过怎样的路径与GC Roots相关联并导致垃圾收集器无法自动回收。

如果不存在泄漏,那就应该检查虚拟机的参数(-Xmx与-Xms)的设置是否适当。

##42接口优化

1.批量处理:比如循环插入数据库,可以改成批量插入,减少磁盘io,提升性能

2.异步处理:针对耗时很长,结果不是必须马上返回的,可以使用异步处理,比如用消息队列

3.空间换时间:使用缓存策略,减少数据库的访问

4.预处理:提前把要查询的数据计算好,放入数据库或者缓存,可以大大提升接口的查询性能

5.池化思想:比如数据库池,线程池等,避免重复创建对象或者建立连接

6.串行改并行:对于不一定要串行执行的逻辑可以改并行处理

7.索引:构建索引,加快查询效率

8.避免大事务:所谓大事务问题,就是运行时间较长的事务,由于事务一直不提交,会导致数据库连接被占用,影响到别的请求访问数据库,影响别的接口性能。

会引起死锁,锁等待,回滚时间过长,高并发下数据库连接池被占用,接口超时,数据库主从延时。

1. RPC调用不放到事务里面

2. 查询操作尽量放到事务之外

3. 事务中避免处理太多数据

4.尽量少用@transation

5.非事务代码挪出来

6.异步处理

9.优化代码逻辑

10.优化sql

11.控制锁的粒度:避免锁的粒度过粗

##43工作中redis怎么使用的?

1.可以用redis做分布式锁和分布式id

2.可以用redis做缓存,做缓存也有两种用法,第一种就是查询数据的时候如果缓存里有就用缓存,如果没有就从数据库查,再放到缓存中去。更新的时候先更新数据库,再删除缓存。第二种就是redis做数据源,比如我们是做教育的,有很多课程信息变动很少,而且有很多树形结构,如果每次都从数据库查查会很慢。

##44MySQL索引很慢的原因

1.查询sql有问题,查询字段太多,或者join了很多张表。可以优化sql

2.索引区分度不高,导致命中的数据范围很大,可以优化查询条件和索引

3.数据库连接不够,锁等待,或者数据库自身io或者cpu很高,或者网络

不太行。可以结合日志和业务排查。

4.优化器基于成本,选错了索引,明明应该走a索引,但是走了b,如果确定a索引更快,可以用force index

##45Kafka为什么这么快

1.磁盘的顺序读写:数据写入是顺序写入的,避免了磁盘随机io寻址读取

2.稀疏索引:kafka并不是对所有的数据都建立索引,而是通过稀疏索引的方式建立索引,然后再通过二分查找算法提高查询效率

3.批量文件压缩:kafka是不会删除文件的,它是通过批量文件压缩,将多次插入的相同的key对应的value合并成为最后一次插入的value,从而对文件进行合理压缩,减少网络io的损耗。

4.零拷贝机制:正常情况文件的读取是从磁盘拷贝到内核缓冲区,然后再从内核缓冲区到用户缓冲区,再从用户缓冲区到socket缓冲区,最后到网卡。kafka通过使用java nio里面的transferTo方法,最终调用的是linux的sendfile函数实现零拷贝。

##46transaction失效原因

1.作用在非public修饰的方法上。拦截器拦截后,发现不是作用在public修饰的方法上,则不会获取@Transactional 的属性配置信息。

2.@Transactional 注解属性 propagation 设置错误,比如以非事务的方式运行,存在事务则挂起

3.@Transactional 注解属性 rollbackFor 设置错误,如果在事务中抛出其他类型的异常,但却期望 Spring 能够回滚事务,就需要指定rollbackFor属性。

4.同一个类中方法调用,导致@Transactional失效

5. 异常被你的 catch“吃了”导致@Transactional失效

6.数据库引擎不支持事务

相关文章

网友评论

      本文标题:Java面试问题

      本文链接:https://www.haomeiwen.com/subject/jjlukdtx.html