1.Spring Bean的生命周期
https://www.jianshu.com/p/1dec08d290c1
2.Redis缓存热点问题
1.内存淘汰机制设置为allkeys-lfu或者volatile-lfu(Least Frequently Used)方式 最近最少使用 (LRU是最近最少未使用)
2.web服务本地把热点的数据进行缓存
3.热点商品上线前需要预热
4.更新商品信息机制,如何在商品信息更新后,及时更新缓存中的商品信息
3.Redis缓存一致性问题
更新操作时,1.先删除Redis中缓存,2.将执行数据库更新+更新缓存操作,根据key发送到内存队列中。
读取数据的时候,如果发现数据不在缓存中,那么将(读取mysql数据+更新缓存的操作(读请求)),根据key,也发送同一个AarrayBlockQueue中。
一个队列对应一个工作线程,每个工作线程串行拿到对应的操作,然后一条一条的执行。
一个队列中,其实多个更新缓存请求串在一起是没意义的,因此可以做过滤。
4.AQS公平锁,非公平锁的实现
公平锁:
state为0的时候,也就是无锁状态的时候,会先去判断当前线程的是否构建了节点且节点是否为head后的第一个节点,如果是,才会去尝试CAS获取锁,否则加入CLH双端队列。
非公平锁:
新入节点state为0的时候,会去尝试CAS获取锁,获取不到加入CLH。
CLH,自旋+阻塞+唤醒的机制 构建的一个双端队列。
5.消息队列如何保证顺序性能。
出现顺序错乱,多个消费者按顺序取出,但完成顺序不一致,插入数据库的顺序也不一致。
1.拆分成多个队列,一个消费者对应一个队列。
2.一个队列,一个消费者,消费者做内存队列排序。
6.锁升级过程
无锁》偏向锁
如果一个锁为可偏向状态, 则尝试用 CAS 操作, 将自己的线程 ID 写入MarkWord
if(成功)
如果 CAS 操作成功, 则认为已经获取到该对象的偏向锁, 执行同步块代码。
else(偏向锁》轻量级锁)
如果 CAS 操作失败, 则说明, 有另外一个线程 Thread B 抢先获取了偏向锁。 这种状态说明该对象的竞争比较激烈, 此时需要撤销 Thread B 获得的偏向锁,将 Thread B 持有的锁升级为轻量级锁。
如果一个锁为已偏向状态,检测 MarkWord 中存储的 thread ID 是否等于当前 thread ID 。
if(MarkWord 中存储的 thread ID 等于当前 thread ID)
如果相等,则证明本线程已经获取到偏向锁, 可以直接继续执行同步代码块
else(偏向锁》轻量级锁)
轻量级加锁过程:
首先根据标志位判断出对象状态处于不可偏向的无锁状态。
在当前线程的栈桢(Stack Frame)中创建用于存储锁记录(lock record)的空间,并将对象头中的Mark Word复制到锁记录中
然后线程尝试使用 CAS 操作将对象头中的 Mark Word 替换为指向锁记录的指针
if( CAS 操作将对象头中的 Mark Word 替换为指向锁记录的指针){
当前线程获得锁
}else{
该对象已经被加锁了, 先进行自旋操作, 再次尝试 CAS 争抢, 如果仍未争抢到(轻量级锁》重量级锁)。
}
7.synchronized 和ReentrantLock 锁的区别
1.Re获取锁可以带超时时间。
2.等待可中断,lock.lockInterruptibly(),允许在等待时由其它线程调用等待线程的Thread.interrupt方法来中断等待线程的等待而直接返回。
3.Re可以通过condition,用来实现分组唤醒需要唤醒的线程们,synchronized要么随机唤醒一个线程要么唤醒全部线程
4.Re实现了公平锁
1.syc操作对象头部分的Mark Word,lock调用Unsafe的park方法
8.Spring AOP打印日志
引入spring-boot-starter-aop包
1.@Aspect 定义切面类
2.定义切点
@Pointcut(“execution(public * com.stuPayment.controller….(…))”)
public void controllerLog(){}//签名,可以理解成这个切入点的一个名称
3.定义切面方法
@Before(“controllerLog() || uiControllerLog()”)
public void logBeforeController(JoinPoint joinPoint) {
logger.info("################THE ARGS OF THE CONTROLLER : " + Arrays.toString(joinPoint.getArgs()));
}
@Before @After @Around @AfterReturning @AfterThrowing
9.ThreadLocal内存泄露的原因?
ThreadLocalMap使用ThreadLocal的弱引用作为key,如果一个ThreadLocal不存在外部强引用时,
ThreadLocalMap持有ThreadLocal的弱引用,即使没有手动删除,ThreadLocal也会被回收这样就会导致ThreadLocalMap中key为null, 而value还存在着强引用,只有thead线程退出以后,value的强引用链条才会断掉。
如果当前线程再迟迟不结束的话,这些key为null的Entry的value就会一直存在一条强引用链:
Thread Ref -> Thread -> ThreaLocalMap -> Entry -> value
10.zookeeper和eureka?
zookeeper保证 cp,强一致性,leader挂掉期间不提供服务;
Eureka保证ap,每个实例都可对外提供服务,但每个实例上数据不一定完全一致。
11.redis和mq 消息订阅模式区别?
- redis没有相应的机制保证消息可靠消费,没有订阅者的话消息会消失。
- mq消息只能被消费一次,redis同时发送给多个订阅者
- redis只能备份整个Redis实例中的数据,mq可以选择性持久化,对特定的消息,队列持久化。
12.Spring如何解决的循环依赖?
调用构造函数初始化对象放入
singletonFactories:
需要依赖注入对象A在singletonObjects中不存在且正在创建isSingletonCurrentlyInCreation(beanName)(即发现了循环依赖)
放入
earlySingletonObjects:
在earlySingletonObjects获取需要注入对象,完成注入,放入singletonObjects。
singletonObjects:
13.什么情况会导致Full GC;
- 老年代空间不足(新生带转入老年代超过固定代数的对象,或创建大对象,大数组时)
- 在进行Minor GC之前会检查新生代所有存活对象的总和是否大于老年代最大的可用连续空间 若>
2.1 不允许空间担保 >> Full GC
2.2 允许空间分配担保 如果之前统计所得到的Minor GC晋升到老年代的平均大小 大于 老年的剩余空间 >> Full GC。
备注:Eden区创建一对象,发现空间不够用;Full GC之前调用,仅适用于Parallel Scavenge(-XX:+UseParallelGC)。 - CMS
Minor GC时,survivor 区放不下、对象只能放入老年代,而此时老年代也放不下造成的(CMS的promotion failed);
在执行CMS GC的过程中同时有对象要放入老年代,而此时老年代空间不足造成的(CMS的promotion concurrent mode failure);
4.perm(永久代)/metaspace(元空间) 空间不足时。
5.system.gc(), 此方法的调用是建议JVM进行Full GC。
14. 什么情况会导致年轻代对象迁移至老年代。
- 超过一定年龄(默认15岁)
- 动态年龄,survivor某一个年龄的对象 总的容量大于survivor区的一半,>=该年龄的对象都移动至老年代。
- 大对象,数组 直接进入老年代
15.wait sleep的区别
1.wait释放锁 进入等待池,需要被唤醒;sleep不释放锁,不需要被唤醒
2.sleep释放cpu
3.wait必须放在同步代码块sychronized里面
4.wait() 是Thread的方法,waite()是object的方法。
sleep、wait方法都让出CPU。
16.Redis集群添加新节点
17.Redis主从同步原理
18.mongo集群原理
19.jvm 为什么将永久代去除,变为元空间(类字节码,类信息,常量,静态变量)。
1、字符串存在永久代中,容易出现性能问题和内存溢出。
2、类及方法的信息等比较难确定其大小,因此对于永久代的大小指定比较困难,太小容易出现永久代溢出,太大则容易导致老年代溢出。
3、永久代会为 GC 带来不必要的复杂度,并且回收效率偏低。有了元数据区,GC和类信息删除就可以同步了。
4、Oracle 可能会将HotSpot 与 JRockit 合二为一。