Java 2022面试题目整理

1.List 和 Set 的区别?

List , Set 都是继承自 Collection 接口 List 特点:元素有放入顺序,元素可重复 ,
Set 特点:元素无放入顺序,元素不可重复,重复元素会覆盖掉,(元素虽然无放入顺序,但是元素在set中的位
置是有该元素的 HashCode 决定的,其位置其实是固定的,加入SetObject 必须定义 equals ()方法 ,另外list
支持for循环,也就是通过下标来遍历,也可以用迭代器,但是set只能用迭代,因为他无序,无法用下标来取得想
要的值。) SetList对比 Set:检索元素效率低下,删除和插入效率高,插入和删除不会引起元素位置改变。
List:和数组类似,List可以动态增长,查找元素效率高,插入删除元素效率低,因为会引起其他元素位置改变

2. HashSet 是如何保证不重复的?

HashSet 中 add ()元素时,判断元素是否存在的依据,不仅要比较hash值,同时还要结合 equles 方法比较。
HashSet 中的 add ()方法会使用 HashMap 的 add ()方法。以下是 HashSet 部分源码:

private static final Object PRESENT = new Object();
private transient HashMap<E,Object> map;
public HashSet() {
	map = new HashMap<>();
}
public boolean add(E e) {
	return map.put(e, PRESENT)==null;
}

HashMap 的 key 是唯一的,由上面的代码可以看出 HashSet 添加进去的值就是作为 HashMap 的key。所以不会
重复( HashMap 比较key是否相等是先比较 hashcode 在比较 equals )。

3. HashMap 的扩容过程?

当向容器添加元素的时候,会判断当前容器的元素个数,如果大于等于阈值(知道这个阈字怎么念吗?不念 fa 值,
念 yu 值四声)---即当前数组的长度乘以加载因子的值的时候,就要自动扩容啦。
扩容( resize )就是重新计算容量,向 HashMap 对象里不停的添加元素,而 HashMap 对象内部的数组无法装载更
多的元素时,对象就需要扩大数组的长度,以便能装入更多的元素。当然 Java 里的数组是无法自动扩容的,方法
是使用一个新的数组代替已有的容量小的数组,就像我们用一个小桶装水,如果想装更多的水,就得换大水桶。

HashMap hashMap=new HashMap(cap);
cap =3, hashMap 的容量为4;
cap =4, hashMap 的容量为4;
cap =5, hashMap 的容量为8;
cap =9, hashMap 的容量为16;
如果 cap 是2的n次方,则容量为 cap ,否则为大于 cap 的第一个2的n次方的数。

4.对象的四种引用?

强引用 只要引用存在,垃圾回收器永远不会回收
Object obj = new Object();
User user=new User();

可直接通过obj取得对应的对象 如 obj.equels(new Object()); 而这样 obj 对象对后面 new Object 的一个强
引用,只有当 obj 这个引用被释放之后,对象才会被释放掉,这也是我们经常所用到的编码形式。

软引用 非必须引用,内存溢出之前进行回收,可以通过以下代码实现
Object obj = new Object();
SoftReference<Object> sf = new SoftReference<Object>(obj);
obj = null;
sf.get();//有时候会返回null

这时候sf是对obj的一个软引用,通过sf.get()方法可以取到这个对象,当然,当这个对象被标记为需要回收的对象时,则返回null; 软引用主要用户实现类似缓存的功能,在内存足够的情况下直接通过软引用取值,无需从繁忙的真实来源查询数据,提升速度;当内存不足时,自动删除这部分缓存数据,从真正的来源查询这些数据。

弱引用 第二次垃圾回收时回收,可以通过如下代码实现
Object obj = new Object();
WeakReference<Object> wf = new WeakReference<Object>(obj);
obj = null;
wf.get();//有时候会返回null
wf.isEnQueued();//返回是否被垃圾回收器标记为即将回收的垃圾

弱引用是在第二次垃圾回收时回收,短时间内通过弱引用取对应的数据,可以取到,当执行过第二次垃圾回收时,将返回null。弱引用主要用于监控对象是否已经被垃圾回收器标记为即将回收的垃圾,可以通过弱引用的isEnQueued 方法返回对象是否被垃圾回收器标记。
ThreadLocal 中有使用到弱引用,

public class ThreadLocal<T> {
static class ThreadLocalMap {
	static class Entry extends WeakReference<ThreadLocal<?>> {
            /** The value associated with this ThreadLocal. */
            Object value;
            Entry(ThreadLocal<?> k, Object v) {
            super(k);
            value = v;
		}
	}
    //....
    }
//.....
}

虚引用 垃圾回收时回收,无法通过引用取到对象值,可以通过如下代码实现
Object obj = new Object();
PhantomReference<Object> pf = new PhantomReference<Object>(obj);
obj=null;
pf.get();//永远返回null
pf.isEnQueued();//返回是否从内存中已经删除

虚引用是每次垃圾回收的时候都会被回收,通过虚引用的get方法永远获取到的数据为null,因此也被成为幽灵引用。虚引用主要用于检测对象是否已经从内存中删除。

5.Java获取反射的三种方法

1.通过new对象实现反射机制 
2.通过路径实现反射机制 
3.通过类名实现反射机制

public class Student {
    private int id;
    String name;
    protected boolean sex;
    public float score;
}

public class Get {
    //获取反射机制三种方式
    public static void main(String[] args) throws ClassNotFoundException {
        //方式一(通过建立对象)
        Student stu = new Student();
        Class classobj1 = stu.getClass();
        System.out.println(classobj1.getName());
        //方式二(所在通过路径-相对路径)
        Class classobj2 = Class.forName("fanshe.Student");
        System.out.println(classobj2.getName());
        //方式三(通过类名)
        Class classobj3 = Student.class;
        System.out.println(classobj3.getName());
    }
}

6. Java反射机制

Java 反射机制是在运行状态中,对于任意一个类,都能够获得这个类的所有属性和方法,对于任意一个对象都能够
调用它的任意一个属性和方法。这种在运行时动态的获取信息以及动态调用对象的方法的功能称为 Java 的反射机
制。

Class 类与 java.lang.reflect 类库一起对反射的概念进行了支持,该类库包含了Field,Method,Constructor(每个类都实现了 Member 接口)。这些类型的对象时由 JVM 在运行时创建的,用以表示未知类里对应的成员。

这样你就可以使用 Constructor 创建新的对象,用 get()set() 方法读取和修改与 Field 对象关联的字段,用invoke() 方法调用与 Method 对象关联的方法。另外,还可以调用 getFields() getMethods()getConstructors() 等很便利的方法,以返回表示字段,方法,以及构造器的对象的数组。这样匿名对象的信息
就能在运行时被完全确定下来,而在编译时不需要知道任何事情。

import java.lang.reflect.Constructor;
public class ReflectTest {
    public static void main(String[] args) throws Exception {
        Class clazz = null;
        clazz = Class.forName("com.jas.reflect.Fruit");
        Constructor<Fruit> constructor1 = clazz.getConstructor();
        Constructor<Fruit> constructor2 = clazz.getConstructor(String.class);
        Fruit fruit1 = constructor1.newInstance();
        Fruit fruit2 = constructor2.newInstance("Apple");
    }
}
class Fruit{
    public Fruit(){
    	System.out.println("无参构造器 Run...........");
    }
    public Fruit(String type){
    	System.out.println("有参构造器 Run..........." + type);
    }
}

运行结果: 无参构造器 Run……….. 有参构造器 Run………..Apple

7.Arrays.sort 和 Collections.sort 实现原理 和区别

CollectionCollections区别

java.util.Collection 是一个集合接口。它提供了对集合对象进行基本操作的通用接口方法。
java.util.Collections 是针对集合类的一个帮助类,他提供一系列静态方法实现对各种集合的搜索、排序、
线程安全等操作。 然后还有混排(Shuffling)、反转(Reverse)、替换所有的元素(fill)、拷贝(copy)、返
回Collections中最小元素(min)、返回Collections中最大元素(max)、返回指定源列表中最后一次出现指定目
标列表的起始位置( lastIndexOfSubList )、返回指定源列表中第一次出现指定目标列表的起始位置
( IndexOfSubList )、根据指定的距离循环移动指定列表中的元素(Rotate;

事实上Collections.sort方法底层就是调用的array.sort方法,

public static void sort(Object[] a) {
    if (LegacyMergeSort.userRequested)
    	legacyMergeSort(a);
    else
    	ComparableTimSort.sort(a, 0, a.length, null, 0, 0);
}

//void java.util.ComparableTimSort.sort()
static void sort(Object[] a, int lo, int hi, Object[] work, int workBase, int workLen)
{
	assert a != null && lo >= 0 && lo <= hi && hi <= a.length;
    int nRemaining = hi - lo;
    if (nRemaining < 2)
		return; // Arrays of size 0 and 1 are always sorted
    // If array is small, do a "mini-TimSort" with no merges
    if (nRemaining < MIN_MERGE) {
        int initRunLen = countRunAndMakeAscending(a, lo, hi);
        binarySort(a, lo, hi, lo + initRunLen);
        return;
    }
}

legacyMergeSort (a):归并排序 ComparableTimSort.sort()Timsort 排序
Timsort 排序是结合了合并排序(merge sort)和插入排序(insertion sort)而得出的排序算法

Timsort的核心过程:
TimSort 算法为了减少对升序部分的回溯和对降序部分的性能倒退,将输入按其升序和降序特点进行了分
区。排序的输入的单位不是一个个单独的数字,而是一个个的块-分区。其中每一个分区叫一个run。针对这
些 run 序列,每次拿一个 run 出来按规则进行合并。每次合并会将两个 run合并成一个 run。合并的结果保
存到栈中。合并直到消耗掉所有的 run,这时将栈上剩余的 run合并到只剩一个 run 为止。这时这个仅剩的
run 便是排好序的结果。
    
综上述过程,Timsort算法的过程包括
(0)如何数组长度小于某个值,直接用二分插入排序算法
(1)找到各个run,并入栈
(2)按规则合并run    

8. LinkedHashMap 的应用?

基于 LinkedHashMap 的访问顺序的特点,可构造一个 LRU(Least Recently Used) 最近最少使用简单缓存。
也有一些开源的缓存产品如 ehcache 的淘汰策略( LRU )就是在 LinkedHashMap 上扩展的。

9. LinkedList 和 ArrayList 的区别?

数据结构:linkedlist 是基于双向链表,arraylist 是基于数组.
读写性能:尾部追加无性能差异,如果中间位置插入,linkedlist 要快,arraylist 随机查找性能更快
扩容机制:arraylist 空间不足时,需要进行扩容.
空间占用:linked 空间占用要稍微大一些.

10. LinkedHashMap 和 TreeMap 有序是一样的吗?

LinkedHashMap 是按照插入顺序排序
TreeMap 按照 key 的值排序

11. HashMap 原理?

计算 key 对应的 HashCode,该 HashCode 与自身右移 16 位进行异或运算,对 Hash 值进行扰动,减少 Hash 冲突
根据 HashCode 计算取 mod 计算该 key 所在数组中的位置(n-1)&hash,
如果对应的位置无元素,则新建一个节点放到该位置
如果该位置上存在元素,则判断该元素的类型如果是树节点,则遍历树在树中追加元素
如果是非树节点则追加到链表的尾部,如果链表超过 8 个,则转换为红黑树
如果 put 元素的个数,超过数组大小*负载因子的话,则进行扩容,
扩容的时候生成原数组大小两倍的空间,然后将原数组的元素依次拷贝到新的数组.
采用红黑树的好处是如果 hash 冲突比较严重时,ᨀ升查询的性能.避免 hash 攻击,造成安全问题

12. volatile 的作用?

volatile 的作用主要有两个,一个是防止指令重排序/一个是保持内存的可见性.
指令重排序:
    JVM 编译器会对指令进行重排序以ᨀ升并发性能.会让指令按照非程序语义的顺序执行.有编译重排序,处理器指
    令重排序、内存重排序
    而指令重排序在多线程情况下可能存在问题,比如单例模式,多线程下可能获取未初始化的对象.
    创建对象的过程:
        1.申请内存地址.
        2.在内存地址上初始化对象
        3.将引用指向内存地址
    假设按照 1->3->2 的顺序执行.就会存在问题.
    我们通过增加 volatile 关键字来禁用重排序
    
内存可见:
    可见性就是一个线程修改了某个变量的值,其他线程就能立即看到修改的值.
    Jvm 内存模型规定了所有的变量都是存储在主存中的,每个线程还有自己的工作线程,工作线程保存了主存变量
    的副本,不同线程间通讯需要先同步到主存.
    使用 volatile 修饰能在每次变量发生变化后,强制刷新到主存
    
内存屏障:
	阻止屏障两侧的指令重排序;即屏障下面的代码不能跟屏障上面的代码交换执行顺序
    强制把写缓冲区/高速缓存中的脏数据等写回主内存,让缓存中相应的数据失效。
    对于 Load Barrier 来说,在指令前插入 Load Barrier,可以让 CPU 高速缓存中 j 的数据失效,强制从新从主内存加载新数据;
    对于 Store Barrier 来说,在指令后插入 Store Barrier,能让写入缓存中的最新数据更新写入主内存,让其
    他线程可见

13. Wait 和 sleep 的区别?

sleep 是让出 CPU 的时间片,但是并不释放他所持有的对象锁.
wait 是放弃对象锁,进入 ObjetMonitor 的 waitSet 中,notify 后重现尝试去竞争锁

14. 浅 clone 和深 clone 的区别?

浅 clone:指拷贝对象时仅仅拷贝对象本身(包括对象中的基本变量),而不拷贝对象包含的引用指向的对象。
深 clone:不仅拷贝对象本身,而且拷贝对象包含的引用指向的所有对象。

15. hashCode 和 equals 的区别?

两个都是用来判断对象是否相等的依据.hashCode 性能较好,根据对象生成的 hashCode,直接比较判断,而
equals 相对较慢,需要有一定的逻辑判断.
重写 equals 一定要重写 hashCode,hashCode 相等不一定 equals,equals 相等一定 hashCode 相等.
HashMap、Set 等就先根据 hashCode 做判断以升性能

16. 悲观锁和乐观锁 的区别?

悲观锁:总是假设最坏的情况,每次去拿数据的时候,都认为别人会修改.例如关系型数据库的库锁、表锁、行锁
等,以及 Java 中的 synchronize 和 reentrantLock 等独占锁等.
乐观锁:总是假设最好的情况,每次去拿数据的时候,都认为别人不会修改.如果存在锁竞争,可使用 CAS 算法,乐
观锁适用于多读的情况.Atomic 下就是乐观锁的实现.

17. ThreadLocal 的实现原理?

当向 ThreadLocal 中 set 一个变量的时候,实际上先获取当前线程的 threadlocals,它是一个 Map 结构,key 为
ThreadLocal 对象的弱引用,Value 为设置的值.
Entry 中 key 为弱引用的目的就是当 ThreadLocal 置空时,Entry 中的 key 与 threadlocal 只剩下一弱用,当垃圾回收时,就可以回收 threadlocal 对象.
使用线程池存在什么问题,因为线程池每次获取的可能不是一个线程,有的时候甚至线程会消亡,有的线程可能永驻线程池,因此会导致 threadlocal 的 set 和 get 不一致,或者内存溢出等问题.

18.动态代理和 Cglib 有什么区别?

java 动态代理-|利用 Java 的反射机制生成了代理接口的匿名类,代理的类必须要实现接口

cglib-|利用 ASM 开源包,对代理对象的 class 文件加载进来,通过修改字节码生成的子类来实现的,代理的类不需要实现接口

19.Redis 数据结构及使用场景?

Stringlistsetsortsetmap
数据结构value 为数字和字符串双向链表value 为空的 HashMapHashMap+跳表hashmap
使用场景计数器\缓存json 数据队列或者列表去重,取交集、并集 等排序,排名缓存对象
常用命令get、set、 incr、 decr、mgetlpush,rpush,lp op,rpop,lrange ,BLPOPsadd,srem,spop,sdi ff ,smembers,sunionzadd,zrange,zrem, zcardhget、hset、 hgetall

20.Redis 为什么可以用做分布式锁?

1、Redis 为单进程单线程模式,采用队列模式将并发访问变成串行访问,且多客户端对 Redis 的连接并不存在
竞争关系。
2、Redis 的 SETNX 命令可以方便的实现分布式锁。
setNX(SET if Not eXists)
语法:SETNX key value
返回值:设置成功,返回 1 ;设置失败,返回 0
当且仅当 key 不存在时将 key 的值设为 value,并返回 1;若给定的 key 已经存在,则 SETNX 不
做任何动作,并返回 0。
综上所述,可以通过 setnx 的返回值来判断是否获取到锁,并且不用担心并发访问的问题,因为 Redis 是单线
程的,所以如果返回 1 则获取到锁,返回 0 则没获取到。当业务操作执行完后,一定要释放锁,释放锁的逻辑
很简单,就是把之前设置的 key 删除掉即可,这样下次又可以通过 setnx 该 key 获取到锁了。

21. 什么是缓存穿透和缓存雪崩 ?

缓存穿透:
如果 key 对应缓存不存在,每次请求不会从缓存获取,而是直接查询到数据源,从而压垮数据源.比如,相同的超
时时间或者 key 在内存和数据源都不存在.
如果没有数据,也要将空的结果返回. 采用布隆过滤器

缓存击穿:
如果缓存过期,大量的并发请求过来,大量的请求从数据库加载,压垮数据库.主要针对是同一个 key 的并发.
增加互斥锁

缓存雪崩:
服务器重启或者大量缓存在同一时间失效,会给数据库带来很大的压力.多个 key 的瞬时流量.
设置随机过期时间.

22.B+树与 B 树的区别 ?

B+树是 B-树的变体,也是一种多路搜索树, 它与 B- 树的不同之处在于:
1.所有关键字存储在叶子节点出现,内部节点(非叶子节点并不存储真正的 data)
2.为所有叶子结点增加了一个链指针

b+树的中间节点不保存数据,所以磁盘页能容纳更多节点元素,更“矮胖”;
b+树查询必须查找到叶子节点,b 树只要匹配到即可不用管元素位置,因此 b+树查找更稳定(并不慢);
对于范围查找来说,b+树只需遍历叶子节点链表即可,b 树却需要重复地中序遍历,如下两图:
有 k 个子树的中间节点包含有 k 个元素(B 树中是 k-1 个元素),每个元素不保存数据,只用来索引,所有数
据都保存在叶子节点。
所有的叶子结点中包含了全部元素的信息,及指向含这些元素记录的指针,且叶子结点本身依关键字的大小自
小而大顺序链接。
所有的中间节点元素都同时存在于子节点,在子节点元素中是最大(或最小)元素

23. 索引设计的原则 ?

索引尽量复用,不要重复创建索引
不要过度索引,索引过多会导致写性能下降
最左原则
索引使用的字段缩短长度,比如字符串,数字类型等.

24.千万大表的优化手段?

1.优化现有 Mysql
索引优化 explain 的索引使用情况 、覆盖索引、避免回表、合理设计索引等
增加统计表
增加缓存
读写分离
分库分表:水平拆分:范围分、日期、某个字段 hash 取模;垂直拆分

2.换一种 100%兼容的 Mysql 数据库,云数据库等

3.换 Nosql 存储或者大数据解决方案.

25.分库分表需要考虑哪些问题?

全局主键的问题:雪花算法,64 位也就是 8 字节的整数,由时间戳+工作机器 ID+序列号
事务的问题:分布式事务
join:各库存储小表或者服务层处理
分页、聚集等:各自处理然后统一聚集
数据迁移:平滑升级需要数据双写

26. 为什么 ID 要求有序?

防止页分裂及页挪动

27. mysql 是如何保证不丢数据的?

是利用了 redo log。Mysql 是先把磁盘上的数据加载到内存中,在内存中对数据进行修改,再刷回磁盘上。如
果此时突然宕机,内存中的数据就会丢失。redo log 包括两部分:一是内存中的日志缓冲(redo log
buffer),该部分日志是易失性的;二是磁盘上的重做日志文件(redo log file),该部分日志是持久的。
innodb 通过 force log at commit 机制实现事务的持久性,即在事务ᨀ交的时候,必须先将该事务的所有事务
日志写入到磁盘上的 redo log file 和 undo log file 中进行持久化。也就是说提交了两个日志文件。

28.数据分区方案有哪些?

范围分区:比如时间、业务字段(比如品牌 ID)、优点方便扩容,缺点容易造成数据热点
Hash 求余分区:数据均匀分布,但是扩容、缩容不方便
Hash 一致性分区:扩容、缩容只需要少部分数据迁移,数据分布均匀
虚拟槽分区:redis 分区策略,将数据分为 2^14 个槽,数据均匀分布,容易扩缩容

29.限流的算法有哪些?

计数器算法
漏桶算法
令牌桶算法

30.多路复用技术有哪些?

select 只知道有流事件,将消息传递从内核空间拷贝到用户空间,有最大连接数的限制,1000
poll 跟 select 类似,但是没有最大连接数的限制
而 epoll 知道具体是哪个流有事件发生.用户空间和内核空间共享内存.

31.Spring中用到哪些设计模式?

工厂:根据 bean 的名称获取
动态代理:aop
观察者:applicationListener
模版:jdbctemplate
单例:scopere

32.对SpringIOC 的理解?

控制反转,以前对象都是自己创建把控,现在交给 spring 容器来管理.Spring 容器通过配置文件、注解以及
Configuration 等来创建和管理容器的依赖关系.也有利于对象的复用和生命周期的管理.

33. 对SpringAOP的理解?

利用 java 动态代理或者 cglib,将公共行为封装成可重用模块,减少系统的重复代码,降低模块间的耦合度.比如
日志记录、事务、监控、权限控制等等.

34.Kafka 快的原因?

1.Kafka 基于磁盘顺序写的.机械硬盘每次读写都会先寻址是很耗时的.Kafka 每个分区都是一个文件,每次写入
的时候把数据追加到文件尾部.这种设计有一种缺陷,就是没办法删除数据.
2.Kafka 并没有调用 write 直接写磁盘,而是通过内存映射文件的方式,它是利用操作系统的页实现文件到物理
内存的直接映射.完成映射之后,对物理内存的操作会同步到磁盘.producer.type 来配置是同步还是异步刷新内
存.
3.分段快速检索
4.实现零拷贝,从内核空间到网卡直接拷贝不需要经过用户空间.
5.分区支持多消费者同时读取

35.Kafka为什么不会丢数据?

1.分区的副本同步机制,从分区的所有副本中,选取一个作为 master,其他的作为 followers,leader ᨀ供读
写,follower 从 leader 拉取数据进行同步,当 Leader 挂了,从 ISR 列表中选取一个作为 leader
2.request.required.acks 有三个值
0:不需要 server 响应
1:leader 成功后,作出响应
-1:所有的 ISR 同步成功后,作出响应
可以将 min.insync.replicas:配置为 2 在吞吐量和数据可靠性之间做一个衡量
3.leader 新写入的消息,并不是立马就能读取到,Leader 会等 ISR 中的所有副本同步后,更新到高水位才会被
consumer 消费到,以免 leader 挂了之后,导致数据不一致

36.final 在 Java 中有什么作用?

final 修饰的类叫最终类,该类不能被继承。
final 修饰的方法不能被重写。
final 修饰的变量叫常量,常量必须初始化,初始化之后值就不能被修改。

37.Java 中操作字符串都有哪些类?它们之间有什么区别?

操作字符串的类有:String、StringBuffer、StringBuilder。

String 和 StringBuffer、StringBuilder 的区别在于 String 声明的是不可变的对象,每次操作都会生成新的 String 对象,然后将指针指向新的 String 对象,而 StringBuffer、StringBuilder 可以在原有对象的基础上进行操作,所以在经常改变字符串内容的情况下最好不要使用 String。

StringBuffer 和 StringBuilder 最大的区别在于,StringBuffer 是线程安全的,而 StringBuilder 是非线程安全的,但 StringBuilder 的性能却高于 StringBuffer,所以在单线程环境下推荐使用 StringBuilder,多线程环境下推荐使用 StringBuffer。

38.String str="i"与 String str=new String(“i”)一样吗?

不一样,因为内存的分配方式不一样。String str="i"的方式,Java 虚拟机会将其分配到常量池中;而 String str=new String("i") 则会被分到堆内存中。

39. 如何决定使用 HashMap 还是 TreeMap?

对于在 Map 中插入、删除、定位一个元素这类操作,HashMap 是最好的选择,因为相对而言 HashMap 的插入会更快,但如果你要对一个 key 集合进行有序的遍历,那 TreeMap 是更好的选择。

40.哪些集合类是线程安全的?

Vector、Hashtable、Stack 都是线程安全的,而像 HashMap 则是非线程安全的,不过在 JDK 1.5 之后随着 Java. util. concurrent 并发包的出现,它们也有了自己对应的线程安全类,比如 HashMap 对应的线程安全类就是 ConcurrentHashMap。

41.解释内存中的栈(stack)、堆(heap)和静态存储区的用法?

通常我们定义一个基本数据类型的变量,一个对象的引用,还有就是函数调用的现场保存都使用内存中的栈空间;

而通过 new 关键字和构造器创建的对象放在堆空间;

程序中的字面量(literal)如直接书写的 100、“hello”和常量都是放在静态存储区中。栈空间操作最快但是也很小,通常大量的对象都是放在堆空间,整个内存包括硬盘上的虚拟内存都可以被当成堆空间来使用。

String str = new String(“hello”);

上面的语句中 str 放在栈上,用 new 创建出来的字符串对象放在堆上,而“hello”这个字面量放在静态存储区。

补充:较新版本的 Java 中使用了一项叫“逃逸分析“的技术,可以将一些局部对象放在栈上以提升对象的操作性能。

42.上千万条消息在mq中积压了⼏个⼩时如何解决?

1)先修复consumer的问题,确保其恢复消费速度,然后将现有consumer都停掉;
2)新建⼀个topic,partition是原来的10倍,临时建⽴好原先10倍或者20倍的queue数量;
3)然后写⼀个临时的分发数据的consumer程序,这个程序部署上去消费积压的数据;
消费之后不做耗时的处理,直接均匀轮询写⼊临时建⽴好的10倍数量的queue;
4)接着临时征⽤10倍的机器来部署consumer,每⼀批consumer消费⼀个临时queue的数据;
5)这种做法相当于是临时将queue资源和consumer资源扩⼤10倍,以正常的10倍速度来消费数据;
6)等快速消费完积压数据之后,得恢复原先部署架构,重新⽤原先的consumer机器来消费消息。

总结:
1. 修复并停掉consumer;
2. 新建⼀个topic,partition是原来的10倍,建⽴临时queue,数量是原来的10倍或20倍;
3. 写临时consumer程序,临时征⽤10倍的机器去消费数据;
4. 消费完成之后,恢复原先consumer;

43.如何避免消息重复投递或重复消费?

在消息⽣产时,MQ内部针对每条⽣产者发送的消息⽣成⼀个inner-msg-id,作为去重和幂等的依据(消息投递失败并重
传),避免重复的消息进⼊队列;在消息消费时,要求消息体中必须要有⼀个bizId(对于同⼀业务全局唯⼀,如⽀付ID、订单
ID、帖⼦ID等)作为去重和幂等的依据,避免同⼀条消息被重复消费。

44. Java 类加载过程?

1. 加载
加载是类加载的第一个过程,在这个阶段,将完成一下三件事情:
	• 通过一个类的全限定名获取该类的二进制流。
	• 将该二进制流中的静态存储结构转化为方法去运行时数据结构。
	• 在内存中生成该类的 Class 对象,作为该类的数据访问入口。
2. 验证
验证的目的是为了确保 Class 文件的字节流中的信息不回危害到虚拟机.在该阶段主要完成以下四钟验证:
    • 文件格式验证:验证字节流是否符合 Class 文件的规范,如主次版本号是否在当前虚拟机范围内,常量池中的常
      量是否有不被支持的类型.
    • 元数据验证:对字节码描述的信息进行语义分析,如这个类是否有父类,是否集成了不被继承的类等。
    • 字节码验证:是整个验证过程中最复杂的一个阶段,通过验证数据流和控制流的分析,确定程序语义是否正确,主
      要针对方法体的验证。如:方法中的类型转换是否正确,跳转指令是否正确等。
    • 符号引用验证:这个动作在后面的解析过程中发生,主要是为了确保解析动作能正确执行。
3. 准备
准备阶段是为类的静态变量分配内存并将其初始化为默认值,这些内存都将在方法区中进行分配。准备阶段不分配类中的实例变量的内存,实例变量将会在对象实例化时随着对象一起分配在 Java 堆中。
	public static int value=123; //在准备阶段 value 初始值为 0 。在初始化阶段才会变为 123。
4. 解析
该阶段主要完成符号引用到直接引用的转换动作。解析动作并不一定在初始化动作完成之前,也有可能在初始化之后。
5. 初始化
初始化时类加载的最后一步,前面的类加载过程,除了在加载阶段用户应用程序可以通过自定义类加载器参与之外,其余动作完全由虚拟机主导和控制。到了初始化阶段,才真正开始执行类中定义的Java 程序代码。
6. 使用
7. 卸载

45.简述 Java 垃圾回收机制?

在 Java 中,程序员是不需要显示的去释放一个对象的内存的,而是由虚拟机自行执行。在 JVM 中,有一个垃圾回收线程,它是低优先级的,在正常情况下是不会执行的,只有在虚拟机空闲或者当前堆内存不足时,才会触发执行,扫面那些没有被任何引用的对象,并将它们添加到要回收的集合中,进行回收。

46. CPU资源占用高如何排查?

一般通用采用arthas 来排查问题。

如果arthas场景无法使用如何排查?
  1. top 查看当前CPU情况,找到占用CPU过高的进程PID=123
  2. top  -H -p 123 找出两个CPU占用较高的线程,记录下来 PID=2345,3456转换为十六进制。
  3. jstack -l 123 > temp.txt 打印出当前进程的线程栈。
  4. 查找到对应于第二步的两个线程运行栈,分析代码。

47.OOM异常如何排查?

1.使用top指令查询服务器系统状态。
2.ps -aux|grep java 找出当前Java进程的PID
3.jstat -gcutil pid interval 查看当前GC的状态
4.jmap -histo:live pid 可用统计存活对象的分布情况,从高到低查看占据内存最多的对象。
5.jmap -dump:forma=b,file=文件名 [pid] 
6.使用性能分析工具对上一步dump出来的文件进行分析,工具有MAT等。

48.Kafka消息是采用pull模式还是push模式,为什么?

Kafka 最初考虑的问题是,customer 应该从 brokes 拉取消息还是 brokers 将消息推送到
consumer,也就是 pull 还 push。在这方面,Kafka 遵循了一种大部分消息系统共同的传统
的设计:producer 将消息推送到 broker,consumer 从 broker 拉取消息。

一些消息系统比如 Scribe 和 Apache Flume 采用了 push 模式,将消息推送到下游的
consumer。这样做有好处也有坏处:由 broker 决定消息推送的速率,对于不同消费速率的
consumer 就不太好处理了。消息系统都致力于让 consumer 以最大的速率最快速的消费消
息,但不幸的是,push 模式下,当 broker 推送的速率远大于 consumer 消费的速率时,
consumer 恐怕就要崩溃了,最终 Kafka 还是选取了传统的 pull 模式。
Pull 模式的另外一个好处是 consumer 可以自主决定是否批量的从 broker 拉取数据。Push
模式必须在不知道下游 consumer 消费能力和消费策略的情况下决定是立即推送每条消息还
是缓存之后批量推送。如果为了避免 consumer 崩溃而采用较低的推送速率,将可能导致一
次只推送较少的消息而造成浪费。Pull 模式下,consumer 就可以根据自己的消费能力去决
定这些策略。

Pull 有个缺点是,如果 broker 没有可供消费的消息,将导致 consumer 不断在循环中轮询,
直到新消息到 t 达。为了避免这点,Kafka 有个参数可以让 consumer 阻塞知道新消息到达。
(当然也可以阻塞知道消息的数量达到某个特定的量这样就可以批量发)

49.为什么说Kafka吞吐效率高?

(1).Kafka 把 topic 中一个 parition 大文件分成多个小文件段,通过多个小文件段,就容易定
期清除或删除已经消费完文件,减少磁盘占用。
(2).通过索引信息可以快速定位 message 和确定 response 的最大大小。
(3).通过 index 元数据全部映射到 memory,可以避免 segment file 的 IO 磁盘操作。
(4).通过索引文件稀疏存储,可以大幅降低 index 文件元数据占用空间大小。

50.Kafka 创建 Topic 时如何将分区放置到不同的 Broker 中?

1.Kafka 创建 Topic 时如何将分区放置到不同的 Broker 中。
2.第一个分区(编号为 0)的第一个副本放置位置是随机从 brokerList 选择的。
3.其他分区的第一个副本放置位置相对于第 0 个分区依次往后移。也就是如果我们有 5 个
   Broker,5 个分区,假设第一个分区放在第四个 Broker 上,那么第二个分区将会放在第五
   个 Broker 上;第三个分区将会放在第一个 Broker 上;第四个分区将会放在第二个
   Broker 上,依次类推;
4.剩余的副本相对于第一个副本放置位置其实是由 nextReplicaShift 决定的,而这个数也是随机产生的。

51.Kafka的数据如何保存到硬盘?

topic 中的多个 partition 以文件夹的形式保存到 broker,每个分区序号从 0 递增,且消息有序。
Partition 文件下有多个 segment(xxx.index,xxx.log)
segment 文件里的 大小和配置文件大小一致可以根据要求修改 默认为 1g
如果大小大于 1g 时,会滚动一个新的 segment 并且以上一个 segment 最后一条消息的偏移量命名

52. Kafka 的消费者如何消费数据?

消费者每次消费数据的时候,消费者都会记录消费的物理偏移量(offset)的位置等到下次消费时,
他会接着上次位置继续消费。

53. MySQL 的主从复制了解吗?

主库将变更写入 binlog 日志,然后从库连接到主库之后,从库有一个 IO 线程,将主库的 binlog 日志拷贝到自己本地,写入一个 relay 中继日志中接着从库中有一个 SQL 线程会从中 继日志读取 binlog,然后执行 binlog 日志中的内容,也就是在自己本地再次执行一遍 SQL。

54.MySQL产生主从延迟的原因?

1.主库的从库太多
2.从库硬件配置比主库差
3.慢 SQL 语句过多
4.主从库之间的网络延迟
5.主库读写压力大

55.MySQL中有哪几种锁?

1.按照锁的粒度划分:行锁、表锁、页锁
2.按照锁的使用方式划分:共享锁、排它锁(悲观锁的一种实现)
3.还有两种思想上的锁:悲观锁、乐观锁。
4.InnoDB中有几种行级锁类型:Record Lock、Gap Lock、Next-key Lock
5.Record Lock:在索引记录上加锁
6.Gap Lock:间隙锁
7.Next-key Lock:Record Lock+Gap Lock

56.对SQL慢查询会考虑哪些优化 ?

1.索引优化: explain 的索引使用情况,覆盖索引、避免回表、合理设计索引等。
2.增加统计表: 减少对明细表直接查询
3.增加缓存: 利用缓存,减少查询次数。
4.读写分离: 减小主库压力。
5.分库分表: 
         水平拆分:范围分、日期、某个字段 hash 取模等;
         垂直拆分:根据具体业务垂直拆分;

57.如何使用Redis统计网站的UV?

注意: UV与PV不同,UV需要去重。
一般有2种方案:
1、用BitMap。存的是用户的uid,计算UV的时候,做下bitcount就行了。
2、用布隆过滤器。将每次访问的用户uid都放到布隆过滤器中。优点是省内存,缺点是无法得 到精确的UV。但是对于不需要精确知道具体UV,只需要大概的数量级的场景,是个不错的选择。

58.Redis 如何做内存优化?

尽可能使用散列表(hashes),散列表(是说散列表里面存储的数少)使用的内存非常小, 所以你应该尽可能的将你的数据模型抽象到一个散列表里面。比如你的 web 系统中有一个用 户对象,不要为这个用户的名称,姓氏,邮箱,密码设置单独的 key,而是应该把这个用户的 所有信息存储到一张散列表里面.

59.Redis使用需要注意哪些问题?

(1)Master 最好不要写内存快照,如果 Master 写内存快照,save 命令调度 rdbSave 函数,会阻塞主线程的工作,当快照比较大时对性能影响是非常大 的,会间断性暂停服务
(2)如果数据比较重要,某个 Slave 开启 AOF 备份数据,策略设置为每秒同步一
(3)为了主从复制的速度和连接的稳定性,Master 和 Slave 最好在同一个局域网
(4)尽量避免在压力很大的主库上增加从 
(5)主从复制不要用图状结构,用单向链表结构更为稳定,即:Master <- Slave1<- Slave2 <- Slave3...这样的结构方便解决单点故障问题,实现 Slave 对 Master 的替换。如 果 Master 挂了,可以立刻启用 Slave1 做 Master,其 他不变。

60.redis常用过期键的删除策略?

(1)定时删除:在设置键的过期时间的同时,创建一个定时器 timer). 让定时 器在键的过期时 间来临时,立即执行对键的删除操作。
(2)惰性删除:放任键过期不管,但是每次从键空间中获取键时,都检查取得 的键是否过期, 如果过期的话,就删除该键;如果没有过期,就返回该键。
(3)定期删除:每隔一段时间程序 就对数据库进行一次检查,删除里面的过期 键。至于要删除多少过期键,以及要检查多少个 数据库,则由算法决定。

61.Redis集群如何定位key的?

1.使用crc16算法对key进行hash;
2.将hash值对16384取模,得到具体的槽位;
3.根据节点和槽位的映射信息(与集群建立连接后,客户端可以取得槽位映射信息),找到具体 的节点地址;
4.去具体的节点找key;
5.如果key不在这个节点上,则redis集群会返回moved指令,加上新的节点地址给客户端。同 时,客户端会刷新本地的节点槽位映射关系;
6.如果槽位正在迁移中,那么redis集群会返回asking指令给客户端,这是临时纠正,客户端不 会刷新本地的节点槽位映射关系;

62.volatile和Synchronized有什么异同?

Synchronized既能保证可见性,又能保证原子性,而volatile只能保证可见性,无法保证原子性。
ThreadLocal 和 Synchronized都用于解决多线程并发访问,防止任务共享资源上产生冲突。但是ThreadLocal与Synchronized有本质的区别。
Synchronized用于实现同步机制,是利用锁的机制使变量或代码块在某一时刻只能被一个线程访问,是一种“以时间换空间”的方式。
而ThreadLocal为每一个线程都提供了变量的副本,使得每个线程在某一时间访问到的并不是同一个对象,根除了对变量的共享,是一种“以空间换时间”的方式。

63.请谈谈ThreadLocal是怎么解决并发安全的?

ThreadLocal这是Java提供的一种保存线程私有信息的机制,因为其在整个线程生命周期内有效,所以可以方便地在一个线程关联的不同业务模块之间传递信息,比如事务ID、Cookie等上下文相关信息。
ThreadLocal为每一个线程维护变量的副本,把共享数据的可见范围限制在同一个线程之内,其实现原理是,在ThreadLocal类中有一个Map,用于存储每一个线程的变量的副本。

64.什么是控制反转(IOC)?什么是依赖注入?

1、控制反转是应用于软件工程领域中的,在运行时被装配器对象来绑定耦合对象的一种编程技巧,对象之间耦合关系在编译时通常是未知的。在传统的编程方式中,业务逻辑的流程是由应用程序中的早已被设定好关联关系的对象来决定的。在使用控制反转的情况下,业务逻辑的流程是由对象关系图来决定的,该对象关系图由装配器负责实例化,这种实现方式还可以将对象之间的关联关系的定义抽象化。而绑定的过程是通过“依赖注入”实现的。

2、控制反转是一种以给予应用程序中目标组件更多控制为目的设计范式,并在我们的实际工作中起到了有效的作用。

3、依赖注入是在编译阶段尚未知所需的功能是来自哪个的类的情况下,将其他对象所依赖的功能对象实例化的模式。这就需要一种机制用来激活相应的组件以提供特定的功能,所以依赖注入是控制反转的基础。否则如果在组件不受框架控制的情况下,框架又怎么知道要创建哪个组件?

65. Spring支持哪几种配置方式?

1.基于XML的配置;
2.基于注解的配置;
3.基于Java的配置;

66. Spring框架中都用到了哪些设计模式?

Spring框架中使用到了大量的设计模式,下面列举了比较有代表性的:

代理模式—在 AOP 和 remoting 中被用的比较多。
单例模式—在 spring 配置文件中定义的 bean 默认为单例模式。
模板方法—用来解决代码重复的问题。比如. RestTemplate, JmsTemplate, JpaTemplate。
前端控制器—Srping提供了 DispatcherServlet 来对请求进行分发。
视图帮助(View Helper )—Spring提供了一系列的JSP标签,高效宏来辅助将分散的代码整合在视图里。
依赖注入—贯穿于 BeanFactory / ApplicationContext 接口的核心理念。
工厂模式—BeanFactory用来创建对象的实例。

67. 防止Sql注入,Sql语句需要怎么做?

使用占位符来取代字符串拼接

68. 假如 Redis 里面有 1 亿个 key,其中有 10w 个 key 是以某 个固定的已知的前缀开头的,如果将它们全部找出来?

使用 keys 指令可以扫出指定模式的 key 列表。

对方接着追问:如果这个 Redis 正在给线上的业务提供服务,那使用 keys 指令会有什么问题?

这个时候你要回答 Redis 关键的一个特性:Redis 的单线程的。keys 指令会导致线程阻塞一段时间,线上服务会停顿,直到指令执行完毕,服务才能恢复。这个时候可以使用 scan 指令,scan 指令可以无阻塞的提取出指定模式的 key 列表,但是会有一定的重复概率,在客户端做一次去重就可以了,但是整体所花费的时间会比直接用 keys 指令长.

69. Redis支持哪几种数据类型?

String、List、Set、Sorted Set、Hashes

70.IoC有哪些好处?

它将最小化应用程序中的代码量。
它将使您的应用程序易于测试, 因为它不需要单元测试用例中的任何单例或 JNDI 查找机制。
它以最小的影响和最少的 侵入机制促进松耦合。
它支持即时的实例化和延迟加载服务。

71. Spring 事务注解的失效场景?

Transactional 注解应用在非 public 修饰的方法上@Transactional 注解属性 propagation 设置错误
@Transactional 注解属性 rollbackFor 设置错误
同一个类中方法调用,导致@Transactional 失效
异常被 catch“吃了”导致@Transactional 失效

72.Spring事务中的隔离级别有哪几种?

在TransactionDefinition接口中定义了五个表示隔离级别的常量:
ISOLATION_DEFAULT:使用后端数据库默认的隔离级别,Mysql默认采用的

REPEATABLE_READ隔离级别;Oracle默认采用的READ_COMMITTED隔离级别。

ISOLATION_READ_UNCOMMITTED:最低的隔离级别,允许读取尚未提交的数据变更, 可能会导致脏读、幻读或不可重复读。

ISOLATION_READ_COMMITTED:允许读取并发事务已经提交的数据,可以阻止脏读, 但是幻读或不可重复读仍有可能发生

ISOLATION_REPEATABLE_READ:对同一字段的多次读取结果都是一致的,除非数据是 被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。

ISOLATION_SERIALIZABLE:最高的隔离级别,完全服从ACID的隔离级别。所有的事务依 次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重 复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。

73.Spring 和 SpringBoot 有什么不同?

Spring 框架提供多种特性使得 web 应用开发变得更简便,包括依赖注入、数据绑定、切面编程、数据存取等等。

随着时间推移,Spring 生态变得越来越复杂了,并且应用程序所必须的配置文件也令人觉得可怕。这就是 Spring Boot 派上用场的地方了 – 它使得 Spring 的配置变得更轻而易举。

实际上,Spring 是 unopinionated(予以配置项多,倾向性弱) 的,Spring Boot 在平台和库的做法中更 opinionated ,使得我们更容易上手。

这里有两条 SpringBoot 带来的好处:
1. 根据 classpath 中的 artifacts 的自动化配置应用程序
2. 提供非功能性特性例如安全和健康检查给到生产环境中的应用程序

74.说说Kafka中LEO、LSO、AR、ISR、HW 关键字含义?

LEO:Log End Offset。日志末端位移值或末端偏移量,表示日志下一条待插入消息的 位移 值。举个例子,如果日志有 10 条消息,位移值从 0 开始,那么,第 10 条消息的位 移值就是 9。此时,LEO = 10。

LSO:Log Stable Offset。这是 Kafka 事务的概念。如果你没有使用到事务,那么这个 值不存 在(其实也不是不存在,只是设置成一个无意义的值)。该值控制了事务型消费 者能够看到的消 息范围。它经常与 Log Start Offset,即日志起始位移值相混淆,因为 有些人将后者缩写成 LSO,这是不对的。在 Kafka 中,LSO 就是指代 Log Stable Offset。 AR:Assigned Replicas。AR 是主题被创建后,分区创建时被分配的副本集合,副本个 数由副 本因子决定。

ISR:In-Sync Replicas。Kafka 中特别重要的概念,指代的是 AR 中那些与 Leader 保 持同步 的副本集合。在 AR 中的副本可能不在 ISR 中,但 Leader 副本天然就包含在 ISR 中。关于 ISR,还有一个常见的面试题目是如何判断副本是否应该属于 ISR。目前的判断 依据 是:Follower 副本的 LEO 落后 Leader LEO 的时间,是否超过了 Broker 端参数 replica.lag.time.max.ms 值。如果超过了,副本就会被从 ISR 中移除。

HW:高水位值(High watermark)。这是控制消费者可读取消息范围的重要字段。一 个普通消 费者只能“看到”Leader 副本上介于 Log Start Offset 和 HW(不含)之间的 所有消息。水位以 上的消息是对消费者不可见的。关于 HW,问法有很多,我能想到的 最高级的问法,就是让你 完整地梳理下 Follower 副本拉取 Leader 副本、执行同步机制 的详细步骤。

75.kafka中什么情况下一个broker会从ISR中被踢出去?

leader会维护一个与其基本保持同步的Replica列表,该列表称为ISR(in-sync Replica),每 个Partition都会有一个ISR,而且是由leader动态维护 ,如果一个follower比一个leader落后 太多,或者超过一定时间未发起数据复制请求,则leader将其重ISR中移除 。

76. kafka为什么那么快?

Cache Filesystem Cache PageCache缓存顺序。 由于现代的操作系统提供了预读和写技术,磁盘的顺序写大多数情况下比随机写内存还要快。

Zero-copy。零拷技术减少拷贝次数

Batching of Messages 批量量处理。合并小 的请求,然后以流的方式进行交互,直顶网络上限。

Pull拉模式。使用拉模式进行消息的获取消费,与消费端处理能力相符。

77. 如何估算Kafka集群的机器数量?

通常来说,CPU 和内存资源的充足是比较容易保证的,因此,你需要从磁盘空间和带宽占用两 个维度去评估机器数量。

在预估磁盘的占用时,你一定不要忘记计算副本同步的开销。如果一条消息占用 1KB 的磁 盘 空间,那么,在有 3 个副本的主题中,你就需要 3KB 的总空间来保存这条消息。显式地 将这 些考虑因素答出来,能够彰显你考虑问题的全面性,是一个难得的加分项。

对于评估带宽来说,常见的带宽有 1Gbps 和 10Gbps,但你要切记,这两个数字仅仅是最大 值。因此,你最好和面试官确认一下给定的带宽是多少。然后,明确阐述出当带宽占用接 近总 带宽的 90% 时,丢包情形就会发生。这样能显示出你的网络基本功。

78.Kafka的哪些场景中使用了零拷贝?

Zero Copy 是特别容易被问到的高阶题目。在 Kafka 中,体现 Zero Copy 使用场景的地方有两处:基于 mmap 的索引和日志文件读写所用的 TransportLayer。

先说第一个。索引都是基于 MappedByteBuffer 的,也就是让用户态和内核态共享内核态 的 数据缓冲区,此时,数据不需要复制到用户态空间。不过,mmap 虽然避免了不必要的 拷 贝,但不一定就能保证很高的性能。在不同的操作系统下,mmap 的创建和销毁成本可 能是 不一样的。很高的创建和销毁开销会抵消 Zero Copy 带来的性能优势。由于这种不确 定性, 在 Kafka 中,只有索引应用了 mmap,最核心的日志并未使用 mmap 机制。

再说第二个。TransportLayer 是 Kafka 传输层的接口。它的某个实现类使用了 FileChannel 的 transferTo 方法。该方法底层使用 sendfile 实现了 Zero Copy。对 Kafka 而言,如果 I/O 通道使用普通的 PLAINTEXT,那么,Kafka 就可以利用 Zero Copy 特 性,直接将页缓存中 的数据发送到网卡的 Buffer 中,避免中间的多次拷贝。相反,如果 I/O 通道启用了 SSL,那 么,Kafka 便无法利用 Zero Copy 特性了。

79.Kafka如何调优?

回答任何调优问题的第一步,就是确定优化目标,并且定量给出目标!这点特别重要。对于 Kafka 而言,常见的优化目标是吞吐量、延时、持久性和可用性。每一个方向的优化思路都 是 不同的,甚至是相反的。

确定了目标之后,还要明确优化的维度。有些调优属于通用的优化思路,比如对操作系统、 JVM 等的优化;有些则是有针对性的,比如要优化 Kafka 的 TPS。我们需要从 3 个方向去考虑:

Producer 端:增加 batch.size、linger.ms,启用压缩,关闭重试等。

Broker 端:增加 num.replica.fetchers,提升 Follower 同步 TPS,避免 Broker Full GC 等。

Consumer:增加 fetch.min.bytes 等

80.简述Kafka中Follower消息同步的完整流程?

首先,Follower 发送 FETCH 请求给 Leader。接着,Leader 会读取底层日志文件中的消 息 数据,再更新它内存中的 Follower 副本的 LEO 值,更新为 FETCH 请求中的 fetchOffset 值。最后,尝试更新分区高水位值。Follower 接收到 FETCH 响应之后,会把 消息写入到底 层日志,接着更新 LEO 和 HW 值。

Leader 和 Follower 的 HW 值更新时机是不同的,Follower 的 HW 更新永远落后于 Leader 的 HW。这种时间上的错配是造成各种不一致的原因。

81.Hashmap 什么时候进行扩容呢?

当 hashmap 中的元素个数超过数组大小 loadFactor 时,就会进行数组扩容,
loadFactor 的默认值为 0.75,也就是说,默认情况下,数组大小为 16,那么当
hashmap 中元素个数超过 160.75=12 的时候,就把数组的大小扩展为 216=32,
即扩大一倍,然后重新计算每个元素在数组中的位置,而这是一个非常消耗性能的操
作,所以如果我们已经预知 hashmap 中元素的个数,那么预设元素的个数能够有效
的提高 hashmap 的性能。比如说,我们有 1000 个元素 new HashMap(1000),
但是理论上来讲 new HashMap(1024) 更合适,不过上面 annegu 已经说过,即使
是 1000,hashmap 也自动会将其设置为 1024。 但是 new HashMap(1024) 还
不是更合适的,因为 0.75*1000 < 1000, 也就是说为了让 0.75 * size > 1000, 我们
必须这样 new HashMap(2048) 才最合适,既考虑了 & 的问题,也避免了 resize
的问题。

82.如何自定义注解实现功能?

> 创建自定义注解和创建一个接口相似,但是注解的 interface 关键字需要以@符号开头。
> 注解方法不能带有参数;
> 注解方法返回值类型限定为:基本类型、String、Enums、Annotation 或者是这些类型的
数组;
> 注解方法可以有默认值;
> 注解本身能够包含元注解,元注解被用来注解其它注解。

83. 什么是数据库三范式?

第一范式:数据库表的每一个字段都是不可分割的。
第二范式:数据库表中的非主属性只依赖于主键。
第三范式:不存在非主属性对关键字的传递函数依赖关系。

84.假如给你一个新产品,你将从哪些方面来保障它的质量?

可以从代码开发、测试保障、线上质量三个方面来保障。

在代码开发阶段,有单元测试、代码Review、静态代码扫描等;

测试保障阶段,有功能测试、性能测试、高可用测试、稳定性测试、兼容性测试等;

在线上质量方面,有灰度发布、紧急回滚、故障演练、线上监控和巡检等。

85.Spring IOC 如何实现?

> Spring 中的 org.springframework.beans 包和 org.springframework.context 包构成了
Spring 框架 IoC 容器的基础。

> BeanFactory 接口提供了一个先进的配置机制,使得任何类型的对象的配置成为可能。
ApplicationContex 接口对 BeanFactory(是一个子接口)进行了扩展,在 BeanFactory 的
基础上添加了其他功能,比如与 Spring 的 AOP 更容易集成,也提供了处理 message
resource 的机制(用于国际化)、事件传播以及应用层的特别配置,比如针对 Web 应用的
WebApplicationContext。

> org.springframework.beans.factory.BeanFactory 是 Spring IoC 容器的具体实现,用
来包装和管理前面提到的各种 bean。BeanFactory 接口是 Spring IoC 容器的核心接口。

86.Spring AOP 实现原理是什么?

> Spring AOP 中的动态代理主要有两种方式,JDK 动态代理和 CGLIB 动态代理。JDK 动态代
理通过反射来接收被代理的类,并且要求被代理的类必须实现一个接口。JDK 动态代理的
核心是 InvocationHandler 接口和 Proxy 类。

> 如果目标类没有实现接口,那么 Spring AOP 会选择使用 CGLIB 来动态代理目标类。CGLIB
(Code Generation Library),是一个代码生成的类库,可以在运行时动态的生成某个类
的子类,注意,CGLIB 是通过继承的方式做的动态代理,因此如果某个类被标记为 final,
那么它是无法使用 CGLIB 做动态代理的。

87.Spring MVC 运行流程?

> 1.spring mvc 将所有的请求都提交给 DispatcherServlet,它会委托应用系统的其他模块
负责对请求 进行真正的处理工作。

> 2.DispatcherServlet 查询一个或多个 HandlerMapping,找到处理请求的 Controller.

> 3.DispatcherServlet 请请求提交到目标 Controller

> 4.Controller 进行业务逻辑处理后,会返回一个 ModelAndView

> 5.Dispathcher 查询一个或多个 ViewResolver 视图解析器,找到 ModelAndView 对象指定
的视图对象

> 6.视图对象负责渲染返回给客户端。

88.请简述常用的索引有哪些种类?

1. 普通索引: 即针对数据库表创建索引
2. 唯一索引: 与普通索引类似,不同的就是:MySQL 数据库索引列的值必须唯一,但允许有空值
3. 主键索引: 它是一种特殊的唯一索引,不允许有空值。一般是在建表的时候同时创建主键索引
4. 组合索引: 为了进一步榨取 MySQL 的效率,就要考虑建立组合索引。即将数据库表中的多个
   字段联合起来作为一个组合索引。

89. 如何将字符串反转?

使用 StringBuilder 或者 stringBuffer 的 reverse() 方法,如下:

// StringBuffer reverse
StringBuffer stringBuffer = new StringBuffer();
stringBuffer. append("abcdefg");
System. out. println(stringBuffer. reverse()); // gfedcba

// StringBuilder reverse
StringBuilder stringBuilder = new StringBuilder();
stringBuilder. append("abcdefg");
System. out. println(stringBuilder. reverse()); // gfedcba

90. try-catch-finally 中,如果 catch 中 return 了,finally 还会执行吗?

finally 一定会执行,即使是 catch 中 return 了,catch 中的 return 会等 finally 中的代码执行完之后,才会执行。

91.常见的异常类有哪些?

NullPointerException 空指针异常
ClassNotFoundException 指定类不存在
NumberFormatException 字符串转换为数字异常
IndexOutOfBoundsException 数组下标越界异常
ClassCastException 数据类型转换异常
FileNotFoundException 文件未找到异常
NoSuchMethodException 方法不存在异常
IOException IO 异常
SocketException Socket 异常

92. MyBatis 中 #{}和 ${}的区别是什么?

#{}是预编译处理,${}是字符替换。在使用 #{}时,MyBatis 会将 SQL 中的 #{}替换成“?”,配合 PreparedStatement 的 set 方法赋值,这样可以有效的防止 SQL 注入,保证程序的运行安全。

93. MyBatis 有几种分页方式?

分页方式:逻辑分页和物理分页。

「逻辑分页:」 使用 MyBatis 自带的 RowBounds 进行分页,它是一次性查询很多数据,然后在数据中再进行检索。

「物理分页:」 自己手写 SQL 分页或使用分页插件 PageHelper,去数据库查询指定条数的分页数据的形式。

94. MySQL 的内连接、左连接、右连接有什么区别?

内连接关键字:inner join;左连接:left join;右连接:right join。

内连接是把匹配的关联数据显示出来;左连接是左边的表全部显示出来,右边的表显示出符合条件的数据;
右连接正好相反。

95. String类为什么是final的?

String的值不可变aaa,修改值bbb后,string新开辟一个内存空间存储新值bbb,保证其他已经指向aaa的string不用改变,保证效率及安全,string的定义在stack中,值在heap中。

96. 如何衡量一个架构师应该具备的能力?

首先要有架构师的思维,对分布式、高并发、高性能、高可用、可扩展、松耦合、高内聚、可复用、系统边界、安全等方面有深刻的理解。

技术面要广,熟悉架构技术栈,比如:熟悉微服务,缓存,分布式消息中间件,分布式任务中间件,数据层中间件,分布式监控中间件,网关中间件,分布式配置中心等等,并不是所有的技术栈要非常精通,但重要的技术,- 定要掌握得非常深。

注重架构技术实践,这是开发童鞋非常缺失的。建议多和架构师多交流,多落地相关技术的实践,集中火力多实战成长会很快的。理论看100遍,不如实践-遍。

掌握好uml,提升个人系统分析、 系统架构、系统设计、画业务架构图、技术架构图、写架构方案等方面的能力。

从架构思维,架构技术栈,架构职责等角度写好一 份架构师的简历,重点突出个人掌握的架构技术栈,重点突出项目的架构亮点,难点。

在企业内部转架构,或者去别的企业转型架构。架构面试方面多实践,如果没经验,可以让架构师老司机们多模拟面试几轮。

97.业务架构师与基础架构师区别?

对于java程序猿而言,架构师分为业务架构师,基础架构师两大类,从高级开发转成业务架构师,难度小,出成绩快。业务架构和基础架构有70%是一样的,那就是都要求有架构能力,剩下的30%是业务架构要求熟练掌握业务,制定架构方案,架构落地,基础架构则是100%要求纯技术。

短期而言,看似基础架构更风光,其实不然。业务架构发展前景更好-些,因为35岁以后,拼的是综合能力,不再是纯架构能力。业务架构要求有更好的沟通能力,架构规划,架构落地能力,-定的行业业务背景,甚至管理能力,所以从业务架构更容易做到技术总监或cto。

如果一直 做基础架构,那么可能是首席架构师。-般的架构老司机是业务架构,基础架构通吃的,好就业,到什么山唱什么歌。

98.上亿数据如何大规模更新?

可以用分布式任务调度中间件的大任务分片来做,把上亿的数据分给多台机器来做。
如果实时性要求不高, 完全可以设置一定的时间间隔,减少DB压力;
如果实时要求高,数据层需要分库。如果每天增量数据较多,可以考虑周期性地做数据归档。

99. 为什么要使用Elasticsearch?

数据量非常大的时候,采用以往的模糊查询,模糊查询前置配置,会放弃索引,导致商品查询是全表扫面,在百万级别的数据库中,效率非常低下,而我们使用ES做一个 全文索引,我们将经常查询的商品的某些字段,比如说商品名,描述、价格还有id这些字段我 们放入我们索引库里,可以提高查询速度。

100.Elasticsearch索引文档是如何存储的?

ES协调节点默认使用文档ID参与计算(也支持通过routing),以便为路由提供合适的分片。 shard = hash(document_id) % (num_of_primary_shards) 当分片所在的节点接收到来自协调节点的请求后,会将请求写入到Memory Buffer,然后定时 (默认是每隔1秒)写入到Filesystem Cache,这个从Momery Buffer到Filesystem Cache的过程就叫做refresh; 当然在某些情况下,存在Momery Buffer和Filesystem Cache的数据可能会丢失,ES是通过 translog的机制来保证数据的可靠性的。其实现机制是接收到请求后,同时也会写入到 translog中,当Filesystem cache中的数据写入到磁盘中时,才会清除掉,这个过程叫做 flush; 在flush过程中,内存中的缓冲将被清除,内容被写入一个新段,段的fsync将创建一个新的提 交点,并将内容刷新到磁盘,旧的translog将被删除并开始一个新的translog。 flush触发的时机是定时触发(默认30分钟)或者translog变得太大(默认为512M)时;

101. Elasticsearch常用的调优手段有哪些?

设计阶段调优
(1)根据业务增量需求,采取基于日期模板创建索引,通过 roll over API 滚动索引;
(2)使用别名进行索引管理;
(3)每天凌晨定时对索引做 force_merge 操作,以释放空间;
(4)采取冷热分离机制,热数据存储到 SSD,提高检索效率;冷数据定期进行 shrink操作, 以缩减存储;
(5)采取 curator 进行索引的生命周期管理;
(6)仅针对需要分词的字段,合理的设置分词器;
(7)Mapping 阶段充分结合各个字段的属性,是否需要检索、是否需要存储等。

写入调优
(1)写入前副本数设置为 0;
(2)写入前关闭 refresh_interval 设置为-1,禁用刷新机制;
(3)写入过程中:采取 bulk 批量写入;
(4)写入后恢复副本数和刷新间隔;
(5)尽量使用自动生成的 id。

查询调优
(1)禁用 wildcard;
(2)禁用批量 terms(成百上千的场景);
(3)充分利用倒排索引机制,能 keyword 类型尽量 keyword;
(4)数据量大时候,可以先基于时间敲定索引再检索;
(5)设置合理的路由机制。

其他调优
部署调优,业务调优等。
上面的提及一部分,面试者就基本对你之前的实践或者运维经验有所评估了

102. 部署Elasticsearch时,系统调优有哪些?

1)64 GB 内存的机器是非常理想的, 但是 32 GB 和 16 GB 机器也是很常见的。少于 8 GB 会适得其反。

(2)如果你要在更快的 CPUs 和更多的核心之间选择,选择更多的核心更好。多个内核提供 的额外并发远胜过稍微快一点点的时钟频率。

(3)如果你负担得起 SSD,它将远远超出任何旋转介质。 基于 SSD 的节点,查询和索引性能 都有提升。如果你负担得起,SSD 是一个好的选择。

(4)即使数据中心们近在咫尺,也要避免集群跨越多个数据中心。绝对要避免集群跨越大的 地理距离。

(5)请确保运行你应用程序的 JVM 和服务器的 JVM 是完全一样的。 在Elasticsearch 的几 个地方,使用 Java 的本地序列化。

(6)通过设置 gateway.recover_after_nodes、gateway.expected_nodes、 gateway.recover_after_time 可以在集群重启的时候避免过多的分片交换,这可能会让数 据恢复从数个小时缩短为几秒钟。

(7)Elasticsearch 默认被配置为使用单播发现,以防止节点无意中加入集群。只有在同一台 机器上运行的节点才会自动组成集群。最好使用单播代替组播。

(8)不要随意修改垃圾回收器(CMS)和各个线程池的大小。

(9)把你的内存的(少于)一半给 Lucene(但不要超过 32 GB!),通过ES_HEAP_SIZE 环境变量设置。

(10)内存交换到磁盘对服务器性能来说是致命的。如果内存交换到磁盘上,一个100 微秒的 操作可能变成 10 毫秒。 再想想那么多 10 微秒的操作时延累加起来。 不难看出 swapping 对 于性能是多么可怕。

(11)Lucene 使用了大 量 的文件。同时,Elasticsearch 在节点和 HTTP 客户端之间进行通 信也使用了大量的套接字。 所有这一切都需要足够的文件描述符。你应该增加你的文件描述 符,设置一个很大的值,如 64,000.

103. Elasticsearch 在GC方面需要注意什么?

(1)倒排词典的索引需要常驻内存,无法 GC,需要监控 data node 上 segmentmemory 增长趋势。

(2)各类缓存,field cache, filter cache, indexing cache, bulk queue 等等,要设置合理 的大小,并且要应该根据最坏的情况来看 heap 是否够用,也就是各类缓存全部占满的时候, 还有 heap 空间可以分配给其他任务吗?避免采用 clear cache等“自欺欺人”的方式来释放内存。

(3)避免返回大量结果集的搜索与聚合。确实需要大量拉取数据的场景,可以采用scan &scroll api 来实现。

(4)cluster stats 驻留内存并无法水平扩展,超大规模集群可以考虑分拆成多个集群通过 tribe node 连接。

(5)想知道 heap 够不够,必须结合实际应用场景,并对集群的 heap 使用情况做持续的监控。

(6)根据监控数据理解内存需求,合理配置各类circuit breaker,将内存溢出风险降低到最低。

104. Elasticsearch常用的中文分词器有哪些?

分词服务开源/商业优势劣势
IK Analyzer开源简单易用,支持自定义词典和远程词典词库需要自行维护,不支持词性识别
结巴分词开源新词识别功能不支持词性识别
Ansj中文分词开源分词精准度不错,支持词性识别对标hanlp词库略少,学习成本高
HANLP开源目前词库最完善,支持的特性非常多需要更优的分词效果,学习成本高
百度NLP商业完善的商业搜索方案收费
阿里OpenSearch商业多年电商行业积累收付费

更多共享资料请参考:https://gitee.com/huangmike/itshare

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值