# 面试总结 **Repository Path**: happyGG10/interview_summary ## Basic Information - **Project Name**: 面试总结 - **Description**: 面试经验 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 3 - **Forks**: 0 - **Created**: 2018-09-10 - **Last Updated**: 2022-08-12 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README 回答问题思路 如果一个问题你只是正面的回答完面试官的问题,那这个问题只能得到60分的评价,如果想要更大进入的机会,我们应该从问题的解决方案,问题的原理,问题的自身理解。三个方面来回答。这样才能得到更高的评价。 一.数据库 1.数据库事务的理解,底层实现 ACID:原子性、一致性、隔离性、持久性 原子性(Atomicity):事务作为一个整体被执行,包含在其中的对数据库的操作要么全部被执行,要么都不执行。 一致性(Consistency):事务应确保数据库的状态从一个一致状态转变为另一个一致状态。一致状态的含义是数据库中的数据应满足完整性约束。 隔离性(Isolation):多个事务并发执行时,一个事务的执行不应影响其他事务的执行。 持久性(Durability):一个事务一旦提交,他对数据库的修改应该永久保存在数据库中。 2.数据库索引都有哪些 BTree和Hash索引都在什么情况下使用 3.给定表A,分别有三列a1,a2,a3。分别在a1,a2,a3建立三个单独的索引。此时会有几个索引树。 3个 4.数据库的索引里面都包含哪些内容 5.数据库的笛卡尔积使用的算法原理是什么。 6.给定表A,分别有三列a1,a2,a3。分别在a1,a2,a3建立三个单独的索引,和(a1,a2,a3)建立一个聚合索引,有什么差别。分别生成几个索引树。 a1,a2,a3产生3个索引树,(a1,a2,a3)产生a1、(a1、a2)、(a1,a2,a3)三个索引树 给定表A,分别有三列a1,a2,a3。分别在a1,a2,a3建立三个单独的索引,执行Sql:select * from A where a1 = ? and a2 = ? and a3 = ?。哪些索引会生效 看a1、a2、a3列对应的列决定,如果a1是主键,通过a1过滤后得到的数据量少,则将数据放入内存,在内存中通过a2、a3来过滤数据,在内存中过滤就不用索引了(SQL是根据表中数据来进行查询优化的) 7.如何进行分库,分表。方案都有哪些。比如都用户的操作进行记录进行分表,如何分表让每张表的记录分配均匀。 8.mysql分页有什么优化 9.悲观锁、乐观锁 悲观锁:之所以叫做悲观锁,是因为这是一种对数据的修改抱有悲观态度的并发控制方式。我们一般认为数据被并发修改的概率比较大,所以需要在修改之前先加锁。 悲观锁主要分为共享锁或排他锁 共享锁【Shared lock】又称为读锁,简称S锁。顾名思义,共享锁就是多个事务对于同一数据可以共享一把锁,都能访问到数据,但是只能读不能修改。 排他锁【Exclusive lock】又称为写锁,简称X锁。顾名思义,排他锁就是不能与其他锁并存,如果一个事务获取了一个数据行的排他锁,其他事务就不能再获取该行的其他锁,包括共享锁和排他锁,但是获取排他锁的事务是可以对数据行读取和修改。 悲观锁,通过for update,锁定整行,如果这一行没有索引,会锁定整张表。mysql自动提交不会锁,需要关闭自动提交。 乐观锁:相对于悲观锁,在对数据库进行处理的时候,乐观锁并不会使用数据库提供的锁机制。一般的实现乐观锁的方式就是记录数据版本。 CAS是项乐观锁技术,当多个线程尝试使用CAS同时更新同一个变量时,只有其中一个线程能更新变量的值,而其它线程都失败,失败的线程并不会被挂起,而是被告知这次竞争中失败,并可以再次尝试。 选择:在乐观锁与悲观锁的选择上面,主要看下两者的区别以及适用场景就可以了。 乐观锁并未真正加锁,效率高。一旦锁的粒度掌握不好,更新失败的概率就会比较高,容易发生业务失败。 悲观锁依赖数据库锁,效率低。更新失败的概率比较低。 10.组合索引,最左原则 11.mysql 的表锁、行锁 12.大数据量分库、分表;如何分库、分表及应该注意的问题有哪些?分库分表带来的问题有哪些?分库分表后怎么实现分页查询(大数据量会有分页查询吗?)? 13.select from where group by having join in 执行的顺序 14.数据库查询命中索引后,怎么进行寻址查询这条记录的。 Q:那你能说说什么是索引吗? A:(这道题肯定难不住我啊)索引其实是一种数据结构,能够帮助我们快速的检索数据库中的数据 Q:那么索引具体采用的哪种数据结构呢? A:(这道题我也背过)常见的MySQL主要有两种结构:Hash索引和B+ Tree索引,我们使用的是InnoDB引擎,默认的是B+树 这里我耍了一个小心机,特意说了一下索引和存储引擎有关。希望面试官可以问我一些关于存储引擎的问题。然而面试官并没有被我带跑... Q:既然你提到InnoDB使用的B+ 树的索引模型,那么你知道为什么采用B+ 树吗?这和Hash索引比较起来有什么优缺点吗? A:(突然觉得这道题有点难,但是我还是凭借着自己的知识储备简单的回答上一些)因为Hash索引底层是哈希表,哈希表是一种以key-value存储数据的结构,所以多个数据在存储关系上是完全没有任何顺序关系的,所以,对于区间查询是无法直接通过索引查询的,就需要全表扫描。所以,哈希索引只适用于等值查询的场景。而B+ 树是一种多路平衡查询树,所以他的节点是天然有序的(左子节点小于父节点、父节点小于右子节点),所以对于范围查询的时候不需要做全表扫描 Q:除了上面这个范围查询的,你还能说出其他的一些区别吗? A:(这个题我回答的不好,事后百度了一下) B+ Tree索引和Hash索引区别? 哈希索引适合等值查询,但是无法进行范围查询 哈希索引没办法利用索引完成排序 哈希索引不支持多列联合索引的最左匹配规则 如果有大量重复键值的情况下,哈希索引的效率会很低,因为存在哈希碰撞问题 2 聚簇索引、覆盖索引 Q:刚刚我们聊到B+ Tree ,那你知道B+ Tree的叶子节点都可以存哪些东西吗? A:InnoDB的B+ Tree可能存储的是整行数据,也有可能是主键的值 Q:那这两者有什么区别吗? A:(当他问我叶子节点的时候,其实我就猜到他可能要问我聚簇索引和非聚簇索引了)在 InnoDB 里,索引B+ Tree的叶子节点存储了整行数据的是主键索引,也被称之为聚簇索引。而索引B+ Tree的叶子节点存储了主键的值的是非主键索引,也被称之为非聚簇索引 Q:那么,聚簇索引和非聚簇索引,在查询数据的时候有区别吗? A:聚簇索引查询会更快? Q:为什么呢? A:因为主键索引树的叶子节点直接就是我们要查询的整行数据了。而非主键索引的叶子节点是主键的值,查到主键的值以后,还需要再通过主键的值再进行一次查询 Q:刚刚你提到主键索引查询只会查一次,而非主键索引需要回表查询多次。(后来我才知道,原来这个过程叫做回表)是所有情况都是这样的吗?非主键索引一定会查询多次吗? A:(额、这个问题我回答的不好,后来我自己查资料才知道,通过覆盖索引也可以只查询一次) 覆盖索引? 覆盖索引(covering index)指一个查询语句的执行只用从索引中就能够取得,不必从数据表中读取。也可以称之为实现了索引覆盖。 当一条查询语句符合覆盖索引条件时,MySQL只需要通过索引就可以返回查询所需要的数据,这样避免了查到索引后再返回表操作,减少I/O提高效率。 如,表covering_index_sample中有一个普通索引 idx_key1_key2(key1,key2)。 当我们通过SQL语句:select key2 from covering_index_sample where key1 = 'keytest';的时候,就可以通过覆盖索引查询,无需回表。 3 联合索引、最左前缀匹配 Q:不知道的话没关系,想问一下,你们在创建索引的时候都会考虑哪些因素呢? A:我们一般对于查询概率比较高,经常作为where条件的字段设置索引 Q: 那你们有用过联合索引吗? A:用过呀,我们有对一些表中创建过联合索引 Q:那你们在创建联合索引的时候,需要做联合索引多个字段之间顺序你们是如何选择的呢? A:我们把识别度最高的字段放到最前面 Q:为什么这么做呢? A:(这个问题有点把我问蒙了,稍微有些慌乱)这样的话可能命中率会高一点吧。。。 Q: 那你知道最左前缀匹配吗? A:(我突然想起来原来面试官是想问这个,怪自己刚刚为什么就没想到这个呢。)哦哦哦。您刚刚问的是这个意思啊,在创建多列索引时,我们根据业务需求,where子句中使用最频繁的一列放在最左边,因为MySQL索引查询会遵循最左前缀匹配的原则,即最左优先,在检索数据时从联合索引的最左边开始匹配。所以当我们创建一个联合索引的时候,如(key1,key2,key3),相当于创建了(key1)、(key1,key2)和(key1,key2,key3)三个索引,这就是最左匹配原则 虽然我一开始有点懵,没有联想到最左前缀匹配,但是面试官还是引导了我。很友善。 4 索引下推、查询优化 Q:你们线上用的MySQL是哪个版本啊呢? A:我们MySQL是5.7 Q:那你知道在MySQL 5.6中,对索引做了哪些优化吗? A:不好意思,这个我没有去了解过。(事后我查了一下,有一个比较重要的 :Index Condition Pushdown Optimization) Index Condition Pushdown(索引下推) MySQL 5.6引入了索引下推优化,默认开启,使用SET optimizer_switch = 'index_condition_pushdown=off';可以将其关闭。官方文档中给的例子和解释如下: people表中(zipcode,lastname,firstname)构成一个索引 SELECT * FROM people WHERE zipcode='95054' AND lastname LIKE '%etrunia%' AND address LIKE '%Main Street%'; 如果没有使用索引下推技术,则MySQL会通过zipcode='95054'从存储引擎中查询对应的数据,返回到MySQL服务端,然后MySQL服务端基于lastname LIKE '%etrunia%'和address LIKE '%Main Street%'来判断数据是否符合条件。 如果使用了索引下推技术,则MYSQL首先会返回符合zipcode='95054'的索引,然后根据lastname LIKE '%etrunia%'和address LIKE '%Main Street%'来判断索引是否符合条件。如果符合条件,则根据该索引来定位对应的数据,如果不符合,则直接reject掉。有了索引下推优化,可以在有like条件查询的情况下,减少回表次数。 Q:你们创建的那么多索引,到底有没有生效,或者说你们的SQL语句有没有使用索引查询你们有统计过吗? A:这个还没有统计过,除非遇到慢SQL的时候我们才会去排查 Q:那排查的时候,有什么手段可以知道有没有走索引查询呢? A:可以通过explain查看sql语句的执行计划,通过执行计划来分析索引使用情况 Q:那什么情况下会发生明明创建了索引,但是执行的时候并没有通过索引呢? A:(大概记得和优化器有关,但是这个问题并没有回答好) 查询优化器? 一条SQL语句的查询,可以有不同的执行方案,至于最终选择哪种方案,需要通过优化器进行选择,选择执行成本最低的方案。 在一条单表查询语句真正执行之前,MySQL的查询优化器会找出执行该语句所有可能使用的方案,对比之后找出成本最低的方案。 这个成本最低的方案就是所谓的执行计划。优化过程大致如下: 1、根据搜索条件,找出所有可能使用的索引 2、计算全表扫描的代价 3、计算使用不同索引执行查询的代价 4、对比各种执行方案的代价,找出成本最低的那一个 二.JAVA集合类 1.java集合的继承关系 ![输入图片说明](https://images.gitee.com/uploads/images/2019/0826/172823_8ceeb7d9_2145264.png "屏幕截图.png") 2.List和Set区别 ArrayList:有序集合,底层数组实现,默认大小10,扩容1.5倍; HashMap(JDK1.8):K、V集合,底层数组+链表实现+红黑树,默认16,加载因子0.75,扩容阀值12;hash计算:的值高位16位于低16异或运算,同位置计算:(h=key.hashCode())^h & (length - 1);resize后,原数据的位置下标要么保持不变,要么是原来位置下标+原table大小;hash值为负数则该位置为红黑树结构,为正数则为链表结构 HashSet:无序集合,底层为HashMap的key TreeSet:有序集合,底层为红黑树(带有红黑标记的二叉树) 3.HashSet 是如何保证不重复的 public HashSet() { map = new HashMap<>(); } public boolean add(E e) { return map.put(e, PRESENT)==null; } 4.ArrayList和LinkedList区别 1.ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。 (LinkedList是双向链表,有next也有previous) 2.对于随机访问get和set,ArrayList觉得优于LinkedList,因为LinkedList要移动指针。 3.对于新增和删除操作add和remove,LinedList比较占优势,因为ArrayList要移动数据。 5.ArrayList初始化元素是多少以及扩容机制 默认初始是10,最大2的31次方,扩容是原来数组的1.5倍,array.copy的方式 6.自己手动写一个ArrayList 7.ArrayList的subList方法的使用场景,会有什么问题 subList 返回的是 ArrayList 的内部类 SubList, 并不是 ArrayList 而是 ArrayList 的一个视图, 对于 SubList 子列表的所有操作最终会反映到原列表上。 8.如何实现一个线程安全的ArrayList 1.使用synchronized关键字; Collections.synchronizedList() 2.List> data=Collections.synchronizedList(new ArrayList>()); 9.CopyOnWriteArrayList是否有了解过,原理是什么。多用于什么场景。  CopyOnWrite容器即写时复制的容器。通俗的理解是当我们往一个容器添加元素的时候,不直接往当前容器添加,而是先将当前容器进行Copy,复制出一个新的容器,然后新的容器里添加元素,添加完元素之后,再将原容器的引用指向新的容器。这样做的好处是我们可以对CopyOnWrite容器进行并发的读,而不需要加锁,因为当前容器不会添加任何元素。所以CopyOnWrite容器也是一种读写分离的思想,读和写不同的容器。 private transient volatile Object[] array;底层被volatile 修饰 10.LinkedList内部使用的双向链表还是单项链表 11.双向链表和单项链表区别 12.描述一下HashMap放入一个元素的put方法的过程 HashMap在put方法中,它使用hashCode()和equals()方法。当我们通过传递key-value对调用put方法的时候,HashMap使用Key hashCode()和哈希算法来找出存储key-value对的索引。如果索引处为空,则直接插入到对应的数组中,否则,判断是否是红黑树,若是,则红黑树插入,否则遍历链表,若长度不小于8,则将链表转为红黑树,转成功之后 再插入。 13.HashMap在放入链表一个元素的时候是放在链表的头部还是尾部 链表尾部 14.HashMap初始元素和扩容机制是什么 10 ,容量的1.5倍 15.LinkedHashMap的应用 LinkedHashMap是Map接口的哈希表和链接列表实现,具有可预知的迭代顺序。LinkedHashMap实现与HashMap的不同之处在于,LinkedHashMap维护着一个运行于所有条目的双重链接列表。此链接列表定义了迭代顺序,该迭代顺序可以是插入顺序(insert-order)或者是访问顺序,其中默认的迭代访问顺序就是插入顺序,即可以按插入的顺序遍历元素,这点和HashMap有很大的不同。 16.HashMap(jdk1.8)为什么将链表大于等于8个是转变成红黑树,红黑树的复杂度是多少?如果链表的节点数量超过8个转成了红黑树、又减少小于8个后,会重新转成链表吗?如果不转为什么不一开始就用红黑树?如果转回链表岂不是消耗了时间?一个HashMap在该链表上平凡的put、remove操作,是该链表数量保持在8左右,岂不是会平凡的进行链表与红黑树的转换?思考HashMap作者是怎么考虑的! 17.HashMap在多线程下容易引发什么问题,如果去解决 线程不安全 18.JDK1.8的HashMap如何解决并发下发生死循环问题的。 19.ConcurrentHashMap里面的modCout字段有什么用 20.ConcurrentHashMap的原理是什么吗?ConcurrentHashMap在1.7和1.8有哪些区别。为什么要改成1.8这样。 21.ConcurrentHashMap里面的size方法是否加锁,如果加锁是乐观锁还是悲观锁。 三.JAVA线程&锁 1.synchronized 的实现原理以及锁优化 2.如何保证多线程下 i++ 结果正确? for (int i = 0; i < times; i++) { // 进行自加的操作 synchronized (SynchronizedTest.class) { count++; } } 10.Condition接口及其实现原理 Condition定义了等待/通知两种类型的方法,当前线程调用这些方法时,需要提前获取到Condition对象关联的锁。Condition对象是由Lock对象(调用Lock对象的newCondition()方法)创建出来的,换句话说,Condition是依赖Lock对象的。 11.Fork/Join框架的理解 16.说一下java的内存模型 Java内存模型(Java Memory Model ,JMM)就是一种符合内存模型规范的,屏蔽了各种硬件和操作系统的访问差异的,保证了Java程序在各种平台下对内存的访问都能保证效果一致的机制及规范。 Java内存模型规定所有的变量都是存在主存当中(类似于前面说的物理内存),每个线程都有自己的工作内存(类似于前面的高速缓存)。线程对变量的所有操作都必须在工作内存中进行,而不能直接对主存进行操作。并且每个线程不能访问其他线程的工作内存。 18.是否了解伪共享,伪共享发生的原因是什么,如何去解决伪共享问题。 19.说一下happens-before规则的理解 20.Synchronized 和 ReentrantLock Lock:底层是CAS乐观锁,依赖AbstractQueuedSynchronizer(AQS)类,把所有的请求线程构成一个CLH队列(FIFO的双向双端队列),而对该队列的操作均通过Lock-Free(CAS)操作。 Synchronized是关键字,内置语言实现,Lock是接口; Synchronized在线程发生异常时会自动释放锁,因此不会发生异常死锁。Lock异常时不会自动释放锁,所以需要在finally中实现释放锁; Lock是可以中断锁,Synchronized是非中断锁,必须等待线程执行完成释放锁; Lock可以使用读锁提高多线程读效率。 21.ReentrantLock的原理是什么,公平锁和非公平锁的区别 ReentrantLock主要利用CAS+AQS队列来实现。它支持公平锁和非公平锁,两者的实现类似。 CAS:Compare and Swap,比较并交换。CAS有3个操作数:内存值V、预期值A、要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。该操作是一个原子操作,被广泛的应用在Java的底层实现中。在Java中,CAS主要是由sun.misc.Unsafe这个类通过JNI调用CPU底层指令实现 ReentrantLock主要利用CAS+AQS队列来实现。它支持公平锁和非公平锁,两者的实现类似。 CAS:Compare and Swap,比较并交换。CAS有3个操作数:内存值V、预期值A、要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。该操作是一个原子操作,被广泛的应用在Java的底层实现中。在Java中,CAS主要是由sun.misc.Unsafe这个类通过JNI调用CPU底层指令实现 AbstractQueuedSynchronizer简称AQS 22.说一下对AQS的理解 AQS原理 AQS:AbstractQuenedSynchronizer抽象的队列式同步器。是除了java自带的synchronized关键字之外的锁机制。 AQS的全称为(AbstractQueuedSynchronizer),这个类在java.util.concurrent.locks包 AQS的核心思想是,如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并将共享资源设置为锁定状态,如果被请求的共享资源被占用,那么就需要一套线程阻塞等待以及被唤醒时锁分配的机制,这个机制AQS是用CLH队列锁实现的,即将暂时获取不到锁的线程加入到队列中。 CLH(Craig,Landin,and Hagersten)队列是一个虚拟的双向队列,虚拟的双向队列即不存在队列实例,仅存在节点之间的关联关系。 AQS是将每一条请求共享资源的线程封装成一个CLH锁队列的一个结点(Node),来实现锁的分配。 用大白话来说,AQS就是基于CLH队列,用volatile修饰共享变量state,线程通过CAS去改变状态符,成功则获取锁成功,失败则进入等待队列,等待被唤醒。 **注意:AQS是自旋锁:**在等待唤醒的时候,经常会使用自旋(while(!cas()))的方式,不停地尝试获取锁,直到被其他线程获取成功 实现了AQS的锁有:自旋锁、互斥锁、读锁写锁、条件产量、信号量、栅栏都是AQS的衍生物 23.说一下countdownlatch 和 cyclicbarrier原理和区别 24.Semaphore的原理和使用场景 25.同步方法和同步代码块的区别是什么 同步方法默认用this或者当前类class对象作为锁; 同步代码块可以选择以什么来加锁,比同步方法要更细颗粒度,我们可以选择只同步会发生同步问题的部分代码而不是整个方法; 同步方法使用关键字 synchronized修饰方法,而同步代码块主要是修饰需要进行同步的代码,用 synchronized(object){代码内容}进行修饰; 26.同步方法锁的对象是什么,静态方法和非静态方法锁的对象有区别吗? 1.非静态同步锁对象是this 2.静态同步锁对象是字节码 27.说一下CAS算法的原理,CAS 有什么缺陷,如何解决? CAS虽然很高效的解决原子操作,但是CAS仍然存在三大问题。ABA问题,循环时间长开销大和只能保证一个共享变量的原子操作。 解决方案CAS类似于乐观锁,即每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据。因此解决方案也可以跟乐观锁一样: 使用版本号机制,如手动增加版本号字段 Java 1.5开始,JDK的Atomic包里提供了一个类AtomicStampedReference来解决ABA问题。这个类的compareAndSet方法的作用是首先检查当前引用是否等于预期引用,并且检查当前的标志是否等于预期标志,如果全部相等,则以原子方式将该应用和该标志的值设置为给定的更新值。 28.unsafe类的compareAndSwap方法的参数有哪些 五.JVM 1.说一下JVM的内存结构,每个区域都存放什么内容 程序计数器、虚拟机栈、本地方法栈、堆和方法区 a. Java堆    Java堆是java虚拟机所管理的内存中最大的一块,是被所有线程都共享的内存区域。存在的唯一目的就是存放对象实例,几乎所有的对象实例都 在这里进行分配内存。不过目前随着技术的不断发展,也并不是所有的对象实例都在堆中分配内存,可能也存在栈上分配。由于所占空间大,又存放 各种实例对象,因此java虚拟机的垃圾回收机制主要管理的就是此区域,详细的垃圾回收方法以后会提到。JVM规范中规定堆可以处于物理上不连续 的内存空间中,只要逻辑上是连续的即可。并且可以通过-Xmx和-Xms来扩展堆的内存大小,如果在堆中没有足够的内存为实例分配,并且堆也无法在 扩展时,就会报OutOfMemoryError异常。 b 方法区 跟Java堆一样,方法区是各个线程共享的内存区域,此区域是用来存储类的信息(类的名称、字段信息、方法信息)、静态变量、常量以及编译器 编译后的代码。JVM规范中并不区分方法区和堆,只把方法区描述为堆的逻辑部分,但是它却有一个别名叫做非堆(Non-Heap),目的就是与Java堆区 分开。根据垃圾回收机制中分代回收的思想,如果在HotSpot虚拟机上开发,可以把方法区称为“永久代”(只是可以这么理解,但实质是不一样的), 垃圾回收机制在Java堆中划分一个部分称为永久代,用此区域来实现方法区,这样HotSpot的垃圾收集器就可以像管理Java堆一样管理这部分内存,而 不必为方法区开发专门的内存管理器。 运行时常量池 运行时常量池是方法区的一个部分,class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是**常量池,用于存放编译期间 生成的各种字面量和符号引用,**这部分内容会在类加载后进入方法区的运行时常量池中。Java 虚拟机对 Class 文件的每一部分(自然也包 括常量池)的格式都有严格的规定,每一个字节用于存储哪种数据都必须符合规范上的要求,这样才会被虚拟机认可、装载和执行。 c. 程序计数器 虽然在上图中程序计数器的面积很大,但实际上它是一块较小的内存空间,可以看做当前线程所执行字节码的行号指示器。字节码解释器在工作中 时下一步该干啥、到哪了,就是通过它来确定的。大家都知道在多线程的情况下,CPU在执行线程时是通过轮流切换线程实现的,也就是说一个CPU处 理器(假设是单核)都只会执行一条线程中的指令,因此为了线程切换后能恢复到正确的执行位置,每个线程都要有一个独立的程序计数器,各条线 程之间的计数器互不影响,独立存储,我们称这类内存区域为“线程私有”的内存。很明显,程序计数器就是线程私有的。如果线程正在执行的是一个 java方法,**程序计数器记录的是正在执行的虚拟机字节码指令地址;**如果执行的Native方法,程序计数器记录的值为空(Undefined),此内存 区域是java中唯一一个在java虚拟机规范中没有规定任何OutOfMemoryError情况的区域。 d. Java虚拟机栈 我们经常会把java内存粗糙的分为两个部分,堆和栈,Java虚拟机栈就是栈这一部分,或者说是虚拟机栈中局部变量表部分。跟程序计数器一样, 虚拟机栈也是线程私有的,它的生命周期跟线程相同。每个方法在执行的同时都会创建一个栈帧(Stack Frame),每个栈帧对应一个被调用的方法,栈 帧中用于存储局部变量表、操作数栈、动态链表、方法出口等信息。每一个方法从开始执行到结束就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。 局部变量表:顾名思义,他就是用来存储方法中的局部变量(包括在方法中生命的非静态变量以及函数形参),对于基本数据类型,直接存值,对于引用 类型的变量,存储指向该对象的引用。由于它只存放基本数据类型的变量、引用类型的地址和返回值的地址,这些类型所需空间大小已知且固定,所以当 进入一个方法时,这个方法需要在栈帧中分配多大的局部变量空间是完全可以确定的,在方法运行期间也不会改变局部变量表的大小。 指向运行常量池的引用:在方法执行过程中难免会使用到类中定义的常量,因此栈帧中要存放一个指向运行时常量池的引用。 方法返回地址:当一个方法执行结束后,要返回到之前调用它的地方,因此在栈帧中需要保存一个方法返回地址。 e. 本地方法栈 本地方法栈与虚拟机栈的功能非常的相似,区别不过是虚拟机栈为虚拟机执行java方法服务,而本地方法栈为虚拟机执行Native方法服务。有的虚 拟机并不会区分本地方法栈和虚拟机栈,比如Sun HotSpot虚拟机直接将两个合二为一。 2.讲讲什么情况下回出现内存溢出,内存泄漏? JVM 年轻代到年老代的晋升过程的判断条件是什么呢? OOM错误,stackoverflow错误,permgen space错误 垃圾回收的算法有哪些? 数组在内存中如何分配 强引用 、软引用、 弱引用、虚引用 垃圾回收器都有哪些,怎么去选择用哪种垃圾回收器? 如何进行线上内存回收问题的排查 如何查找CPU标高问题的原因 自己写一个java.lang.String能否被使用。为什么? 说一下双亲委派机制。以及类加载器都有哪些 如何自己实现一个类加载器。 自己实现的类加载器能否破坏双亲委派模型 破坏双亲委派模型的方法有哪些 OSGI都有哪些应用,和双亲委派模型和区别在哪里,有哪些优点。 什么时候触发垃圾回收 说一下类加载的过程 加载 --> 验证 --> 准备 --> 解析 --> 初始化 [--> 使用 --> 卸载] 说一下对象创建的过程 那些对象可以作为GCRoot对象 六.Spring **1.Spring事务传播属性** 1) REQUIRED(默认属性) 如果存在一个事务,则支持当前事务。如果没有事务则开启一个新的事务。 被设置成这个级别时,会为每一个被调用的方法创建一个逻辑事务域。如果前面的方法已经创建了事务,那么后面的方法支持当前的事务,如果当前没有事务会重新建立事务。 2) MANDATORY 支持当前事务,如果当前没有事务,就抛出异常。 3) NEVER 以非事务方式执行,如果当前存在事务,则抛出异常。 4) NOT_SUPPORTED 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。 5) REQUIRES_NEW 新建事务,如果当前存在事务,把当前事务挂起。 6) SUPPORTS 支持当前事务,如果当前没有事务,就以非事务方式执行。 7) NESTED 支持当前事务,新增Savepoint点,与当前事务同步提交或回滚。 嵌套事务一个非常重要的概念就是内层事务依赖于外层事务。外层事务失败时,会回滚内层事务所做的动作。而内层事务操作失败并不会引起外层事务的回滚。 PROPAGATION_NESTED 与PROPAGATION_REQUIRES_NEW的区别: 它们非常类似,都像一个嵌套事务,如果不存在一个活动的事务,都会开启一个新的事务。 使用PROPAGATION_REQUIRES_NEW时,内层事务与外层事务就像两个独立的事务一样,一旦内层事务进行了提交后,外层事务不能对其进行回滚。两个事务互不影响。两个事务不是一个真正的嵌套事务。同时它需要JTA 事务管理器的支持。 使用PROPAGATION_NESTED时,外层事务的回滚可以引起内层事务的回滚。而内层事务的异常并不会导致外层事务的回滚,它是一个真正的嵌套事务。 ** 2.Spring代理实现** 静态代理主要通过将目标类与代理类实现同一个接口,让代理类持有真实类对象,然后在代理类方法中调用真实类方法,在调用真实类方法的前后添加我们所需要的功能扩展代码来达到增强的目的 JDK动态代理 JDK动态代理与静态代理一样,目标类需要实现一个代理接口,它的开发步骤如下: a.定义一个java.lang.reflect.InvocationHandler接口的实现类,重写invoke方法 b.将InvocationHandler对象作为参数传入java.lang.reflect.Proxy的newProxyInstance方法中 c.通过调用java.lang.reflect.Proxy的newProxyInstance方法获得动态代理对象 d.通过代理对象调用目标方法 CgLib动态代理 CgLib动态代理的原理是对指定的业务类生成一个子类,并覆盖其中的业务方法来实现代理。它的开发步骤: 1.定义一个org.springframework.cglib.proxy.MethodInterceptor接口的实现类,重写intercept方法 2.获取org.springframework.cglib.proxy.Enhancer类的对象 3.分别调用Enhancer对象的setSuperclass和setCallback方法,使用create方法获取代理对象 4.通过代理对象调用目标方法 在ApplicationMain启动类加入@EnableAspectJAutoProxy(proxyTargetClass=true,exposeProxy=true) 两种代理的区别 JDK动态代理和CgLib动态代理的主要区别: JDK动态代理只能针对实现了接口的类的接口方法进行代理 CgLib动态代理基于继承来实现代理,所以无法对final类、private方法和static方法实现代理 Spring AOP的代理 Spring AOP中的代理使用的默认策略是: 如果目标对象实现了接口,则默认采用JDK动态代理 如果目标对象没有实现接口,则采用CgLib进行动态代理 如果目标对象实现了接口,且强制CgLib代理,则采用CgLib进行动态代理 **3.Spring如何实现事务的** 1.aspectJ AOP实现事务: 2.事务代理工厂Bean实现事务: <!–写的是事务–> <!–要进行事务的类–> <!–key写的是service层要增强的方法–> <!– 事务的隔离级别,后面逗号后面是异常类,用于回滚数据–> ISOLATION_DEFAULT,PROPAGATION_REQUIRED,-MyExepction 3.注解方式实现事务: 在需要进行事务的方法上增加一个注解“@Transactional(rollbackFor = MyExepction.class )” ** 4.BeanFactory 和 FactoryBean?** BeanFactory定义了 IOC 容器的最基本形式,并提供了 IOC 容器应遵守的的最基本的接口,也就是 Spring IOC 所遵守的最底层和最基本的编程规范。在 Spring 代码中, BeanFactory 只是个接口,并不是 IOC 容器的具体实现,但是 Spring 容器给出了很多种实现,如 DefaultListableBeanFactory 、 XmlBeanFactory 、 ApplicationContext 等,都是附加了某种功能的实现。 BeanFactory是个 Factory ,也就是 IOC 容器或对象工厂, FactoryBean 是个 Bean 。在 Spring 中,所有的 Bean 都是由 BeanFactory( 也就是 IOC 容器 ) 来进行管理的。但对 FactoryBean 而言,这个 Bean 不是简单的 Bean ,而是一个能生产或者修饰对象生成的工厂 Bean, 它的实现与设计模式中的工厂模式和修饰器模式类似。 **5.Spring IOC 的理解,其初始化过程?** **6.BeanFactory 和 ApplicationContext?** BeanFactory: 是Spring里面最低层的接口,提供了最简单的容器的功能,只提供了实例化对象和拿对象的功能; ApplicationContext: 应用上下文,继承BeanFactory接口,它是Spring的一各更高级的容器,提供了更多的有用的功能; 1) 国际化(MessageSource) 2) 访问资源,如URL和文件(ResourceLoader) 3) 载入多个(有继承关系)上下文 ,使得每一个上下文都专注于一个特定的层次,比如应用的web层 4) 消息发送、响应机制(ApplicationEventPublisher) 5) AOP(拦截器) 两者装载bean的区别 BeanFactory: BeanFactory在启动的时候不会去实例化Bean,中有从容器中拿Bean的时候才会去实例化; ApplicationContext: ApplicationContext在启动的时候就把所有的Bean全部实例化了。它还可以为Bean配置lazy-init=true来让Bean延迟实例化; **7.Spring Bean 的生命周期,如何被管理的?** ** 8.如果要你实现Spring AOP,请问怎么实现?** ** 9.如果要你实现Spring IOC,你会注意哪些问题?** **10.Spring 中用到了那些设计模式?** ** 工厂模式、单例模式、代理模式、适配器模式、模板方法模式 ** 11.Spring MVC 的工作原理?** ** 12.Spring 循环注入的原理?** ** 13.Spring 如何保证 Controller 并发的安全** **14.构造注入不能循环注入; setter注入,单例模式可以循环注入; setter注入,原型模式不能循环注入;** ** 15.保证单例bean无可变更成员变量; 使用prototype原型模式; 使用ThreadLocal;** **16.如何在Spring的生命周期中做些事情即:怎么在Spring加载前、中、后做一些事情 ** Dubbo & Zookeeper Dubbo的服务请求失败怎么处理 Zookeeper的用途是什么 Zookeeper watch机制 Zookeeper节点宕机如何处理 Zookeeper的选举算法是什么 描述一下Dubbo一次调用的流程 Dubbo使用的长连接还是短连接 使用协议不同连接不通,dubbo协议使用单一、长连接、tcp通讯协议、NIO异步通讯;hessian(http)、rmi(tcp)、http(http)都是多连接、短连接、同步 说一下对Dubbo的SOA的理解 Zookeeper如何实现分布式锁 Dubbo与Zookeeper服务注册与发现的原理 参考:http://dubbo.apache.org/#!/docs/user/references/registry/zookeeper.md?lang=zh-cn Zookeeper如何实现集群之间通信的 RabbitMQ 用过哪些MQ,怎么用的,和其他mq比较有什么优缺点,MQ的连接是线程安全的吗 消息中间件如何解决消息丢失问题 MQ系统的数据如何保证不丢失、怎么持久化 中间件&框架 接口的幂等性的概念。如何保证接口幂等 对分布式事务的理解 如何实现负载均衡,有哪些算法可以实现 数据的垂直拆分水平拆分 分布式集群下如何做到唯一序列号 列举出你能想到的数据库分库分表策略;分库分表后,如何解决全表查询的问题 全局ID Netty的原理 Netty的线程模型 Netty如何解决selector方法导致CPU100%问题的。 Netty如何处理tcp黏包问题的。 CAP理论说一下 开放问题 设计秒杀活动