一面(技术面)
一、Java内存模型:虚拟机栈-独享的(一个线程一个栈)、堆-共享的(新生代-复制算法、老年代-标记整理-清除)、程序计数器-独享的(CPU切换线程管理)、元数据区-共享的(类信息、常量、静态变量、方法)、本地方法栈-独享(和操作系统交互)
二、full gc怎么触发:新生代满minor gc;1、老生代满2、永久代满 3、minor gc 不了的晋升到老生代的数据大于老生代大小;4、选择了CMS GC算法导致;
三、gc算法:1、可达性2、引用计数法3、复制(内存分2份,左满移右) 4、标记整理-清除(所有对象标记生死状态,整理是死的归类到一起清除,清除是直接清除死标记)
四、高吞吐量的话用哪种gc算法:复制和标记整理-清除
五、ConcurrentHashMap和HashMap:node数组+链表>8升级红黑树;Synch锁二叉树首节点(hash不重复)+cas比较替换+分段锁来保证并发安全;
六、JDK8的stream的操作:reduce,max,min运算,flatMap合并流,filter过滤,map转大写,collect的json转数组;
七、volatile原理volatile的底层如何实现,怎么就能保住可见性了?1、对volatile修饰的变量,执行写操作的话,JVM会发送一条lock前缀指令给CPU,CPU在计算完成后会立即将这个值写回主内存,同时因为有MESI缓存一致性协议,所以各个CPU都会对总线进行嗅探,自己本地缓存中的数据是否被别人修改。
如果发现别人修改了某个缓存的数据,那么CPU会将自己本地缓存的数据过期掉,然后这个CPU上执行的线程在读取那个变量的时候,就会从主内存中重新加载最新的数据。2、禁止指令重排
八、线程池原理,拒绝策略,核心线程数:线程放到阻塞队列,处理中阻塞处理完唤醒;
九、1亿个手机号码,判断重复:1、hash(num)%1000 2、100个文件 3、每个文件读到hashmap.containsKey() 是否存在4、mysql.flink里 ,分组by phone ,distinct
十、单元测试介绍一下,多模块依赖怎么单元测试。Mockito
十一、线程之间的交互方式有哪些?保持线程顺序:
- Lock.wait() +lock. notify() synchronized(lock) run
- a.await() + b.signal() ( ReentrantLock condition a,b 条件锁)
- join方式 new TaskB(taskA.join);
二面(技术面)
一、限流算法:1、计数法 2、令牌桶 3、漏桶法
二、zk挂了怎么办:日志和数据满了;高可用是zab广播选主节点;分成三种角色:leader、follower和observer。leader负责写、follower和observer负责读。redis主从复制模型中,master挂掉之后是靠sentinel自动完成故障转移的
三、秒杀场景设计,应付突然的爆发流量:redis布鲁姆过滤器
四、redis的热点key问题:1、nginx和redie请求key统计
五、redis的更新策略(先操作数据库还是先操作缓存):LRU最少使用淘汰、LFU不常用淘汰、FIFO、超时、主动
六、分布式数据一致性:1、acid分布式事务(原子性、一致性、隔离性和持久性) 2、CAP Base(一致性、可用性、分区容忍性;权衡,允许有损失的场景);3、2PC\3PC 4、Raft投票(超过半数选举master)5、最终一致性:异步记录(msgTable,msgId)+重试+补偿
七、一致性哈希:hashcode%2^32
八、消息队列原理介绍:kafka
九、full gc问题,怎么排查:1、堆溢出 2、栈溢出报错3、jmap dump,jvisualvm等分析工具
十、jvm的回收策略:1、引用计数法2、标记-复制(俩个大小一样内存;活对象复制另一个,清空老空间)-整理(移动活对象整理到一边,清理另一边)3、新对象存在新生代,无法回收存在老年代,静态文件类方法文件存在持久代;
十一、ClassLoader原理和应用:.java->javac->.class->ClassLoader->jvm->机器指令->操作系统
双亲委派,直接递归找到父父父的顶层类加载器,然后顶层向下尝试加载。优点避免类的重复加载;
十二、注解的原理:一个标记1 、extends Annotation 继承了接口,IOC反射appcontext.getAnnotation 获取他;
2、底层是AnnotationInvocationHandler 对注解处理;
3、应用程序入口触发、拦截器 handler等里面getAnnotation()应用它;
十三、数据库原理,数据库中间件,索引优化:
十四、aop原理和应用:定义切面@Aspect class HandlerAspect,
它是动态代理+反射实现的;日志拦截一些actionHandler
十五、Docker的原理:1、通过命名空间完成宿主机进程、网络、文件系统的隔离,一个命名空间对应一个容器;2、使用了镜像存储方式;3、通过CGroups完成了对CPU和内存的隔离;
三面(谈项目,问技术)
一、讨论了一下数据库表设计,领域建模
二、数据库有哪些索引?1、主键索引2、单列索引3、复合索引4、唯一索引5、覆盖索引6、全文索引7、B+树索引8、哈希索引(=IN><)9、R-TREE索引,GIS数据类型10、空间数据类型索引
三、Redis的缓存淘汰策略有哪些?1、LFU最少使用淘汰2、LRU过期淘汰3、手动4、FIFO先进先出5、超时
四、分布式锁的实现方式,zk实现和redis实现哪个比较好:redis+ReektrantLock
五、Ioc(控制反转)容器原理详细讲讲,源码看过么:Spring IOC容器主要有继承体系底层的BeanFactory、高层的ApplicationContext和WebApplicationContext,利用JAVA反射功能实例化Bean,生命周期管理
- Bean有自己的生命周期
容器启动原理:Spring应用的IOC容器通过tomcat的Servlet或Listener监听启动加载;Spring MVC的容器由DispatchServlet作为入口加载;Spring容器是Spring MVC容器的父容器
- 容器加载Bean原理:
1、BeanDefinitionReader读取Resource所指向的配置文件资源,然后解析配置文件。配置文件中每一个<bean>解析成一个BeanDefinition对象,并保存到BeanDefinitionRegistry中;
2、容器扫描BeanDefinitionRegistry中的BeanDefinition;调用InstantiationStrategy进行Bean实例化的工作;使用BeanWrapper完成Bean属性的设置工作;
3、单例Bean缓存池:Spring 在 DefaultSingletonBeanRegistry 类中提供了一个用于缓存单实例 Bean 的缓存器,它是一个用 HashMap 实现的缓存器,单实例的 Bean 以 beanName 为键保存在这个HashMap 中。
六、ID(依赖注入):一个接口多个实现类,切换使用。
七、把乐观锁加在数据库上面,怎么实现:update user set v++,sex=1 where id=3 and v=7
四面(谈技术…)
一、分布式事务:TXID,建立一个事务表,生产者-消费者模式,消费这个表;
二、Java三大特性:封装、多态、继承
三、RPC原理:包括:client,server,注册中心。client本地调用->封装消息体->client注册发现->解码->server本地调用->server注册发现->client解码
四、netty原理:1、NIO框架异步非阻塞,多线程+IO多路复用(单线程同时处理多个请求,线程池);2、零拷贝TransforTo;3、内存池(轻量级GC)4、多个无锁的串行线程5、序列化框架protobuf 6、reactor分派器
五、降级策略和降级框架:1、限流降级 2、超时降级 3、故障降级 4、次数失败降级
降级处理hystrix了解过么
六、两次点击,怎么防止重复下订单:1、JS 2、redis 判断原子递增 3、服务器生成唯一的一次性token记在session当访问时清除;
七、静态代理和动态代理的区别:新建一个代理类用于原有方法功能的扩展和增强;1、静态代理事先编程好的一个代理类;2、动态代理是创建继承InvocationHandler接口的类,编写方法的逻辑, 然后指定要代理的类;最后
Proxy.newProxyInstance实例化对象,最后执行代理类方法;
八、JUC说说你知道的东西:java.util .concurrent 并发包,有CAS和AQS的实现和应用。包含:安全队列、阻塞队列、安全并发concurrentHashMap,ReektrantLock等;
九、B+树的叶子节点:B树是数据存放一个胖树。B+是数据存在叶子节点,其他节点存放索引值;
十、JAVA对象组成:对象头[mark word、类指针、数组长度],实例数据,对齐字节;
mark word包含:无锁、偏向锁(AB俩线程CAS抢锁,成功改锁标志位,失败升级)、轻量级锁(自旋)、重量级锁(自旋失败锁标志位10,所有抢锁失败线程被阻塞)、GC标记;
64位系统下:对象头16字节大小,对象24字节大小;
十一、进程可以调度的,上下文是一个队列来存储进程信息,包含PID,FD,等;CPU去队列里调度;
十二、线程池:
corePoolSize 核心线程数=CPU*2 ,worker数组,存放各个线程序列;比如3个并发的数组,数组元素是线程;(为什么不是1万线程设置10000的原因)
maxiPollSize 最大线程数,提交的线程数;阻塞队列长度;线程放到阻塞队列中等待;线程池有可变长度,固定长度之分;
十三、FLINK如何海量去重:1、布鲁姆过滤器的判断重复 2、RocksDB状态state的判断重复
算法题:
一、哈希冲突的解决方法:
- 拉链法:相同的值写入链表,链表线性结构可以优化为跳表或红黑树;
- 另建一个哈希表存放冲突的记录;
- 对冲突的哈希值再次进行哈希,直到不冲突;
- +1平方->+2平方->+3平方,直到不冲突,+随机数,+1
二、数组的区分:
有序数组存储数据,对数据的索引效率都很高,但是插入和删除就会有性能瓶颈(回忆ArrayList),
链表存储数据,要一次比较元素来检索出数据,所以索引效率低,但是插入和删除效率高(回忆LinkedList)
三、二分查找(分治法):必须是有序数组,定义下标:低位0、高位n、中位=(低位+高位)/2,查找值x>中间位则低位=mid+1,查找值x<中间位则高位=mid-1;
四、找出和为target的俩个数:定义一个HashMap,定义一个2元素结果数组,遍历数组每个元素插入该hashMap,存在target-x,把target-x和x的下标放入2元数组里;
五、找数组中出现次数超过一半的数字:
快排思想,中位开始左右查找,+1次数;
六、TOPK算法:
- 快速排序,后取K个元素
- 冒泡排序,每次冒泡找出最大值,K个泡之后得到TOPK
- k个元素生成小顶二叉堆(二叉树最小在顶左右节点比父节点大,大在下),循环k个元素和堆顶值对比,元素大于堆顶则替换堆顶并重新调整小顶堆;最终堆里K元素是最大的;
七、二叉树前序遍历:构建1个node left/right的二叉树;
需要一个数组来存放结果数据,需要一个栈来执行入栈出栈,通过栈先进后出中转到结果数组里;前后是反转左右入栈顺序即可;层序遍历,用队列中转每层元素到结果数组里;
八、斐波拉契数组0、1、1、2、3、5、8(目标位=前俩位的和):递归公式F(N-1)+F(N-2)
九、阶乘递归公式:F(N-1)*N
十、1+2+3+4+...100: 套公式N/2*(MIN+MAX) 50*101=5050