0%

高频面试题

Java

hashmap底层原理(数据结构、为什么用红黑树等)、

  • JDK1.8之前:

  • 链表数组、拉链法

  • 扰动函数:增加低位的随机性(原哈希值右移16位然后与原哈希值异或得到新哈希值),解决取模后索引位置的碰撞问题

  • 数组长度:n=2^m,取模操作hash%n等价于(n-1)&hash

  • JDK1.8之后:链表长度过长会自动转成红黑树,减少搜索时间

ArrayList与LinkedList区别

Arraylist 底层使用的是 Object 数组;LinkedList 底层使用的是双向链表数据结构

Jvm内存模型

img

img

线程私有

  • 程序计数器:指向下一条需要执行的字节码指令

  • 虚拟机栈:Java 方法执行的内存模型,每次Java方法调用的数据都是通过虚拟机栈传递的。

  • 本地方法栈:Native方法执行的内存模型,每次Native方法调用的数据都是通过本地方法机栈传递的。

线程共享

  • 堆:存放对象实例

  • 元空间【方法区】:存放常量、类信息、静态变量、JIT编译后的代码

  • 直接内存:NIO中的基础

Jvm垃圾回收算法

  • 回收区域:堆是需要回收的核心区域

    img

    大部分情况,对象都会首先在 Eden 区域分配,在一次新生代垃圾回收后,如果对象还存活,则会进入 s0 或者 s1,并且对象的年龄还会加 1,当它的年龄增加到一定程度,就会被晋升到老年代中。

  • 判断可回收:可达性分析,GCROOTS:类静态属性、常量、虚拟机栈中、本地方法栈中 引用的对象

  • 垃圾收集算法:

    • 标记清除【内存碎片问题】、标记整理【效率低】:用于老年代
    • 标记复制:用于新生代,在回收时,将 Eden 和 Survivor 中还存活着的对象全部复制到另一块 Survivor 上,最后清理 Eden 和使用过的那一块 Survivor。

volatile关键字的原理与作用

  • 作用:防止指令重排、保证内存可见性【不保证原子性:i++由多个原子操作组成】
  • 原理:禁止指令重排,在写操作后加入屏障指令,将本地内存中的共享变量值刷新到主内存,在读操作前加入屏障指令,从主内存中读取共享变量。

Java集合类(线程安全/不安全)

  • StringBuilder是线程不安全的,而StringBuffer是线程安全的。
  • ArrayList线程不安全,CopyOnWriteArrayList线程安全
  • HashMap线程不安全,ConcurrentHashMap线程安全
  • 并发队列:ConcurrentLinkedQueue、LinkedBlockingQueue、ArrayBlockingQueue、PriorityBlockingQueue、DelayQueue

【@】Synchronized和Lock的实现原理与区别

ConcurrentHashMap原理,如何保证线程安全

1.7

Java 7 ConcurrentHashMap 存储结构

1.8

1.8抛弃了原有的 Segment 分段锁,而采用了 CAS + synchronized 来保证并发安全性。

Java线程池核心参数与工作流程,拒绝策略

这里写图片描述

线程池 | 冰冻橘子的自留地 (onebug.tech)

  • corepoolqueuemaximumpoolreject四级处理

    keepAliveTime 用于配置闲置线程最长存活时间,直到收缩到 corePoolSize 的大小

  • reject有:抛出异常(abort)、由上层线程执行(callruns)、删除该任务(discard)、删除老任务(discardoldest)

【@】类加载机制

img

加载:获取二进制字节流=>生成方法区运行时数据结构=>生成class对象作为方法区数据入口

连接:验证、准备【初始化默认值】、解析【符号引用替换为直接引用,也就是得到类或者字段、方法在内存中的指针或者偏移量】

初始化:初始化方法 <clinit> ()

【@】SpringAOP的底层原理

Spring AOP 就是基于动态代理的,如果要代理的对象,实现了某个接口,那么 Spring AOP 会使用 JDK Proxy,去创建代理对象,而对于没有实现接口的对象,就无法使用 JDK Proxy 去进行代理了,这时候 Spring AOP 会使用 Cglib 生成一个被代理对象的子类来作为代理,如下图所示:

动态代理:设计模式是代理模式【使用代理对象来代替对真实对象的访问,这样就可以在不修改原目标对象的前提下,提供额外的功能操作,扩展目标对象的功能】。本质上是通过类加载,然后修改类字节码,重新加载到JVM中,具体是通过反射完成。

Understanding the Proxy Design Pattern | by Mithun Sasidharan | Medium

SpringAOPProcess

数据库Mysql

Mysql的几种存储引擎

  • MyISAM 只有表级锁(table-level locking),而 InnoDB 支持行级锁(row-level locking)和表级锁,默认为行级锁。MyISAM 不提供事务支持。

  • InnoDB 提供事务支持,具有提交(commit)和回滚(rollback)事务的能力。

  • MyISAM 不支持,而 InnoDB 支持。

Mysql的聚簇索引和非聚簇索引作用与区别

聚簇索引就是按照每张表的主键构造一颗B+树,同时叶子节点中存放的就是整张表的行记录数据,也将聚集索引的叶子节点称为数据页。这个特性决定了索引组织表中数据也是索引的一部分,每张表只能拥有一个聚簇索引。

聚簇索引之上创建的索引称之为辅助索引,辅助索引访问数据总是需要二次查找。辅助索引叶子节点存储的不再是行的物理位置,而是主键值。通过辅助索引首先找到的是主键值,再通过主键值找到数据行的数据页,再通过数据页中的Page Directory找到数据行。

索引的数据结构对比(hash、B树与B+树),为什么不用红黑树

  • hash表:不支持范围查找,一般不使用

  • 红黑树的弊端:红黑树是二叉树!红黑树一个节点只能存出一个值,在大规模数据存储的时候,红黑树往往出现由于树的深度过大而造成磁盘IO读写过于频繁,进而导致效率低下的情况。

  • B树(多路平衡查找树):B树是一种自平衡的多叉搜索树,一个节点可以拥有两个以上的子节点,节点中的数据索引从左到右递增排列。这种数据结构能够让查找数据、顺序访问、插入数据及删除的动作,都在O(logn)时间内完成。

  • B+树:

    img

  • B树和B+树的区别:B 树的叶子节点都是独立的,B+树的叶子节点有一条引用链指向与它相邻的叶子节点。B+树只在叶子节点存储数据,查找速度稳定。

Mysql的默认隔离级别、不同等级隔离级别解决的问题与实现原理

1
SELECT @@transaction_isolation;
隔离级别 脏读 不可重复读 幻读
READ-UNCOMMITTED
READ-COMMITTED ×
REPEATABLE-READ【默认隔离级别】 × ×
SERIALIZABLE × × ×
  • 脏读:在一个事务内读取到另一个事务中修改但未提交的数据。

  • 不可重复读:在一个事务内读取到另一个事务中修改后提交的数据。

  • 幻读:在一个事务内插入时遇到到另一个事务中插入后提交的数据。

    不可重复读侧重表达 读-读,幻读则是说 读-写,用写来证实读的是鬼影。幻读,并不是说两次读取获取的结果集不同,幻读侧重的方面是某一次的 select 操作得到的结果所表征的数据状态无法支撑后续的业务操作。更为具体一些:select 某记录是否存在,不存在,准备插入此记录,但执行 insert 时发现此记录已存在,无法插入,此时就发生了幻读。

    解决方法:RR级别下只要对 SELECT 操作也手动加行(X)锁即可类似 SERIALIZABLE 级别。

    1
    2
    #FOR UPDATE 也会对此 “记录” 加锁
    SELECT `id` FROM `users` WHERE `id` = 1 FOR UPDATE;

【TODO】Mvcc实现机制(RC和RR隔离级别下的区别)

InnoDB存储引擎对MVCC的实现 | JavaGuide

Mysql事务及特性

事务是指逻辑上的一组操作,组成这组操作的各个单元,要不全成功要不全失败。

Mysql sql优化,慢Sql如何排查

mysql优化之 — 慢SQL分析 - 雪山上的蒲公英 - 博客园 (cnblogs.com)

索引失效的几种场景

索引失效的7种情况 - liehen2046 - 博客园 (cnblogs.com)

【@】数据库Redis

Redis基本数据类型

String、List、Set、Hash、SortedSet、BitMap

Redis底层数据结构

除了动态字符串、链表、字典【包含两个哈希表数,方便渐进式rehash】、还有维持有序性的跳表、维持不重复性的整数集合、节约内存的压缩列表

img

Redis持久化方式,RDB和AOF的区别与优劣势

RDB(Redis Database):将某个时间点的所有数据都存放到硬盘上。如果数据量很大,保存快照的时间会很长,由于是隔一段时间触发一次,实时性不是很好,系统崩溃后丢失的数据可能会很多。

AOF(Append Only File):每执行一条会更改 Redis 中的数据的命令,Redis 就会将该命令写入到内存缓存 中,然后再根据 appendfsync 配置来决定何时将其同步到硬盘中的 AOF 文件。缺点是随着服务器写请求的增多,AOF 文件会越来越大。对此,Redis 提供了一种将 AOF 重写的特性,能够去除 AOF 文件中的冗余写命令。

Redis如何实现分布式锁?

可以使用 Redis 自带的 SETNX 命令实现分布式锁:

  • 使用 SETNX(set if not exist)指令插入一个键值对,如果 Key 已经存在,那么会返回 False,否则插入成功并返回 True。【和CAS一样】

  • SETNX 指令和数据库的唯一索引类似,保证了只存在一个 Key 的键值对,那么可以用一个 Key 的键值对是否存在来判断是否存于锁定状态。

  • EXPIRE 指令可以为一个键值对设置一个过期时间,从而避免了数据库唯一索引实现方式中释放锁失败的问题。

除此之外,还可以使用官方提供的 RedLock 分布式锁实现:

  • 尝试从 N 个互相独立 Redis 实例获取锁;

    使用了多个 Redis 实例来实现分布式锁,这是为了保证在发生单点故障时仍然可用。

  • 计算获取锁消耗的时间,只有时间小于锁的过期时间,并且从大多数(N / 2 + 1)实例上获取了锁,才认为获取锁成功;

  • 如果获取锁失败,就到每个实例上释放锁

计算机网络

Tcp与Udp区别

TCP、UDP协议的区别

Tcp如何保证可靠传输

TCP 使用超时重传来实现可靠传输:如果一个已经发送的报文段在超时时间内没有收到确认,那么就重传这个报文段。

浏览器上输入地址后的整个请求过程

  1. 浏览器缓存直接返回,否则
  2. DNS解析域名得到IP:浏览器缓存、hosts文件、本地域名服务器
  3. TCP连接:三次握手
  4. 发送HTTP请求(请求方法 请求头 cookie user-agent 等等 请求内容 表单等 ),获取HTTP响应(js、css、html、媒体文件) 【Keep-Alive 复用、TCP四次挥手】,浏览器渲染显示

OSI七层、五层模型,每一层的作用

osi七层模型

【@】TCP握手挥手过程及【状态变化】

img

img

Tcp流量控制与拥塞控制

  • 流量控制是为了控制发送方发送速率,保证接收方来得及接收。拥塞控制是为了降低整个网络的拥塞程度。

  • 接收方发送的确认报文中的窗口字段可以用来控制发送方窗口大小。TCP 主要通过四个算法来进行拥塞控制:慢开始、拥塞避免、快重传、快恢复。

    img

Http、Https、两者区别

HTTPS 是基于 HTTP 的,也是用 TCP 作为底层协议,并额外使用 SSL/TLS 协议用作加密和安全认证。默认端口号是 443,HTTP默认端口号是80。HTTP 安全性没有 HTTPS 高,但是 HTTPS 比 HTTP 耗费更多服务器资源。

操作系统

进程与线程的区别

线程是调度的基本单位,进程是资源管理的基本单位。一般来说,进程之间是独立的,一个进程下会有若干个线程,然后这些线程共享进程资源,并极有可能相互影响。

补充协程 - 廖雪峰的官方网站 (liaoxuefeng.com)

【@】进程之间通信方式

  1. 管道/匿名管道(Pipes) :用于具有亲缘关系的父子进程间或者兄弟进程之间的通信。
  2. 有名管道(Names Pipes) : 匿名管道由于没有名字,只能用于亲缘关系的进程间通信。为了克服这个缺点,提出了有名管道。有名管道严格遵循**先进先出(first in first out)**。有名管道以磁盘文件的方式存在,可以实现本机任意两个进程通信。【ls | grep xxx
  3. 信号(Signal) :信号是一种比较复杂的通信方式,用于通知接收进程某个事件;【kill -9 pid
  4. 消息队列(Message Queuing) :消息队列是消息的链表,消息队列有以下优点:可以独立于读写进程存在,不需要进程自己提供同步方法,读进程可以根据消息类型有选择地接收消息,而不像 FIFO 那样只能默认地接收。
  5. 信号量(Semaphores) :信号量是一个计数器。信号量的意图在于进程间同步。这种通信方式主要用于解决与同步相关的问题并避免竞争条件。
  6. 共享内存(Shared memory) :多个进程可以访问和更新同一块内存空间。这种方式需要依靠某种同步操作,如互斥锁和信号量等。可以说这是最快的进程间通信方式。
  7. 套接字(Sockets) : 套接字是支持 TCP/IP 的网络通信的基本操作单元,可以看做是不同主机之间的进程进行双向通信的端点。

CAS操作原理和实现

CAS (compareAndSwap),中文叫比较交换,是一种无锁原子算法,映射到操作系统就是一条CPU的原子指令,其作用是让CPU先进行比较两个值是否相等,然后原子地更新某个位置的值,其实现方式是基于硬件平台的汇编指令。CAS 是实现自旋锁的基础,CAS 利用 CPU 指令保证了操作的原子性,以达到锁的效果,至于自旋呢,看字面意思也很明白,自己旋转,翻译成人话就是循环,一般是用一个无限循环实现。这样一来,一个无限循环中,执行一个 CAS 操作,当操作成功,返回 true 时,循环结束;当返回 false 时,接着执行循环,继续尝试 CAS 操作,直到返回 true。

死锁的产生条件与解决方案

产生条件:

  • 互斥
  • 不可抢占
  • 申请等待

解决:

  • 鸵鸟
  • 死锁检测【有向图环形检测、银行家算法】与死锁恢复【抢占、回滚、kill】
  • 死锁预防:破坏条件
  • 死锁避免:银行家算法

软件工程

常见的设计模式与优缺点