面试题day11
美团优选
-
ArrayList和LinkedList区别
底层数据结构不同,链表和数据
对随机读的支持不同
容量不同
使用场景不同,ArrayList多读少写,LinkedList少读多写
-
HashMap八股(数据结构,扩容,链表和红黑树转换)
死循环
链表散列
扩容
- 链表引发
- 链表长度大于8,判断是否转为红黑树
- 数组大小大于64,转红黑树
- 数组大小小于64,扩容
- 链表长度大于8,判断是否转为红黑树
- 数组引发
- 数组元素大于容量与扩容因子之积,扩容
- 链表引发
-
Collections.SynchronizedMap底层实现原理
在操作HashMap时自动添加了synchronized来实现线程同步
-
为什么重写了equals方法,就一定要重写hashCode方法
- 当我们将equals方法重写后有必要将hashCode方法也重写,这样做才能保证不违背hashCode方法中“相同对象必须有相同哈希值”的约定
- hashmap中是通过hashcode决定元素放入哪个索引(桶)中,然后通过equals判断和索引中中对应链表中的每个元素是否相同,如果有相同 ->不放,如果不相同 -> 放
- 只重写equals,放置元素时,会将相同key键值对放置到不同索引,导致覆盖失败
-
hashmap和hashtable区别
- 线程是否安全: HashMap 是非线程安全的,HashTable 是线程安全的,因为 HashTable 内部的方法基本都经过synchronized 修饰。
- 效率: 因为线程安全的问题,HashMap 要比 HashTable 效率高一点。另外,HashTable 基本被淘汰,不要在代码中使用它;
对 Null key 和 Null value 的支持: HashMap 可以存储 null 的 key 和 value,但 null 作为键只能有一个,null 作为值可以有多个;HashTable 不允许有 null 键和 null 值,否则会抛出 NullPointerException。 - 初始容量大小和每次扩充容量大小的不同 : ① 创建时如果不指定容量初始值,Hashtable 默认的初始大小为 11,之后每次扩充,容量变为原来的 2n+1。HashMap 默认的初始化大小为 16。之后每次扩充,容量变为原来的 2 倍。② 创建时如果给定了容量初始值,那么 Hashtable 会直接使用你给定的大小,而 HashMap 会将其扩充为 2 的幂次方大小(HashMap 中的tableSizeFor()方法保证,下面给出了源代码)。也就是说 HashMap 总是使用 2 的幂作为哈希表的大小,后面会介绍到为什么是 2 的幂次方。
- 底层数据结构: JDK1.8 以后的 HashMap 在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为 8)(将链表转换成红黑树前会判断,如果当前数组的长度小于 64,那么会选择先进行数组扩容,而不是转换为红黑树)时,将链表转化为红黑树,以减少搜索时间。Hashtable 没有这样的机制。
-
有哪些线程安全的集合类,讲一讲原理
HashTable 全局加synchronized
ConcurrentHashMap 1.7sgment 1.8cas+synchronized
CopyOnWriteArrayList 写时复制
-
JVM内存空间分布
堆 方法区
虚拟机栈 本地方法栈 程序计数器 直接内存
操作数栈 局部变量表 动态链接 方法返回地址
栈溢出有两种,一种是stackoverflow,另一种是outofmemory,前者一般是因为方法递归没终止条件,后者一般是方法中线程启动过多。
-
垃圾回收算法
标记-清除,标记-复制,标记-整理
-
哪些可以作为GCROOT
虚拟机栈、本地方法栈、常量、静态变量
-
常见的垃圾回收器
Serial、SerialOld、
ParallelScavenger、ParallelOld、
ParNew、CMS、
G1
-
讲一讲CMS的回收过程
初始标记:标记GC root直接关联对象和年轻代指向老年代的对象(需要停止用户线程Stop The World,但是速度很快)
并发标记:追随GC root下关联的对象,速度慢但是不用停止用户线程
并发预处理:因为并发标记没有停止用户线程,所以老年代的引用可能发生变化,标记为dirty,为下一步的重新标记做准备
重新标记:停止用户线程,再次扫描老年代和年轻代指向老年代的对象;
并发清除:GC回收完成,因为没有停止用户线程,可能会产生浮动垃圾,留着下次处理了。
-
三色标记法
三色标记法将对象分为三类:
- 白色,在刚开始遍历的时候,所有的对象都是白色的
- 灰色,被垃圾回收器扫描过,但是至少还有一个引用没有被扫描
- 黑色,被垃圾回收器扫描过,并且这个对象的引用也全部都被扫描过,是安全存活的对象
标记过程:
- 起初所有的对象都是白色的;
- 从根对象出发扫描所有可达对象,标记为灰色,放入待处理队列;
- 从待处理队列中取出灰色对象,将其引用的对象标记为灰色并放入待处理队列中,自身标记为黑色;
- 重复步骤3,直到待处理队列为空,此时白色对象即为不可达的“垃圾”,回收白色对象;
根对象在垃圾回收的术语中又叫做根集合,它是垃圾回收器在标记过程时最先检查的对象。
-
安全点和安全区
-
安全点
- 在 Java 虚拟机里,传统的垃圾回收算法为了枚举 GC Roots,必须要经历一个Stop-the-world(STW)的过程,即停止其他非垃圾回收线程的工作,直到完成垃圾回收。
- Java 虚拟机中的 STW 是通过安全点(safepoint)机制来实现的。
- 当 Java 虚拟机收到 STW 请求,它便会等待所有的线程都到达安全点,才允许请求 STW 的线程进行独占的工作。
- 程序执行时并非在所有地方都能停顿下来开始GC,只有在到达安全点时才能暂停。
- 当然,安全点的初始目的并不是让其他线程停下,而是找到一个稳定的执行状态。
- 在这个执行状态下,Java 虚拟机的堆栈不会发生变化。这么一来,垃圾收集器便能够“安全”地执行可达性分析。
-
安全区域
- 安全点机制保证了程序执行时,在不太长的时间内就会遇到可进入GC的安全点。
- 但是,当线程处于Sleep状态或者Blocked状态时,这时候线程无法响应JVM的中断请求,“走”到安全的地方去中断挂起,JVM也显然不太可能等待线程重新被分配CPU时间以响应请求。
- 对于这种情况,就需要安全区域(Safe Region)来解决。
- 安全区域是指在一段代码片段之中,引用关系不会发生变化。 在这个区域中的任意地方开始GC都是安全的、我们也可以把安全区域看做是被扩展了的安全点。
- 在线程执行到安全区域中的代码时,首先标识自己已经进人了安全区域,这样,当在这段时间里JVM要发起GC时,就不用管标识自己为安全区域状态的线程了。
- 在线程要离开安全区域时,它要检査系统是否已经完成了根节点枚举(或者是整个GC过程),如果完成了,那线程就继续执行,否则它就必须等待直到收到可以安全离开安全区域的信号为止。
-
-
主动中断和抢占中断
- 抢先式中断:( 目前没有虚拟机采用)
- 首先中断所有线程。如果还有线程不在安全点,就恢复线程,让线程跑到安全点。
- 主动式中断:
- 设置—个中断标志,各个线程运行到Safe Point的时候主动轮询这个标志, 如果中断标志为真,则将自己进行中断挂起。
- 抢先式中断:( 目前没有虚拟机采用)
-
volatile关键字的作用
引出Java内存模型,保证可见性,防止指令重排序
-
mesi协议
缓存一致性协议,对于同一地址的读内存操作是并发的,针对同一地址的写操作是独占的,对弈内存地址写操作同一时间只能由一个处理器来执行.
- 当CPU写数据时,如果写的变量是共享变量,即在其他CPU中也存在该变量的副本,会发出信号通知其他CPU将该变量的缓存行置为无效状态;
- 当CPU读取共享变量时,发现自己缓存的该变量的缓存行是无效的,那么它就会从内存中重新读取。
-
内存屏障
禁止指令重排
-
redis数据结构
5个基础数据结构和3个高级数据结构
-
redis过期键删除策略
定期删除、惰性删除、定时删除
-
mq的作用
异步、削峰、解耦
-
kafka副本了解吗,讲一下
-
为什么不让一个partition被同组的多个consumer消费
-
编程题:链表倒数第n个结点
-
反问:部门业务相关
-
JSF和Dubbo有什么区别
-
Dubbo有了解过吗?讲一下
Dubbo的10层架构,服务注册和服务发现的流程,consumer和provider之间通信
-
怎么学习Dubbo的?看过源码吗?
-
Dubbo用了哪些设计模式
-
mysql用过吗?讲一下b+树
-
redis和mysql一致性解决方案
-
编程题1:二叉树中路径和为目标值的所有路径
-
编程题2:全排列