1.ArrayList和LinkedList区别:
1.底层数据结构:ArrayList 是动态数组的数据结构实现,LinkedList 是双向链表的数据结构实现;
2.操作数据效率:
查询:ArrayList按照下标查询的时间复杂度O(1)【内存是连续的,根据寻址公式】, LinkedList不支持下标查询
新增:ArrayList尾部插入和删除,时间复杂度是O(1);其他部分增删需要挪动数组,时间复杂度是O(n);LinkedList头尾节点增删时间复杂度是O(1),其他都需要遍历链表,时间复杂度是O(n);
3.内存空间占用:ArrayList底层是数组,内存连续,节省内存;LinkedList 是双向链表需要存储数据,和两个指针,更占用内存
4.线程安全:lArrayList和LinkedList都不是线程安全的
ArrayList 和 LinkedList 的区别是什么?①底层数据结构②效率③空间④线程是否安全
2.HashMap的put方法
hash(key)判断有没有存在,不存在直接添加。存在就链表或者红黑树追加到节点后。
1. 判断键值对数组table是否为空或为null,否则执行resize()进行扩容(初始化16位)
2. 根据键值key计算hash值得到数组索引
3. 判断table[i]==null,条件成立,直接新建节点添加
4. 如果table[i]==null ,不成立
4.1 判断table[i]的首个元素是否和key一样,如果相同直接覆盖value
4.2 判断table[i] 是否为treeNode,即table[i] 是否是红黑树,如果是红黑树,则直接在树中插入键值对
4.3 遍历table[i],链表的尾部插入数据,然后判断链表长度是否大于8,大于8的话把链表转换为红黑树,在红黑树中执行插入操 作,遍历过程中若发现key已经存在直接覆盖value
5. 插入成功后,判断实际存在的键值对数量size是否超多了最大容量threshold(数组长度*0.75),如果超过,进行扩容。
3.说一下ThreadLocal
1. ThreadLocal 可以实现【资源对象】的线程隔离,让每个线程各用各的【资源对象】,避免争用引发的线程安全问题
2. ThreadLocal 同时实现了线程内的资源共享
3. 每个线程内有一个 ThreadLocalMap 类型的成员变量,用来存储资源对象
a)调用 set 方法,就是以 ThreadLocal 自己作为 key,资源对象作为 value,放入当前线
程的 ThreadLocalMap 集合中
b)调用 get 方法,就是以 ThreadLocal 自己作为 key,到当前线程中查找关联的资源值
c)调用 remove 方法,就是以 ThreadLocal 自己作为 key,移除当前线程关联的资源值
4. ThreadLocal内存泄漏问题
ThreadLocalMap 中的 key 是弱引用,值为强引用; key 会被GC 释放内存,关联 value 的内存并不会释放。建议主动 remove 释放 key,value
4.说一下JVM中,哪些是共享区,哪些可以作为GC ROOT?
堆区和方法区是所有线程共享。栈、本地方法栈、程序计数器是每个线程独有的。
什么是gc root,JVM在进行垃圾回收时,需要找到"垃圾"对象,也就是没有被引用的对象,但是直接找"垃圾"对象时比较耗时的,所以反过来,先找"非垃圾"对象,也就是正常对象,name就需要从某些"根"开始去找,根据这些"根"的引用路径找到正常对象,而这些"根"有一个特征,就是它只会引用其他对象,而不会被其他对象引用,例如:栈中的本地变量、方法区的静态变量、本地方法栈中的变量、正在运行的线程等可以作为gc root。
5.你们项目如何排查JVM问题
1、通过jmap或设置jvm参数获取堆内存快照dump
2、通过工具, VisualVM去分析dump文件,VisualVM可以加载离线的dump文件
3、通过查看堆信息的情况,可以大概定位内存溢出是哪行代码出了问题
4、找到对应的代码,通过阅读上下文的情况,进行修复即可
6.如何查看线程死锁
可以通过jstack命令来进行查看,jstack pid 命令会显示发生了死锁的线程
或者两个线程去操作数据库时发生了死锁,这时可以查询数据的死锁情况
1、查询是否锁表
show OPEN TABLES where IN_use >0;
2、查询进程
show processlist;
3、查看正在锁的事务
SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCKS;
4、查看等待锁的事务
SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCK_WAITS
7.线程之间如何进行通讯的
- 线程之间可以通过共享内存或基于网络来进行通信
- 如果是通过共享内存来进行通信,则需要考虑并发问题,什么时候阻塞,什么时候唤醒
- 像Java中的wait()、notify()就是阻塞和唤醒
- 通过网络就比较简单了,通过网络连接将通信数据发送给对方,当然也要考虑到并发问题,处理方式就是加锁等方式
8.介绍一下Spring,读过源码介绍一下大致流程
Spring是一个快速开发框架,Spring可以帮助程序员来管理对象
在创建Spring容器,也就是启动Spring时:
a. 首先会进行扫描,扫描得到所有的BeanDefinition对象,并存入一个Map中
b. 然后筛选出非懒加载的单例BeanDefinition进行创建Bean,对于多例Bean不需要在启动过程中去进行创建,对于多例Bean会在每次获取Bean时利用BeanDefinition去创建
c.利用BeanDefinition创建Bean就是Bean的创建生命周期,这期间包括了合并BeanDefinition、推断构造方法、实例化、属性填充、初始化前、初始化、初始化后等步骤,其中AOP就是发生在初始化后这一步骤中
单例Bean创建完了之后,Spring会发布一个容器启动时间
Spring启动结束
在源码中会更复杂,比如源码中会提供一些模板方法,让子类来实现,比如源码中还涉及到一些BeanFactoryPostProcessor和BeanPostProcessor的注册,Spring的扫描就是通过BeanFactoryPostProcessor来实现的,依赖注入就是通过BeanPostProcessor来实现的
在Spring启动过程中还会去处理@Import等注解
9.说一下Spring的事务机制
声明式事务@Transaction;
编程式事务(代码侵入)
1.Spring事务的隔离级别对应的就是数据库的隔离级别
2.Spring事务的传播机制是Spring事务自己实现的,也是Spring事务中最复杂的
3.回滚属性:rollbackFor
事务失效场景:
①异常捕获处理,自己处理了异常,没有抛出,解决:手动抛出
②抛出检查异常,配置rollbackFor属性为Exception
③非public方法导致的事务失效,改为public
③方法类调用,没有基于AOP调用。
10.什么时候@Transactional失效
因为Spring事务是基于代理来实现的,所以某个加了@Transactional的方法只有是被代理对象调用时,那么这个注解才会生效,所以如果是被代理对象来调用这个方法,那么@Transactional是不会生效的。
同时如果某个方法是private的,那么@Transactional也会失效,因为底层cglib是基于父子类来实现的,子类是不能重载父类的private方法的,所以无法很好的利用代理,也会导致@Transactional失效。
11.还读过哪些框架源码介绍一下你还熟悉的
可以说Spring,Mybatis,SpringBoot,SpringCloud,消息队列等开发框架或者中间件,如果这些都不会的话.可以说说HashMap、线程池等JDK自带的源码
12.JDK1.7到JDK1.8 HashMap 发生了什么变化?
1.7中底层是数组+链表,1.8中底层是数组+链表+红黑树,加红黑树的目的是提高HashMap插入和查询整体的效率
1.7中链表插入使用的是头插法,1.8中链表插入使用的是尾插法,因为1.8中插入key和value时需要判断链表元素个数,所以需要遍历俩表元素个数,所以正好就直接使用了尾插法,同时也可以解决之前头插法存在的环形链表问题
1.7中哈希算法比较复杂,存在各种右移与异或运算,1.8中进行了简化,因为负载的哈希算法的目的就是提高三烈性,来提供HashMap的整体效率,而1.8中新增了红黑树,所以可以适当的简化哈希算法,节省CPU资源
13.JDK1.7到JDK1.8 java虚拟机发生了什么变化?
1.7中存在永久代,1.8中没有了永久代,替换它的是元空间,元空间锁占的内存不是在虚拟机内部,而是本地内存空间,这么做的原因是,不管是永久代还是元空间,他们都是方法区的具体时间,之所以元空间所占的内存改为本地内存,官方的说法是为了和JRockit统一,不过额外还有一些原因,比如方法区锁储存的类信息通畅是比较难确定的,所以对于方法区的大小是比较难指定的,太小了容易出现方法区的溢出,太大了又会占用了太多虚拟机的内存空间,而转移到本地内存后则不会影响到虚拟机所占用的内存了。
14.如何实现AOP,项目中哪些地方用到了AOP
利用动态搭理技术来实现AOP,比如JDK动态搭理,或Cglib动态搭理,利用动态搭理技术,可以针对某个类生成代理对象,当调用代理对象的某个方法时,可以任意控制该方法的执行,比如可以先打印执行时间,在执行方法,并且该方法执行完成后,再次打印执行时间。
项目中,比如事务,权限控制,方法执行时长日志都是通过AOP技术来实现的,凡是需要对某些方法做统一处理的都可以用AOP来实现,利用AOP可以做到业务无侵入。
15.Spring中后置处理器的作用是什么?
Spring中的后置处理器分为BeanFactory后置处理器和Bean后置处理器,他们是Spring底层源码架构设计中非常重要的一种机制,同时开发者也可以利用这两种后置处理器来进行扩展。BeanFactory后置处理器表示针对BeanFactory的处理器,Spring启动过程中,会先创建出BeanFactory实例,然后利用BeanFactory处理器来加工BeanFactory,比如Spring的扫描就是基于BeanFactory后置处理来实现的,而Bean后置处理器也类似,Spring在创建一个Bean的过程中,首先会实例化一个对象,然后再利用Bean后置处理器来对该对象进行加工,比如我们常说的依赖注入就是基于一个Bean后置处理器来实现的,通过该Bean后置处理器来给实例对象中加了@Autowired注解的属性自动赋值,还比如我们常说的AOP,也是利用一个Bean后置处理器来实现的,基于原实例对象,判断是否需要进行AOp,如果需要,name就基于原实例对象进行动态代理,生成一个代理对象。