(1)2019蚂蚁金服Java面试题
面试基础
谈谈一致hash算法?
按照hash算法来将对应的key哈希到一个具有232次方个桶的空间中,即0~(232)-1的数字空间。将这些数字头尾相连,想象成一个闭合的环形。如果集群中加入新的机器,采用它的IP或者唯一别名计算哈希值,也映射到环中。新增key值以顺时针的方向,存储到离自己最近的机器中。
参考:https://blog.csdn.net/cb_lcl/article/details/81448570说说乐观锁和悲观锁?
乐观锁:假设每次操作别人不会修改数据,当更新的时候根据版本号判断数据是否变化,适合读多写少的场景。
悲观锁:假设每次操作别人会修改数据,操作前先加上锁,后面的操作被阻塞,直至操作完毕释放锁。说说对MySQL的了解,和Oracle的区别?
MySQL是一种开源关系型数据库,支持标准的SQL语言,支持多种数据引擎、事务、主从配置。广泛用于中小型互联网公司。与Oracle的区别:- 定位:Oracle是大型数据库,Mysql是中小型数据库。Oracle是付费的商业数据库,MySQL是免费开源数据库。
- 性能:Oracle有更强的并发性能,更丰富的数据管理工具。
说说事务四大特性?
- A(原子性):事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。
- C(一致性):事务前后数据的完整性必须保持一致,符合逻辑运算。
- I(隔离性):多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离。
- D(持久性):事务一旦被提交,它对数据的改变是永久性的,即使数据库发生故障也不应该对其有影响。
说说事务的两阶段提交机制?
分布式事务有两个角色:事务协调者和事务参与者,两阶段提交协议是协调所有事务参与者决定提交或取消(回滚)的方式。- prepare阶段:协调者询问各个参与者是否正常执行,通知事务参与者准备提交或取消事务,然后进入表决过程。在表决过程中,参与者将告知协调者自己的决策:同意(事务参与者本地作业执行成功)或取消(本地作业执行故障)。
- commit阶段:协调者利用准备阶段的结果进行决策,当且仅当所有的参与者同意提交事务协调者才通知所有的参与者提交事务,否则协调者将通知所有的参与者取消事务
说说数据库的索引原理,聚簇索引和非聚簇索引?
索引本质上是一种数据结构(存储结构+算法),目的是为了加快数据检索的速度。
https://www.jianshu.com/p/e1dce41a6b2b举例说一下索引的使用场景?
- 主键索引:给表设置主键,这个表就拥有主键索引。
- 普通索引:增加某个字段的索引,比如用户表根据用户名查询。
- 复合索引:多个字段增加索引,遵循最左原则,比如创建索引(col1 + col2 + col3),相当于创建了(col1)、(col1,col2)、(col,col2,col3)三个索引。
说说数据库当前读和快照读?
- 快照读:简单的
select
是快照读。 - 当前读:
select for update,insert,update,delete
是当前读。 - 原 理:在repeat read级别下,快照读是通过MVVC(多版本控制)和undo log来实现的,当前读是通过加record lock(记录锁)和gap lock(间隙锁)来实现的。
- 快照读:简单的
HashTable & HashMap & ConcurrentHashMap区别及优缺点?
相比HashMap,HashTable和ConcurrentHashMap都是线程安全的。HashTable在put和get操作上加上synchronize实现同步控制。而ConcurrentHashMap通过分段锁,避免锁住全哈希表,提高了同步效率。
参考:https://www.cnblogs.com/sunshinekevin/p/10625516.htmlSynchronize是可重入锁吗,实现原理?
可重入锁,
参考:https://blog.csdn.net/zc19921215/article/details/84780335说说JVM类加载过程?
加载(Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化(Initialization)、使用(Using)和卸载(Unloading)7个阶段。
装载:查找和导入Class文件。
链接:把类的二进制数据合并到JRE中。
校验:检查载入Class文件数据的正确性。
准备:给类的静态变量分配存储空间。
解析:将符号引用转成直接引用。
初始化:对类的静态变量,静态代码块执行初始化操作。JVM双亲委派机制及使用原因?
- 机制:将当前类的加载任务转给它的父类加载器,如果它的父类还有父类,就继续向上传递,如果父类加载器加载失败则自己加载。
- 意义:避免重复加载以及核心类的修改风险。
说说Java的GC机制,有哪几种GC算法?
C/C++申请内存时都要malloc进行系统调用,使用完毕后通过free释放内存。Java虚拟机是先一次性分配一块较大的空间,然后在该空间上自动进行分配和释放,一般情况下程序员不需要手动操作。GC机制就是解决自动回收,面临三个问题:怎么确定垃圾,怎么回收垃圾,什么时候回收。怎么确定垃圾:
- 引用计数算法:给对象添加一个引用计数器,当有一个地方引用它时,计数器值就加1;当引用失效时,计数器值就减1;任何时刻计数器为0的对象就是死亡的。引用计数算法很简单,判断效率也很高,但是JVM没有选用它来管理内存。原因是什么呢?主要是因为它很难解决循环引用的问题。
- 可达性分析算法:通过一系列名为GC Roots的对象作为起始点,从这些节点开始向下搜索,搜索所有走过的路径称为“引用链”,当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的。
怎么回收垃圾:
- 分代收集法:它根据对象的生存周期,将堆分为新生代和老年代。在新生代中,由于对象生存期短,每次回收都会有大量对象死去,那么这时就采用复制算法。老年代里的对象存活率较高,没有额外的空间进行分配担保,所以可以使用标记整理或者标记清除。
- 标记清除法:将需要回收的对象标记出来,批量清除。缺点是效率低,产生碎片。
- 标记整理法:将需要回收的对象标记出来,移动到一端再清除,避免了碎片的产生。
- 复制法:将新生代内存划分为8:1:1三部分,较大的叫Eden区,其余是两块较小的Survior区。每次都会优先使用Eden区,若Eden区满,就将对象复制到第二块内存区上,然后清除Eden区,如果此时存活的对象太多,以至于Survivor不够时,会将这些对象通过分配担保机制复制到老年代中。
什么时候回收:
GC有两种类型:Scavenge GC和Full GC。- Scavenge GC:一般情况下,当新对象生成,并且在Eden申请空间失败时,就会触发Scavenge GC,对Eden区域进行GC,清除非存活对象,并且把尚且存活的对象移动到Survivor区。然后整理Survivor的两个区。这种方式的GC是对年轻代的Eden区进行,不会影响到年老代。因为大部分对象都是从Eden区开始的,同时Eden区不会分配的很大,所以Eden区的GC会频繁进行。因而,一般在这里需要使用速度快、效率高的算法,使Eden去能尽快空闲出来。
- Full GC:对整个堆进行整理,包括Young、Tenured和Perm。Full GC因为需要对整个堆进行回收,所以比Scavenge GC要慢,因此应该尽可能减少Full GC的次数。在对JVM调优的过程中,很大一部分工作就是对于Full GC的调节。有如下原因可能导致Full GC:
- 年老代(Tenured)被写满
- 持久代(Perm)被写
- System.gc()被显示调用
- 上一次GC之后Heap的各域分配策略动态变化
参考:https://blog.csdn.net/u011983531/article/details/79479972
说说线程池的核心参数和基本原理、调优策略?
- 核心参数:
corePoolSize:线程池中核心线程数量。
maximumPoolSize:线程池同时允许存在的最大线程数量。
RejectedExecutionHandler:拒绝服务策略。
keepAliveTime:当线程池中工作线程数大于corePoolSize,并且线程空闲时间超过keepAliveTime,这些线程将被终止。 - 基本原理:
参考:https://www.jianshu.com/p/896b8e18501b - 调优策略
要想合理的配置线程池,就必须首先分析任务特性,从以下几个角度来进行分析:
任务的性质:CPU密集型任务,IO密集型任务和混合型任务。
任务的优先级:高,中和低。
任务的执行时间:长,中和短。
任务的依赖性:是否依赖其他系统资源,如数据库连接。
参考:https://blog.csdn.net/chenpeng19910926/article/details/78142187
- 核心参数:
Collections.sort底层排序方式?排序稳定性?具体场景的排序策略?
Arrays.sort和Collections.sort最终使用的都是TimSort的算法。
Timsort的核心过程:
1、数组个数小于32的情况使用二分插入排序。
2、数组大于32时,先算出一个合适的大小,在将输入按其升序和降序特点进行了分区。排序的输入的单位不是一个个单独的数字,而是一个个的块-分区。其中每一个分区叫一个run。针对这些 run 序列,每次拿一个run出来按规则进行合并。每次合并会将两个run合并成一个 run。合并的结果保存到栈中。合并直到消耗掉所有的run,这时将栈上剩余的 run合并到只剩一个 run 为止。这时这个仅剩的 run 便是排好序的结果。
3、Timsort是稳定的算法,当待排序的数组中已经有排序好的数,它的时间复杂度会小于nlogn。与其他合并排序一样,Timesort是稳定的排序算法,最坏时间复杂度是O(n log n)。在最坏情况下,Timsort算法需要的临时空间是n/2,在最好情况下,它只需要一个很小的临时存储空间频繁老年代回收怎么分析解决?
参考:
https://www.cnblogs.com/justdojava/p/11198106.html
http://www.codersui.com/198.html
年轻代中经历了N次垃圾回收后仍然存活的对象,会被放到年老代中,老年代占满后触发FULL GC,一般是有对象无法回收会导致老年代频繁回收,排查步骤如下:- ps命令拿到pid:
ps -ef|grep your_application
- jstat命令查询gc情况:
jstat -gcutil 172046 1000
- 使用jmap分析对象占用内存情况,并导出到jmap.txt中:
jmap -histo 172046 > jmap.txt
- ps命令拿到pid:
说说Spring IOC、AOP的原理?
- IOC是控制反转的意思,用于降低代码之间的耦合度。工厂模式的升华,可以把IOC容器看作是一个工厂,这个工厂里要生产的对象都在配置文件中给出定义,然后利用编程语言的的反射编程,根据配置文件中给出的类名生成相应的对象。 (对象A获得依赖对象B的过程,由主动行为变为了被动行为,控制权颠倒过来了,这就是“控制反转”这个名称的由来).
- AOP意为面向切面编程,基于代理思想。对原来目标对象创建代理对象,在不修改原对象代码情况下,通过代理对象增加增强功能的代码,从而对原有业务方法进行增强。实现方式有JDK动态代理和动态字节码Cglib技术。
阻塞队列不用java提供的自己怎么实现,condition和wait不能用?
https://blog.csdn.net/h525483481/article/details/80347485拥塞窗口为什么要用慢启动?
https://www.cnblogs.com/losbyday/p/5847041.html说说负载均衡有几种算法以及优缺点?
- 轮询
将所有请求,依次分发到每台服务器上,适合服务器硬件相同的场景。
优点:服务器请求数目相同
缺点:服务器压力不一样,不适合服务器配置不同的情况 - 随机
请求随机分配到各台服务器上。
优点:使用简单
缺点:不适合机器配置不同的场景 - 最少链接
将请求分配到连接数最少的服务器上(目前处理请求最少的服务器)。
优点:根据服务器当前的请求处理情况动态分配
缺点:算法实现相对复杂,需要监控服务器请求连接数 - Hash(源地址散列)
根据IP地址进行Hash计算,得到IP地址。
优点:将来自同一IP地址的请求,同一会话期内,转发到相同的服务器,实现会话粘滞
缺点:目标服务器宕机后,会话会丢失 - 加权
在轮询、随机、最少链接、Hash等算法的基础上,通过加权的方式,进行负载服务器分配。
优点:根据权重,调节转发服务器的请求数目
缺点:使用相对复杂
- 轮询
Redis的数据一致性问题(分布式多节点环境 & 单机环境)
https://blog.csdn.net/qq_27384769/article/details/79499373简述docker容器?
Docker是一个容器化平台,它以容器的形式将您的应用程序及其所有依赖项打包在一起,以确保您的应用程序在任何环境中无缝运行。设计模式的看法和认知,有哪些设计模式?
设计模式就是一套被反复使用,多数人知晓的代码设计经验的总结。常用的设计模式如下:
工厂模式:Spring通过工厂模式创建bean
代理模式:Spring AOP通过动态代理增强原对象的功能
责任链模式:SpringMVC的过滤器
单例模式:spring构建的对象默认是单例如何实现分布式缓存?
分布式缓存系统要满足动态扩展和高可用性,通常采用Redis实现。Sentinel模式实现主从,
Cluster模式实现集群。通过key做一致性哈希,实现key对应redis结点的分布。多线程如何避免死锁?
- 加锁顺序(线程按照一定的顺序加锁)
- 加锁时限(线程尝试获取锁的时候加上一定的时限,超过时限则放弃对该锁的请求,并释放自己占有的锁)
- 死锁检测(jstack -l jvm_pid)
简述对JVM中的Runtime?
Runtime类代表Java程序的运行时环境,每个Java程序都有一个Runtime实例,该类会被自动创建,可以通过Runtime.getRuntime()方法来获取当前程序的Runtime实例。DNS解析过程?
- 查询本地hosts文件
- 查询本次DNS缓存
- 查询本地DNS服务器
- 本地DNS服务器转发请求到根DNS服务器
参考:https://www.jianshu.com/p/401f34691dcc
简述Htttp协议,Http和Https的区别,Https的加密方式?
HTTP是一个基于TCP/IP通信协议来传递数据(HTML文件、图片等),属于客户端-服务端工作模式,浏览器作为HTTP客户端通过URL向WEB服务器发送请求。https是http和TCP之间加入的SSL层,作用是防止钓鱼和数据加密。HTTPS采用非对称加密算法协商对称加密算法,处理步骤如下:- 服务器将携带的公钥向数字证书机构申请证书。
- 数字证书机构用自己的私钥对公钥签名颁发证书,并返回给服务器。
- 服务器将申请携带公钥的证书分发给客服端。
- 客户端验证证书,证书机构通过验证,或者用户接受不受信任的证书(非权威机构颁发的证书)。获取到公钥。到这一步,在证书保证下服务器拥有私钥,客户端拥有公钥,可进行非对称性加密。
- 使用公钥加密报文发送给服务器,其中携带随机串。其中的随机串用户传输数据时进行对称加密。
- 服务器使用私钥解密。获取报文信息及随机串。
- 解密后服务器发送握手消息给客户端。
- 客户端接受握手消息,握手结束,双方确定加密算法(使用随机串确定的对称性加密),开始传输。
- 数据传输过程使用对称性加密算法。
Http请求过程?
- 对网址进行DNS域名解析,得到对应的IP地址。
- 根据IP找到对应的服务器,发起TCP的三次握手。
- 建立TCP连接后发起HTTP请求。
- 服务器响应HTTP请求,浏览器得到html代码。
- 浏览器解析html代码,请求html中包含其他资源地址(如js、css图片等)。
- 浏览器对页面进行渲染呈现给用户。
说说TCP协议的三次握手四次挥手?
三次握手:
- 第一次握手:Client将标志位SYN置为1,随机产生一个值seq=J,并将该数据包发送给Server,Client进入SYN_SENT状态,等待Server确认。
- 第二次握手:Server收到数据包后由标志位SYN=1知道Client请求建立连接,Server将标志位SYN和ACK都置为1,ack (number )=J+1,随机产生一个值seq=K,并将该数据包发送给Client以确认连接请求,Server进入SYN_RCVD状态。
- 第三次握手:Client收到确认后,检查ack是否为J+1,ACK是否为1,如果正确则将标志位ACK置为1,ack=K+1,并将该数据包发送给Server,Server检查ack是否为K+1,ACK是否为1,如果正确则连接建立成功,Client和Server进入ESTABLISHED状态,完成三次握手,随后Client与Server之间可以开始传输数据了。
四次挥手:
- 第一次挥手:Client发送一个FIN,用来关闭Client到Server的数据传送,Client进入FIN_WAIT_1状态。
- 第二次挥手:Server收到FIN后,发送一个ACK给Client,确认序号为收到序号+1(与SYN相同,一个FIN占用一个序号),Server进入CLOSE_WAIT状态。
- 第三次挥手:Server发送一个FIN,用来关闭Server到Client的数据传送,Server进入LAST_ACK状态。
- 第四次挥手:Client收到FIN后,Client进入TIME_WAIT状态,接着发送一个ACK给Server,确认序号为收到序号+1,Server进入CLOSED状态,完成四次挥手。
项目经验
对你来说影响最大的一个项目?
说说自己参与的项目技术难度在哪里?哪一部分最难攻克?如何攻克?
为什么会想做这个项目?这个项目的idea是谁提出来的?
说说对项目中用到中间件的理解(Dubbo、MQ、Redis、kafka、zookeeper)?
服务器雪崩是怎么造成的?之前有这样的经历吗?怎么防备?
说说高并发架构的设计思路?
- CDN缓存静态资源
- 服务拆分和负载均衡
- 分布式缓存
- 消息队列解耦流量削峰
- 数据库集群和库表散列。
项目中如何实现的大数据的传输和存储?
如何实现何高并发下的削峰和限流?
限流算法:漏桶算法和令牌桶算法(基于Redis的分布式令牌桶实现)简述软件模块化的好处?
- 有利于代码复用
- 有利于分工协作
- 有利于模块化测试
- 有利于模块化部署与监控、排错
生活经历
- 生活中遇到的最大的挫折,怎么解决的?
- 生活中遇到的最有成就感的事情?
- 你的职业规划是什么?