Java 面经

  • 本文为Java面经,其中讲述的是在面试过程中回答得不好的地方,在这里补充,以便为后面的面试积累经验

哈罗单车

一面

Q:公司的MySQL数据库,事务隔离级别是什么?

A:读已提交(RC),一个事务在执行过程中允许访问其他事务成功提交新插入的数据,又能访问修改成功的数据。读取数据的事务允许其他事务继续访问该行数据,但未提交的写事务将禁止其他事务访问该行。可以防止脏读,不能防止不可重复读和幻读。

Q:公司的营销配置端应用机器规格配置和JVM是如何设置的。

A:一开始是4核8G60G,后面升级到了8核16G100G。使用4核8G60G时,JVM的xms和xmx设置的都是4G,使用8核16G100G时,xms和xmx都设置成了8G。MetaSpace设置成了512M。xms是jvm heap(堆内存)最小值,即初始化分配的空间。xmx是jvm heap(堆内存)最大允许值,即设定了程序运行期间最大可占用内存的大小,超过了这个值的大小会导致OOM

二面

Q:实现多线程的方式有哪些?

A:①继承Thread类,重写其run方法来实现多线程;②实现Runnable接口,重写run方法;③实现Callable接口,重写call方法,并使用FutureTask类来包装Callable对象,使用FutureTask对象作为Thread对象的target创建并调用start方法启动线程;④通过线程池实现多线程;

Q:线程池一般用哪些线程池?为什么不推荐使用通过Executors创建的线程池?

A:①自定义线程池;②通过Executors创建的线程池(包含SingleThreadExecutor、CachedThreadPool、FixedThreadPool和ScheduledThreadPool)。

        CachedThreadPool,可缓存线程池,若线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程,该线程池最大程度保证每个请求都能立即被处理。缺点:由于最大线程数被允许为Integer.MAX_VALUE,当线程数过多时,会导致OOM。

        FixedThreadPool,定长线程池,可控制线程池最大并发数,超出的线程会在队列中等待。缺点:由于LinkedBlockingQueue队列是无参构造,默认可存放的队列容量为Integer.MAX_VALUE,当队列中等待的对象过多时,会导致OOM。

        ScheduledThreadPool,周期性线程池,支持任务定时及周期性执行。缺点:同CachedThreadPool。

        SingleThreadExecutor,单线程化线程池,只有一个工作线程可执行,保证所有线程顺序执行。缺点:同FixedThreadPool。

Q:自定义线程池由哪些部分组成?运行的原理是什么?

A:自定义线程池主要由corePoolSize(核心线程数)、maximumPoolSize(最大线程数)、keepAliveTime(线程空闲时间)、unit(keepAliveTime的时间单位)、workQueue(阻塞队列)threadFactory(线程工厂)和handler(拒绝策略的执行器)组成。

        自定义线程池工作原理:

  1. 提交任务,判断corePoolSize是否已满,若未满则判断是否有空闲线程,若没有空闲线程则创建新线程来执行任务,若有空闲线程则直接使用空闲线程执行任务;
  2. 若corePoolSize已满,则判断workQueue是否已满,若workQueue未满则将提交的任务放入workQueue中等待核心线程池执行;
  3. 若workQueue已满则判断maximumPoolSize是否大于corePoolSize,若小于等于,则将新提交的任务交由拒绝策略执行器处理;
  4. 若maximumPoolSize大于corePoolSize,且当先提交线程数大于maximumPoolSize则将新提交的任务交由拒绝策略执行器处理;
  5. 若maximumPoolSize大于corePoolSize,且maximumPoolSize最大线程数未满则将新提交的任务创建新的线程来执行任务;
  6. 当线程池中,超过corePoolSize部分的线程,在其空闲时间超过keepAliveTime和unit的单位时间后,就关闭这部分的线程,使之最终缩减为corePoolSize个线程。如果设置了allowCoreThreadTimeout为ture,则所有线程池中空闲线程超过keepAliveTime后就会被关掉,最终空闲线程数会缩减为零个;

Q:线程池中用到的阻塞队列有哪些?是有界的吗?

A:一般使用到的有ArrayBlockingQueue和LinkedBlockingQueue,都是有界的。其他还有DelayQueue和PriorityBlockingQueue等,都是无界的。

Q:JVM结构由什么组成?都存储了哪些?

A:JVM结构:堆【新生代(Eden区、From Survivor区、To Survivor区)和老年代】、虚拟机栈、本地方法栈、元空间、程序计数器。

        JVM各个组成部分存储的内容:

        堆用于存放对象实例;

        虚拟机栈里是一个个栈帧,每个方法在执行时会创建一个栈帧,栈帧中存放局部变量表、操作数栈、动态链接和返回地址;

        本地方法栈与虚拟机栈类似,不同之处在于本地方法栈为native方法服务,而虚拟机栈为java方法服务,线程私有;

        元空间存储的是已经被Java虚拟机加载的类信息、常量和静态变量;

        程序计数器存储的是当前线程执行的行号指示器,记录当前线程执行到程序哪个位置,通过改变计数器值,可取到下一条需要执行的字节码指令,是比较小的块内存空间,属于线程私有;

Q:使用的jdk版本是哪个版本?用到的JVM垃圾回收器是哪个?

A:jdk版本:JDK8;

jdk8的垃圾回收器:新生代使用的Parallel Scavenge,主要是为了提供高吞吐量垃圾回收服务;老年代使用的CMS垃圾回收器,主要是为了减少应用停顿时间;

Urbanic

一面

Q:JVM类加载过程是什么?

A:大体可以分成五个阶段:加载,验证,准备,解析,初始化

        1、加载:

                ①通过一个类的全限定名来获取定义此类的二进制字节流;

                ②将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构;

                ③在Java堆中生成一个代表这个类的java.lang.class对象,作为方法区这些数据的访问入口;

        2、验证:

                ①文件格式验证(是否符合Class文件格式规范,且能被当前版本虚拟机处理)

                ②元数据验证(对字节码描述的信息进行语义分析,以保证其描述的信息符合Java语言规范要求)

                ③字节码验证(保证被校验类的方法在运行时不会做出危害虚拟机安全的行为)

                ④符号引用验证(虚拟机将符号引用转化为直接引用时,解析阶段中发生)

        3、准备:

                是正式为类变量分配内存并设置类变量初始值的阶段。其中需要将对象初始化为“零”值

        4、解析:

                虚拟机将常量池内的符号引用替换为直接引用的过程。其中将字符串常量池放在堆上,默认是class文件的静态常量池。运行时常量池放在方法区,属于元空间;

        5、初始化:

                加载过程的最后一步,也是真正意义上开始执行类中定义的Java程序代码

Q:Spring Bean生命周期是什么,怎么实例化一个Bean的?

A:整体包含四个阶段:实例化Bean→Bean属性填充→初始化Bean→销毁Bean

        1、实例化Bean:

                就是通常所说的new对象的过程

        2、设置对象属性:

                在Bean创建出来后,给这个Bean对象的属性进行填充配置;

        3、初始化阶段:

                ①检查Aware相关接口并设置相关依赖:如果Bean实现了BeanNameAware接口,会调用实现的setBeanName方法,也就是根据Spring配置文件中Bean的id和name进行传递;如果Bean实现了BeanFactoryAware接口,会调用实现的setBeanFactory,也就是Spring配置文件配置的Spring工厂自身进行传递;如果Bean实现了ApplicationContextAware接口,会调用setApplicationContext方法;

                ②如果Bean实现了BeanPostProcessor接口,会调用实现的postProcessBeforeInitialization的前置处理方法,对Bean进行一些自定义的前置处理;

                ③如果实现了InitializationBean,会调用afterPropertiesSet方法;

                ④如果配置了自定义的init-method方法,则执行其配置的自定义方法;

                ⑤如果Bean实现了BeanPostProcessor接口,调用postProcessAfterInitialization方法

        4、Bean使用阶段:

        5、Bean销毁阶段:

                ①如果Bean实现了DestructionAwareBeanPostProcessor接口,会执行其后置处理器的销毁回调方法;

                ②如果Bean实现了DisposableBean接口,则会调用实现的destory方法;

                ③如果Bean配置了destory-method的自定义销毁方法,则会调用其配置的销毁方法;

二面

Q:使用缓存在读写的时候应该注意什么?

A:

        1、数据库和缓存的一致性问题

                a、使用缓存一般使用旁路缓存模式:即在读请求时,若命中缓存,则直接返回,若未命中缓存,则先从数据库读取数据,读取完数据后将数据设置到缓存后返回数据;在写请求时,先操作数据库,再删除缓存数据;

                b、两个缓存的场景会导致缓存一致性问题:

                        ①场景一:

                                Ⅰ、线程A先发起一个写操作,第一步先更新数据库;

                                Ⅱ、线程B再发起一个写操作,第二步更新了数据库;

                                Ⅲ、由于网络等原因,线程B先更新了缓存;

                                Ⅳ、线程A更新缓存。

                                这时候,缓存保存的是A的数据(老数据),数据库保存的是B的数据(新数据),数据不一致,脏数据产生。如果是删除缓存取代更新缓存则不会出现这个脏数据问题。

                        ②场景二

                                Ⅰ、线程A发起一个写操作,第一步删除缓存;

                                Ⅱ、此时线程B发起一个读操作,缓存丢失;

                                Ⅲ、线程B继续读DB,读出来一个老数据;

                                Ⅳ、然后线程B把老数据设置入cache;

                                Ⅴ、线程A写入DB最新的数据;

                                产生的问题,缓存和数据库的数据不一致了。缓存保存的是老数据,数据库保存的是新数据。因此,Cache-Aside缓存模式,选择了先操作数据库而不是先操作缓存。

                c、保证最终一致性:①缓存延时双删;②删除缓存重试机制;③读取binlog异步删除缓存;

        2、缓存穿透

                a、查询一个一定不存在的数据,缓存未命中,需要查询数据库,查不到数据库则不写入缓存。将导致查不存在的数据每次请求都需要去数据库查询,给数据库带来压力;

                b、如何解决:①对于非法请求,在接口入口处,对参数校验,处理掉非法值;②允许空值的缓存,在写请求时需要同时更新缓存,保证缓存一致性,给缓存设置合理的过期时间;③使用布隆过滤器快速判断数据是否存在;

        3、缓存雪崩

                a、指缓存中大批量数据到了过期时间,而读请求量巨大,导致数据库压力过大甚至宕机的情况;

                b、如何解决:①将缓存过期时间设置得性对离散些;②缓存故障也可能导致缓存雪崩,需要构造Redis高可用集群;

        4、缓存击穿

                a、指热点key在某个时刻过期时恰好对此key有大量的并发请求过来,从而引起大量的请求打到数据库;和缓存雪崩的区别在于缓存雪崩是大量缓存key过期,而缓存击穿则是某一热点key;

                b、如何解决:①使用互斥锁(缓存失效时不立即去读取数据库,先使用某些带成功返回的原子操作命令,如获取锁,成功时取读取数据库并更新缓存);②设置“永不过期”(一个是不设置过期时间;另一个是在热点数据库临近过期时异步线程去更新缓存并重新设置过期时间);

        5、缓存热key

                a、访问频率高的key,称为热点key。若某一热点key的请求到缓存主机时,由于请求量比较大,可能导致缓存主机资源不足甚至宕机的情况,影响正常的服务;

                b、如何解决:①缓存集群扩容(增加分片副本,均衡读流量);②对热key进行hash散列;③使用二级缓存;

        6、缓存容量和内存考虑

                a、合理评估利用容量;b、不同业务场景使用合适的数据结构;

Q:对于一个应用监控的维度有哪些?

A:

        主机:CPU、内存、GC次数、load(负载)

        接口:QPS、服务端耗时、客户端耗时、错误率

                应用提供的服务:QPS、平均RT、错误率

                应用依赖的服务:QPS、平均RT、错误率

        缓存:读QPS、写QPS、命中率、耗时,错误率

        数据库:读QPS、写QPS、耗时、错误率

        线程:各线程池CPU耗时、总线程数

                time_waiting、waiting、runnable

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值