1.介绍一下springloc的情况,解决了什么问题?
2. String为什么设计为Final的,是为了解决什么问题?
3. String和String Buffer有什么区别?
4.介绍一下NIO,为了解决什么问题?
5. spring是怎么解决依赖循环的?
6. 介绍一下Class文件?
7. Java热部署是怎么做的?
8. 虚拟机的垃圾收集算法和常用的垃圾收集器
9. spring核心模块有哪些?
10. springaop有什么用?解决了什么问题?
11.mysql的隔离级别有哪些?
12. mysql常用的存储引擎有哪些?有什么区别?
13.mysql的binlog和redoLog有什么区别?
14.你能解释一下MySQL的MVCC吗?
16. es用过吗?倒排索引说一下?
17.数据库的数据有2亿数据,有50列,每天会加200万数据,为什么插入数据会慢?如何解决这个问题?
18. linux用的熟吗?连接到inux的命令,查询文件的命令,批量查询数据的命令,grep

1、String为什么设计为final的,是为了解决什么问题?

public final class String

final + private 修饰,保证 String 不可变。
String 类被 final 修饰导致其不能被继承,进而避免了子类破坏 String 可变。

首先,String是引用类型,也就是每个字符串都是一个String实例。通过源码可以看到String底层维护了一个byte数组:private final byte[] value;(JDK9中为byte数组,并非网上所说的char数组)。虽然该数组被修饰为final,但这并不能保证数组的数据不会变化,因此还需要声明为private防止被其他类修改数据。

Java 8
private final char value[];

Java 9
private final byte[] value;

关于 final:
被 final 关键字修饰的类不能被继承,被修饰的方法不能被重写,被修饰的变量是基本数据类型则值不能改变,被修饰的变量是引用类型则不能再指向其他对象。

2、String和String Buffer有什么区别?


线程安全性
String 中的对象是不可变的,也就可以理解为常量,线程安全。AbstractStringBuilder 是 StringBuilder 与 StringBuffer 的公共父类,定义了一些字符串的基本操作,如 expandCapacity、append、insert、indexOf 等公共方法。StringBuffer 对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。StringBuilder 并没有对方法进行加同步锁,所以是非线程安全的。
性能
每次对 String 类型进行改变的时候,都会生成一个新的 String 对象,然后将指针指向新的 String 对象。StringBuffer 每次都会对 StringBuffer 对象本身进行操作,而不是生成新的对象并改变对象引用。相同情况下使用 StringBuilder 相比使用 StringBuffer 仅能获得 10%~15% 左右的性能提升,但却要冒多线程不安全的风险。
对于三者使用的总结:
操作少量的数据: 适用 String
单线程操作字符串缓冲区下操作大量数据: 适用 StringBuilder
多线程操作字符串缓冲区下操作大量数据: 适用 StringBuffer

3.介绍一下NIO,为了解决什么问题?


非阻塞 IO,解决 BIO 的线程阻塞问题。
BIO 会对客户端每次来的请求都创建一个线程来服务这个客户端的,如果请求很多,那么创建的线程就很多,然后线程处理的速度还不够快,那么服务端程序的负载过高,最后导致系统崩溃。

NIO 就是同步非阻塞的,客户端和server 通信时,会注册 一个 channel 到 selector 上面,然后selector 的一个线程会轮询所有 channel。如果 channel 上有读写事件发生,selector 就通过 selectorKey 获取这些 channel,然后启动一个工作线程去处理数据,客户端的请求都不会阻塞。

有一个线程池,会分配工作线程去获取数据,然后把数据写入到 buffer 中,channel 就会读取到数据了,然后返回数据给客户端。
image.png
NIO 适合非常多请求的的场景。

参考文章:https://www.yuque.com/wukong-9bwbm/rkks9o/vtuqi6

4、介绍一下springloc的情况,解决了什么问题?


IOC 翻译过来就是控制反转,对象的创建、配置和生命周期,都交给 Spring 容器来管理,且不需要人工来管理对象之间的复杂关系。
好处:解耦。
Spring 提供了两种 IOC 容器:BeanFactory 和 ApplicationContext。

5、spring是怎么解决依赖循环的?

Spring 提供了三种依赖注入方式:字段注入、构造器注入、Setter 方式注入。只有 Setter 方式能够解决循环依赖。
参考资料:https://time.geekbang.org/dailylesson/detail/100075768image.png
自己说一遍:
用了三级缓存,
第一级缓存存放实例化且初始化完成的 bean
第二级缓存存放实例化完成,但是未初始化完成的对象。
第三级缓存放对象工厂,用于创建二级缓存中的对象

A 依赖 B,B 依赖 A

1.创建对象 A,实例化 A 的时候,提前暴露到三级缓存中
2.注入 A 的属性时,发现依赖 B,转而去实例化 B。
3.B 初始化的时候,发现依赖 A,就从第一级缓存中找,发现没有,再从第二级缓存中找,发现也没有,再从第三级缓存中找,因为 A 已经提前暴露到 第三级缓存中了,B 就可以拿到 A 了,完成所有的初始化流程。
4.B 创建完成后,放到第一级缓存中,A 可以获取到 B 的实例,完成 A 的所有初始化流程。

为什么不能用两级缓存?

6、Spring核心模块有哪些?

Spring Core
提供 Spring 框架的基本功能,Spring 以 Bean 的方式组合和管理 Java 应用中的各个组件及其关系。
SpringFactory 来创建 Bean 和管理 Bean。

Spring Context
Spring 上下文

Spring AOP
面向切面编程,对方法进行增强。

Spring DAO
封装了操作 Database 的步骤

Spring ORM
ORM 对象的关系工具

Spring Web
为基于 Web 的应用程序提供了上下文。

Spring Web MVC
Web MVC 框架

扩展阅读:
84 | 开源实战四(上):剖析Spring框架中蕴含的经典设计思想或原则
https://time.geekbang.org/column/article/236935

7、 Spring AOP 有什么用?解决了什么问题?

Java OOP 存在局限性:

  • 静态化语言:类结构一旦定义,不容易被修改
  • 侵入性扩展:通过继承和组合组织新的类结构

AOP 可以方便的修改类和对类的结构进行增强。

8、介绍一下Class文件?

9、Java热部署是怎么做的?

https://www.cnblogs.com/pfxiong/p/4070462.html

10、虚拟机的垃圾收集算法和常用的垃圾收集器

垃圾收集算法

  1. 标记清除

分为两个阶段,标记阶段和清除阶段。先标记出垃圾对象,再将垃圾清除。缺点会产生碎片。

  1. 复制算法

将新生代划分为一个eden区和两个survivor区,默认是eden:survivor0:survivor1=8:1:1,对象首先会先分配在eden区,然后标记垃圾对象之后,将存活对象从eden区复制到survivor0,第二次则将eden区的存活对象和survivor0的对象复制到survivor1中。

  1. 标记清除整理

相当于是标记清除算法的一种优化,因为清除之后会产生碎片,如果不进行整理会造成空间的一种浪费。

另外,收集算法底层实现是三色标记算法(具体的算法实现过程可以上网了解哈)

  • 黑色:对象已经被垃圾收集器访问过,并且这个对象的所有引用都已经被扫描过。
  • 灰色:对象已经被垃圾收集器访问过,但这个对象上至少存在一个引用还没有被扫描。
  • 白色:对象没有被垃圾收集器访问过。

    垃圾收集器

  1. SerialGC-串行垃圾收集器

串行垃圾收集器的垃圾处理方式是在做垃圾回收时,采用单线程的方式来进行垃圾回收处理,在年轻代使用标记-复制的算法,在老年代使用标记-清除-整理的算法,由于是单线程的,不能并行的进行,所以年轻代产生的 GC 和老年代产生的 GC 都会触发 STW,停止所有业务线程。

  1. ParallelGC-并行垃圾收集器

并行垃圾收集器的垃圾处理方式是在做垃圾回收时,采用多线程的方式并行地进行垃圾回收处理,在年轻代使用标记-复制的算法,在老年代使用标记-清除-整理的算法,年轻代产生的 GC 和老年代产生的 GC 也都会触发 STW,停止所有业务线程,但由于是多线程并行进行 GC,所以 GC 的暂停时间会相对应的减少。

  1. CMSGC-CMS 垃圾收集器

CMS 垃圾收集器全称是 Mostly Concurrent Mark And Sweep Garbage Collector,即最大并发标记与清除垃圾收集器,它在年轻代使用并行 STW 方式的标记-复制算法,在老年代使用并发标记-清除算法,与串行和并行垃圾收集器不大一样,它对老年代并没有进行整理,在处理老年代 GC 的时候 GC 线程是跟业务线程一起并发执行的,默认情况下,CMS 使用的并发线程数是 CPU 核心数的四分之一。
CMSGC 的过程有 7 个阶段:
1.初始标记阶段(发生 STW)
2.并发标记阶段
3.并发预清理阶段
4.可取消并发预清理阶段
5.最终标记阶段(发生 STW)
6.并发清除阶段
7.并发重置阶段

  1. G1GC-G1 垃圾收集器

G1 垃圾收集器全称是 Garbage First,即垃圾优先,G1GC 是将堆内存划分成多个小块,默认是 2048 块,每个小块不是固定定义成某个区域的,它是不固定变化的,有时是 eden 区,有时是 survivor 区,有时是 old 区。在并发阶段 G1GC 会计算每个小块的对象存活数量,垃圾最多的小块会被优先收集,G1GC 是以增量的处理方式来进行垃圾收集的。

  1. ZGC

ZGC是比较新的一款垃圾收集器,它其实与CMS中的ParNew和G1类似,ZGC也采用标记-复制算法,当时做了比较大的优化,比如ZGC在标记、转移和重定位阶段几乎都是并发进行的,一般都说ZGC停顿时间目标小于10ms,最主要的就是并发优化。

11、mysql 的隔离级别有哪些?

读未提交(read uncommitted)是指,一个事务还没提交时,它做的变更就能被别的事务看到。
读提交(read committed)是指,一个事务提交之后,它做的变更才会被其他事务看到。
可重复读(repeatable read)是指,一个事务执行过程中看到的数据,总是跟这个事务在启动时看到的数据是一致的。当然在可重复读隔离级别下,未提交变更对其他事务也是不可见的。
串行化(serializable ),顾名思义是对于同一行记录,“写”会加“写锁”,“读”会加“读锁”。当出现读写锁冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行。
image.png

12、mysql常用的存储引擎有哪些?有什么区别?

目前最常用的是InnoDB,其次为MyISAM,还有Memory,Merge,Archive等等。
这边说一下InnoDB和MyISAM的主要差异:

  1. 最主要的区别,也是造成现在InnoDB体量大于MyISAM的主要原因:InnoDB支持事务,而MyISAM不支持。
  2. 对于锁的支持:InnoDB中最细粒度的锁到行锁的级别,而MyISAM只支持表锁。
  3. 从数据结构的角度:InnoDB为聚簇索引,而MyISAM为非聚簇索引。
  4. 从主键角度:MyISAM允许不定义主键,InnoDB必存在主键。

    13、mysql的binlog和redoLog有什么区别?

    binlog 在 server 层,追加写
    redolog 在引擎层,循环写
    binlog与redolog配合可以让mysql拥有crashsafe的能力,通过两阶段提交,来保证两份日志的数据一致性。
    深入聊一下:关于binlog为什么不能保证crashsafe,是因为binlog是人为可控的开启或关闭,mysql的crashsafe能力,不能因为管理员的操作而发生改变。所以binlog一般不作为crashsafe的手段。

    14、你能解释一下MySQL的MVCC吗?

    引入undo日志、roll_pointer、trx_id三个概念:
    undo日志:事务回滚时恢复数据到未变更状态,每次执行增、删、改操作时都会记录变更前的原始数据到undo日志;
    roll_pointer属性:表记录的隐藏字段;
    trx_id属性:表记录隐藏字段表示事务ID;
    设想一个场景数据库,两个不同用户分别读取和修改同一条数据,为了保证数据的正确性;数据库需要引入读锁、写锁,在读数据加读锁禁止写操作,写数据加写锁禁止读操作,这么做会影响数据性能;
    MVCC:引入版本链机制解决读写并发访问问题,由于每次对数据进行增、删、改操作都会备份数据到undo中,因此如果某个记录被多次修改会存在多个版本数据,这些版本信息之间通过roll_pointer属性连成一个链表;在RR级别下,数据记录具有可重复读特性,即在同一个事务执行的过程中对某条数据执行查询(快照读)操作,然后间隔几秒钟继续查询(快照读)数据记录不会发生变化,哪怕在间隔的几秒钟有其它事务对该记录进行了修改操作;

15、redolog持久化到磁盘的时机是什么,是否有可能将未提交的事务持久化到磁盘,这种情况有必要避免吗?

①持久化的时机
参数innodb_flush_log_at_trx_commit 控制:
设置为 0 的时候,表示每次事务提交时都只是把 redo log 留在 redo log buffer 中(只写内存,会存在数据丢失的情况,风险高,写入快) ;
设置为 1 的时候,表示每次事务提交时都将 redo log 直接持久化到磁盘(直接持久化到磁盘,不存在数据丢失,风险低,写入慢);
设置为 2 的时候,表示每次事务提交时都只是把 redo log 写到 page cache(写入系统缓存中,MySQL宕机不会丢失数据,但是主机宕机会丢失数据,风险相对低,写入快);
②可能会将未提交的事务持久化到磁盘
情况一:InnoDB 有一个后台线程,每隔 1 秒,就会把 redo log buffer 中的日志,调用 write 写到文件系统的 page cache,然后调用 fsync 持久化到磁盘。事务执行中间过程的 redo log 也是直接写在 redo log buffer 中的,这些 redo log 也会被后台线程一起持久化到磁盘。也就是说,一个没有提交的事务的 redo log,也是可能已经持久化到磁盘的;
情况二:一种是,redo log buffer 占用的空间即将达到 innodb_log_buffer_size 一半的时候,后台线程会主动写盘。由于这个事务并没有提交,所以这个写盘动作只是 write,而没有调用 fsync,也就是只留在了文件系统的 page cache;
情况三:另一种是,并行的事务提交的时候,顺带将这个事务的 redo log buffer 持久化到磁盘;
③这种情况有必要避免嘛
我认为是不需要的,如果未提交的时候先持久化到磁盘了,如果事务回滚了,应该会有对应的处理删除掉对应持久化的数据;如果事务正常提交了,那就没什么影响了

16、ES 用过吗?倒排索引说一下?

image.png

17、数据库的数据有2亿数据,有50列,每天会加200万数据,为什么插入数据会慢?如何解决这个问题?

从插入速度的角度来说,应该是没有利用到ChangeBuffer。造成的原因为50列中应该有唯一索引,每次插入都需要确定值的唯一性,导致了性能降低。要解决的话,肯定是从这方面入手,即使不能使用到ChangeBuffer,那也要减少唯一索引的数量,以减少确定唯一性所消耗的性能,可以考虑在代码层面确定唯一性,比如利用布隆过滤器等手段确定唯一性,这样效率肯定可以提升了。

18、linux用的熟吗?连接到inux的命令,查询文件的命令,批量查询数据的命令,grep

ssh username@ip,ll -ah,find这些都比较基础,作为开发人员常用命令有且不限于:

  1. lsof,查询端口占用
  2. kill,杀进程
  3. docker相关,容器技术的使用
  4. lrzsz,上传下载文件
  5. tar,unzip 解压缩
  6. tail,查看日志
  7. chmod,修改权限
  8. top, 内存占用
  9. vmstat,系统监控
  10. df -h,磁盘占用

扩展题

1、Spring 的生命周期
image.png
Spring 的生命周期和作用域
https://time.geekbang.org/column/article/12472