Java八股文(下)
Java八股文下篇
- 八、JVM高级篇
- 1、JVM的内存模型以及分区介绍一下?
- 2、四种引用方式有什么?
- 3、判断是否为垃圾算法?
- 4、垃圾回收算法介绍一下?
- 5、类的生命周期以及类加载过程
- 6、加载器种类有什么?
- 7、什么是双亲委派模型以及作用是什么?
- 8、说一说类加载机制?
- 8.1、加载过程
- 8.2、类加载器(ClassLoader)
- 9、Java 垃圾回收机制
- 10、常见的JVM调优方法
- 九、设计模式篇
- 1、什么是设计模式,有何作用?
- 2、设计模式有多少种?,主要分为哪几类?
- 3、什么是单例模式,实现方法有什么,以及对应的应用场景?
- 4、工厂模式是什么?
- 5、代理模式简单介绍一下?
- 6、什么是策略模型,请详细说说?
- 十、消息队列篇(主要以RabbitMQ来说)
- 1、什么十消息队列?
- 2、常见的消息队列模型有哪些?
- 3、消息队列有哪些核心中间件?
- 4、RabbiMQ的交换机有哪几种类型?
- 5、如何确保消息不会丢失?
- 6、什么是RabbitMQ中的私信队列(DLX)?
- 7、成为死信队列条件?
- 8、如何实现延迟消息?
- 9、什么是RabbitMQ的虚拟主机?有何作用?
- 10、如何在 RabbitMQ 中实现消息幂等性?
- 11、RabbitMQ 的高可用集群模式有哪些?如何实现?
- 12、RabbitMQ 集群中,节点间如何同步数据?
- 十一、Docker篇
- 1、什么Docker
- 2、什么是Docker镜像
- 3、什么是Docker容器
- 4、Docker容器有几种状态
- 5、Docker的常用命令?
- 6、docker怎么做到容器隔离的?
- 1、命名空间(Namespaces)
- 2、控制组(Cgroups)
- 十二、Linux常用命令篇
- 一、文件和命令
- 1、cd 命令
- 2、pwd 命令
- 3、ls 命令
- 4、cp 命令
- 5、mv 命令
- 6、rm 命令
- 7、cat 命令
- 二、文件搜索
- 1、find 命令
- 三、文件的权限 - 使用 “+” 设置权限,使用 “-” 用于取消
- 1、chmod 命令
- 2、chown 命令
- 四、文本处理
- 1、grep 命令
- 五、打包和压缩文件
- 六、进程相关的命令
- 1、ps 命令
- 2、kill 命令
- 七、启动(关闭,重启,查看)某个服务
- 设置开机启动或者关闭某个服务
- 十三、git常用命令
- 十四、操作系统
- 1、什么情况下会发生死锁?
- 2、进程和线程的区别是什么?
- 3、进程的通信方式有哪些?
- 十五、常见Java面试场景题
- 1、从网络角度来看,用户从输入网址到网页显示,期间发生了什么?
- 2、分布式锁的实现?
- 3、分布式锁有效性的保证
- 4、生产环境遇到慢sql应该怎么解决?
- 5、如果线上运行的Java程序导致cpu服务器突然爆满,可能哪里出现问题,应该用什么方法进行检测?
- 十六、架构及设计题
- 1、在Java中什么是CAP?
- 2、什么是微服务?
- 3、什么是网关?
八、JVM高级篇
1、JVM的内存模型以及分区介绍一下?
JVM的内存主要分为几个运行时数据区:程序计数器、Java虚拟机栈、本地方法栈、Java堆、方法区。
一、程序计数器
用来记录当前线程执行的字节码指令地址,线程私有,每个线程都有一个独立的程序计数器。
二、Java虚拟机栈
Java 虚拟机栈是描述 Java 方法运行过程的内存模型。线程私有,生命周期与线程相同,每个方法在执行时都会创建一个栈帧(Stack Frame),用于存放该方法的局部变量表、操作数栈、动态链接以及返回地址等信息
三、本地方法栈
为使用到的本地操作系统方法服务。
四、Java堆
是所有线程共享的一块内存区域,用来存放所有的对象实例和数组。
五、方法区
主要用来存储虚拟机加载的类信息、常量、静态变量,常量池等
2、四种引用方式有什么?
- 强引用:类似new
- 软引用
- 弱引用
- 虚引用
3、判断是否为垃圾算法?
一、引用计数法
在对象头维护着一个 counter 计数器,对象被引用一次则计数器 +1;若引用失效则计数器 -1。当计数器为 0 时,就认为该对象无效了,相互引用时候容易出现内存泄漏,导致对象无法回收。
二、可达性分析法
所有和 GC Roots 直接或间接关联的对象都是有效对象,和 GC Roots 没有关联的对象就是无效对象。
GC Roots 是指:
- Java 虚拟机栈(栈帧中的本地变量表)中引用的对象
- 本地方法栈中引用的对象
- 方法区中常量引用的对象
- 方法区中类静态属性引用的对象
4、垃圾回收算法介绍一下?
一、标记-清除
利用可达性分析算法判断对象是否是存活对象,然后将带有标志的对象进行清除。
这种方法有两个不足:
- 效率问题:标记和清除两个过程的效率都不高。
- 空间问题:标记清除之后会产生大量不连续的内存碎片,碎片太多可能导致以后需要分配较大对象时,无法找到足够的连续内存而不得不提前触发另一次垃圾收集动作。
回收过程如图下:
二、复制算法
将内存平分。每次只使用其中的一块。当这一块内存用完,需要进行垃圾回收时,将存活的对象复制到另外一块内存去,然后将第一块内存全部清除。
优点:不会有内存碎片的问题。
缺点:内存缩小为原来的一半,浪费空间。
三、标记-整理算法(老年代)
标记步骤通标记-清除一样。整理:移动所有存活的对象,按照内存地址依次排列,最后将末端带有标记的对象进行回收。
四、分代回收算法
5、类的生命周期以及类加载过程
从加载到虚拟机开始,到卸载
- 加载:类加载器将 类的字节码文件(.class) 读取到内存,并通过 反射机制 创建一个 Class 对象表示该类。
- 验证:确保加载的字节码文件符合 JVM 的规范,验证文件是否被篡改,是否满足 Java 虚拟机的要求(如是否符合 Java 的语法规范,是否是合法的字节码)。
- 准备:在这一阶段,JVM 会为类的 静态变量 分配内存,并设置默认值(如 0、null 等)。
- 解析:将常量池中的符号引用(如类、方法、字段)替换为直接引用。
- 初始化:JVM 会执行类的 静态代码块 和 静态变量的初始化。
- 使用
- 卸载
其中类加载包括 5 个阶段:加载、验证、准备、解析和初始化。
6、加载器种类有什么?
- 启动类加载器
- 扩展类加载器
- 应用程序类加载器
- 自定义类加载器
7、什么是双亲委派模型以及作用是什么?
当一个类加载器收到类加载请求时,不会立即自己去尝试加载这个类,而是将这个请求委派给父类加载器去完成,每一层的类加载器都是这样,直到传送到最顶层的启动类加载器中。只有当父类加载器无法完成加载请求时候,子加载器才会尝试自己去加载。
作用
- 保证类的唯一性:保证同一个类在JVM中只会被加载依次,确保在整个应用中使用的是同一个类的对象。
- 保证安全性:避免重复加载类。例如,java.lang.Object 类只能由最顶层类加载器加载,从而防止恶意代码加载不受新人的类来代替系统核心类。
8、说一说类加载机制?
Java 的类加载机制是 JVM 负责将 .class 文件加载到内存,并转换为 Class 对象的过程。它主要包括 类的生命周期、类加载的过程、类加载器 和 双亲委派模型。
8.1、加载过程
-
加载(Loading)
-
任务:查找并加载类的二进制数据(通常是.class文件)。
-
过程:类加载器(ClassLoader)通过类的全限定名获取二进制数据,并生成一个代表该类的java.lang.Class对象。
-
-
验证(Verification)
-
任务:确保加载的类符合JVM规范,防止安全问题。
-
过程:检查字节码的正确性,如文件格式、元数据、字节码和符号引用等。
-
-
准备(Preparation)
-
任务:为类的静态变量分配内存并设置默认值。
-
过程:静态变量会被初始化为默认值(如0、null等),但不会执行任何代码或赋值。
-
-
解析(Resolution)
-
任务:将常量池中的符号引用转换为直接引用。
-
过程:解析类、接口、字段和方法的引用,确保它们正确指向内存中的实际地址。
-
-
初始化(Initialization)
-
任务:执行类的静态初始化代码。
-
过程:执行静态变量赋值和静态代码块(static {}),这是类加载的最后一步。
-
8.2、类加载器(ClassLoader)
类加载器负责加载类,主要有以下几种:
-
启动类加载器(Bootstrap ClassLoader):加载JVM核心类库(如java.lang.*),由C++实现,是JVM的一部分。
-
扩展类加载器(Extension ClassLoader):加载JAVA_HOME/lib/ext目录或java.ext.dirs指定路径的类库。
-
应用程序类加载器(Application ClassLoader):加载用户类路径(ClassPath)上的类库,是默认的类加载器。
-
自定义类加载器(Custom ClassLoader):用户可自定义类加载器,实现特定的加载需求。
然而对用的类加载器会采用双亲委派模型,加载类时先委托父类加载器尝试加载,只有在父类加载器无法加载时,子类加载器才会尝试。这种机制确保了类的唯一性和安全性。
9、Java 垃圾回收机制
1、Java 的垃圾回收(GC)机制是 JVM 自动管理内存的一部分,主要通过以下几个步骤工作:
-
标记-清除算法:标记所有存活的对象,清除没有被标记的对象。缺点是容易造成内存碎片。
-
复制算法:将内存分为两部分,活跃对象复制到另一部分,释放当前部分内存。
-
标记-整理算法:类似于标记-清除,但会整理内存,避免碎片。
-
分代收集:将对象分为年轻代、老年代和永久代,不同代有不同的垃圾回收策略。
2、什么时候进行垃圾回收
垃圾回收会在以下情况下发生:
-
内存不足:当堆内存不足时,JVM 会触发 GC,尝试回收无用的对象。
-
年轻代满:年轻代的内存满了时,会进行“Minor GC”回收。
-
老年代满:老年代满了时,会触发“Full GC”回收,进行更加复杂的回收过程。
10、常见的JVM调优方法
- 内存调优
堆内存设置:通过-Xms(初始堆大小)和-Xmx(最大堆大小)调整堆内存。例如:
java -Xms512m -Xmx2048m -jar myapp.jar
- 垃圾回收器选择
串行垃圾回收器:适用于单线程环境,使用-XX:+UseSerialGC
。
并行垃圾回收器:适用于多核CPU,使用-XX:+UseParallelGC。
CMS垃圾回收器:适用于低延迟应用,使用-XX:+UseConcMarkSweepGC。
G1垃圾回收器:适用于大内存、低延迟应用,使用-XX:+UseG1GC。
- GC日志分析
启用GC日志:使用-Xloggc:<file>
和-XX:+PrintGCDetails记录GC日志。
分析工具:使用工具如GCViewer
或GCEasy分析GC日志,找出性能瓶颈。
- 线程调优
线程栈大小:通过-Xss设置线程栈大小,例如-Xss512k
。
线程池配置:合理配置线程池大小,避免过多线程导致上下文切换开销。
- 类加载优化
类元数据区大小:通过-XX:MetaspaceSize
和-XX:MaxMetaspaceSize调整元数据区大小。
类加载器优化:避免频繁加载和卸载类,减少类加载器的开销。
- 性能监控工具
JVisualVM:监控JVM内存、线程、GC等。
JConsole:实时监控JVM状态。
Java Mission Control (JMC):深入分析JVM性能。
九、设计模式篇
1、什么是设计模式,有何作用?
设计模式是软件开发过程中经过经验积累和验证总结得出的一套通用代码设计方案,是踩在巨人的肩膀上总结的设计成果。
作用
- 提高代码可扩展性
- 可重用性
- 可维护性
2、设计模式有多少种?,主要分为哪几类?
共23种。
1、创建型模式
用于创建对象的模式,避免代码种出现大量new操作。目的是为了解耦对象的创建和使用。
常用的有:单例、工厂模式等。
2、结构型模式
用于处理对象组合的结构,关注类与对象的组合。
常用的有:代理、组合模式等。
3、行为型模式
用于定义类和对象将的通信方式。
常用的有:策略模式、模板模式等。
3、什么是单例模式,实现方法有什么,以及对应的应用场景?
单例模式是一种创建型设计模式,它确保一个类在整个运行程序过程种只有一个实例。
主要了解请前往:https://blog.csdn.net/a147775/article/details/141216173
4、工厂模式是什么?
工厂模式简而言之就是提供一个创建对象的工厂、地方。
主要了解请前往:https://blog.csdn.net/a147775/article/details/141187593
5、代理模式简单介绍一下?
代理模式是一种结构型设计模式,在不改变原始对象的前提下,通过引入一个代理对象来控制对原始对象的访问,实现额外的功能。
主要了解请前往:https://blog.csdn.net/a147775/article/details/141421373
6、什么是策略模型,请详细说说?
策略模式是一种行为设计模式,它可以在运行时改变对象的行为。策略模式定义了一系列算法或策略,并将每个算法封装在独立的类中,使得它们可以互相替换。策略模式主要解决多种相似算法存在时候,使用条件语句导致的复杂性和难以维护的问题。
主要了解请前往:https://blog.csdn.net/a147775/article/details/141595755
十、消息队列篇(主要以RabbitMQ来说)
1、什么十消息队列?
消息队列是一种异步通信机制,用于在分布式系统中解耦发送方和接受方之间的通信。它通过在消息生产者和消费者之间引入一个中间缓冲区,将消息存储在这个中间缓冲区中,然后由消费者从缓冲区中读取和处理消息。
常见用途
- 解耦
- 削封填谷:在高并发大量请求下,消息队列可以暂存大量的请求,平滑高峰流量,避免系统过载。
- 异步处理:可以将不需要立即处理的任务放入消息队列中异步执行,减少用户请求的响应时间。
2、常见的消息队列模型有哪些?
2.1 、点对点模型
每条消息只能被一个消费者消费一次。消费之后,消息在队列中被删除。
2.2、发布 / 订阅模型
生产者将消息发布到主题,所有订阅了主题的消费者都会接收到该消息,订阅者会接收到相同的信息,用于广播通知。
3、消息队列有哪些核心中间件?
- 生产者:发送消息到交换机
- 交换机:负责将消息路由到队列
- 队列:存储消息
- 消费者:消费消息
- 绑定:绑定交换机和队列
- 路由键:用于交换机到队列的路由规则
- 虚拟主机:逻辑分组,隔离不同应用的资源。
- 连接
- 信道
4、RabbiMQ的交换机有哪几种类型?
- Direct交换机,根据路由键精确进行绑定只有完全匹配的消息才会被转发到对应的队列。
- Fanout交换机会将接收到的每一条消息广播到所有绑定到它的队列,而不考虑路由键。
- Topic交换机会根据消息的路由键和绑定键的模式匹配来决定消息的流转路径。
5、如何确保消息不会丢失?
- 消息持久化
- 生产者确认
- 消费者确认
6、什么是RabbitMQ中的私信队列(DLX)?
死信队列是消息队列系统中处理无法正常消费的消息的一种机制,它主要用来接收那些被拒绝、过期、或已达到最大传递次数的消息。从而避免消息丢失便于进行错误处理和后续分析。
7、成为死信队列条件?
- 消息被拒绝
- 消息过期:如果消息设置了TTL,当消息超过了这个时间未被消费,就会成为死信。
- 队列长度限制
8、如何实现延迟消息?
通过私信交换机实现延迟消息,具体如下:将消息设置TTL过期,然后过期后路由到一个死信交换机,然后再从死信交换机重新发布以实现延迟发送。
9、什么是RabbitMQ的虚拟主机?有何作用?
它是一个逻辑上的隔离概念,用于隔离不同的应用或租户。每个虚拟主机可以拥有自己独立的队列、交换器、绑定、权限等资源。这样,多个相互独立的应用可以共存在一台RabbitMQ服务器上而不会互相影响。
10、如何在 RabbitMQ 中实现消息幂等性?
- 设置唯一的消息 ID
- 消费者在处理消息前检查这个消息ID是否已经处理过
- 如果消息ID 未被记录过,则处理消息并记录这个消息ID。
- 如果消息 ID 已经存在,直接跳过该消息。
11、RabbitMQ 的高可用集群模式有哪些?如何实现?
主要有两种集群模式:普通模式和镜像队列模式。
1)普通模式:所有节点共享数据,但消息存储在单节点上。如果存储该消息的节点挂掉,消息也会丢失。
2)镜像队列模式:每个队列在多个节点上有副本(镜像)。消息在主节点和其副本之间同步,在任何一个节点上接收到消息后,会同步到其他节点中,从而实现消息的高可用性。如果主节点挂掉,备用节点会提升为新主节点继续服务。
12、RabbitMQ 集群中,节点间如何同步数据?
节点间的数据同步主要是通过镜像队列实现的。镜像队列是 RabbitMQ 提供的一种机制。
十一、Docker篇
1、什么Docker
Docker是一个容器化平台,它以容器的形式将您的应用程序及其所有依赖项打包在一起,以确保您的应用程序在任何环境中无缝运行。
2、什么是Docker镜像
Docker镜像是Docker容器的源代码,Docker镜像用于创建容器。使用build命令创建镜像。
3、什么是Docker容器
Docker容器包括应用程序及其所有依赖项,作为操作系统的独立进程运行。
4、Docker容器有几种状态
四种状态:运行、已暂停、重新启动、已退出。
5、Docker的常用命令?
docker pull 拉去或更新指定的镜像
docker push 将镜像推送到远程仓库
docker rm 删除容器
docker rmi 删除镜像
docker images 列出所有镜像
启动nginx容器(随机端口映射),并挂载本地文件目录到容器html的命令?
// -d:后台运行 -p端口 --name:指定容器名称 -v 数据卷挂载
Docker run -d -p --name nginx2 -v /home/nginx:/usr/share/nginx/html nginx
查看正在运行容器列表
docker ps
查看所有容器 -----包含正在运行 和已停止的
docker ps -a
停止所有正在运行的容器
使用docker kill (list集合:每个容器)
清理批量后台停止的容器
使用docker rm (list集合:每个容器)
6、docker怎么做到容器隔离的?
Docker 的容器隔离主要依赖于 Linux 内核的 命名空间(Namespaces) 和 控制组(Cgroups) 技术,这些技术可以确保容器彼此之间、以及容器与宿主机之间的资源和操作系统环境的隔离。
1、命名空间(Namespaces)
命名空间是 Linux 内核提供的一个机制,用于隔离进程、网络、文件系统等资源。在 Docker 中,每个容器都运行在一个独立的命名空间中,确保容器之间的操作互不干扰。Docker 使用了以下几种命名空间来实现隔离:
1)PID Namespace(进程隔离):每个容器都有自己的进程命名空间,容器内部的进程只能看到并操作容器内部的进程,而无法看到宿主机或其他容器的进程。这意味着容器内部的 PID 1 进程仅在该容器中可见,容器外部的进程无法干扰容器中的进程。
2)NET Namespace(网络隔离):每个容器拥有自己的网络栈,包括 IP 地址、路由表、端口、网络接口等。容器之间的网络是隔离的,容器内的网络配置不会影响到宿主机或其他容器。通过网络命名空间,容器可以有独立的 IP 地址,容器间的通信需要通过网络桥接、端口映射等方式进行。
3)MNT Namespace(文件系统挂载隔离):容器有独立的文件系统视图,容器内的挂载点(如 /etc、/dev、/sys 等)是与宿主机和其他容器隔离的。通过挂载命名空间,每个容器只能访问自己容器内的文件系统,避免容器之间相互影响。
4)UTS Namespace(主机名隔离):每个容器都可以拥有自己的主机名和域名,这样容器内的 hostname 和宿主机的主机名不冲突,有助于容器内的应用区分不同的环境。
5)IPC Namespace(进程间通信隔离):容器内部的进程间通信(如共享内存、信号量等)也在自己的命名空间中,不会与宿主机或其他容器共享资源。
6)USER Namespace(用户隔离):容器内的用户 ID 和组 ID 可以与宿主机隔离,使容器内的 root 用户变成宿主机的普通用户,从而提升安全性,避免容器内的恶意用户获得宿主机的 root 权限。
2、控制组(Cgroups)
控制组(Cgroups)是 Linux 内核的另一项技术,旨在控制和限制进程(或容器)使用的系统资源。Docker 通过 Cgroups 对容器进行资源管理,从而实现资源隔离:
CPU 限制:可以限制每个容器使用的 CPU 时间和 CPU 核心。
内存限制:每个容器可以配置最大内存使用限制,防止容器过度使用内存导致宿主机崩溃。
I/O 限制:可以限制容器对磁盘 I/O、网络带宽等的使用。
通过 Cgroups,Docker 确保容器不会消耗过多的资源,从而避免对宿主机和其他容器造成影响。Cgroups) 技术,这些技术可以确保容器彼此之间、以及容器与宿主机之间的资源和操作系统环境的隔离。
十二、Linux常用命令篇
一、文件和命令
1、cd 命令
(它用于切换当前目录,它的参数是要切换到的目录的路径,可以是绝对路径,也可以是相对路径)
cd / 返回跟目录
cd - 返回上次所在的目录
mkdir <目录名> 创建目录
mkdir -p /tmp/dir1/dir2 递归创建目录树
rm -f file1 删除’file1’⽂件
rmdir dir1 删除’dir1’⽬录
rm -rf dir1 删除’dir1’⽬录和其内容
2、pwd 命令
pwd 显示工作路径
3、ls 命令
ls 查看目录中的文件
ls -l 显示文件和目录的详细资料
ls -a 列出全部文件,包含隐藏文件
ls -lh 查看⽂件和⽬录的详情列表(增强⽂件⼤⼩易读性)
4、cp 命令
(用于复制文件,copy之意,它还可以把多个文件一次性地复制到一个目录下)
cp -a /temp/dir1 复制一个目录至当前目录
5、mv 命令
-f force强制的意思,如果目标文件已经存在,不会询问而直接覆盖
-i 若目标文件已经存在,就会询问是否覆盖
-u 若目标文件已经存在,且比目标文件新,才会更新
mv old_dir new_dir 重命名/移动⽬录
6、rm 命令
-f :就是force的意思,忽略不存在的文件,不会出现警告消息
-i :互动模式,在删除前会询问用户是否操作
-r :递归删除,最常用于目录删除,它是一个非常危险的参数
7、cat 命令
(用于查看文本文件的内容,后接要查看的文件名,通常可用管道与 more 和 less 一起使用)
cat file1 从第一个字节开始正向查看文件的内容
grep ss hello.txt 在⽂件hello.txt中查找关键词 ss
二、文件搜索
1、find 命令
find / -name file 从根目录开始搜索文件/目录
find / -user user1 搜索用户 user1 的文件/目录
find /dir -name *.bin 在目录/dir 中搜索带有 .bin 后缀的文件
三、文件的权限 - 使用 “+” 设置权限,使用 “-” 用于取消
1、chmod 命令
ls -lh 显示当前目录所有文件的权限
chmod 777 文件名 修改文件权限(最高权限)
chmod ugo+rwx dir 设置目录的所有人(u)、群组(g)以及其他人(o)以读(r,4 )、写(w,2)和执行(x,1)的权限
chmod go-rwx dir1 删除群组(g)与其他人(o)对目录的读写执行权限
2、chown 命令
chown user1 file1 改变一个文件的所有人属性
chown -R user1 dir1 改变一个目录的所有人属性并同时改变改目录下所有文件的属性
chown user1:group1 file1 改变一个文件的所有人和群组属性
四、文本处理
1、grep 命令
(分析一行的信息,若当中有我们所需要的信息,就将该行显示出来,该命令通常与管道命令一起使用,用于对一些命令的输出进行筛选加工等等)
grep Aug /var/log/messages 在文件 '/var/log/messages’中查找关键词"Aug"
五、打包和压缩文件
1、tar 命令
对文件进行打包,默认情况并不会压缩,如果指定了相应的参数,它还会调用相应的压缩程序(如gzip和bzip等)进行压缩和解压)推荐
六、进程相关的命令
1、ps 命令
用于将某个时间点的进程运行情况选取下来并输出,process之意
ps -ef # 显示所有进程的详细信息。
ps aux # 查看系统所有的进程数据
netstat -tlnp # 查看各个节点及进程和使用的端口号:
-t: 显示 TCP 端口。
-u: 显示 UDP 端口。
-l: 仅显示监听状态的端口。
-n: 显示数字形式的地址和端口(不进行 DNS 解析)。
-p: 显示每个连接对应的进程 ID 和程序名称。
查看进程端口号:netstat -tunlp | grep 端口号
查看指定进程:ps -ef | grep 进程名称
2、kill 命令
kill -9 pid (-9表示强制关闭)
kill -9 程序的名字
七、启动(关闭,重启,查看)某个服务
systemctl (start|stop|restart|status) service_name
设置开机启动或者关闭某个服务
开机启动:systemctl enable service_name
开机关闭:systemctl disable service_name
十三、git常用命令
1、初始化仓库
创建一个新的本地 Git 仓库:
git init
克隆一个现有的远程仓库到本地:
git clone <repository-url>
2、 配置 Git 用户信息
设置用户名(通常只需设置一次):
git config --global user.name "Your Name"
设置用户邮箱(通常只需设置一次):
git config --global user.email "you@example.com"
3.、添加和提交更改
查看当前工作目录的状态(检查哪些文件被修改过或未跟踪):
git status
添加所有更改到暂存区:
git add .
提交暂存区的更改到本地仓库:
git commit -m "Commit message describing changes"
4、分支管理
列出所有本地分支:
git branch
创建并切换到新分支:
git checkout -b <branch-name>
切换到现有分支:
git checkout <branch-name>
删除本地分支:
git branch -d <branch-name>
合并分支到当前分支:
git merge <branch-name>
5、 远程仓库操作
查看配置的远程仓库:
git remote -v
添加远程仓库:
git remote add origin <repository-url>
推送本地分支到远程仓库:
git push origin <branch-name>
从远程仓库拉取最新的更改:
git pull origin <branch-name>
6、解决冲突
查看某个文件的历史版本差异:
git diff <file-name>
查看两个提交之间的差异:
git diff <commit-hash1> <commit-hash2>
十四、操作系统
1、什么情况下会发生死锁?
一般来说,多个线程相互等待对方持有的资源且都不释放自己的资源,这种现象称为死锁。具体有四个必要条件,必须同时满足才会发生死锁:
-
互斥条件:资源不能同时被多个线程共享访问,必须互斥访问。
-
占有且等待:一个线程已经占有至少一个资源,但又去申请另一个资源,同时该资源被其他线程占有。
-
不可剥夺:线程占有的资源不能被剥夺,资源只能在使用完后由线程自行释放。
-
环路等待:假设三个线程A、B、C。A在等待B占有的资源,B在等待C占有的资源,C在等待A占有的资源。形成资源等待的环形链。
这四大必要条件,只要能够破坏其一,就能避免死锁,可以采取以下几种措施: -
避免互斥条件:尽量减少资源的独占性,使用非阻塞同步机制。
-
破坏占有且等待:采用资源预分配策略,即进程一次性请求所需的所有资源。
-
破坏不可剥夺:如果一个进程得不到所需的资源,应释放它所持有的资源,或者使用优先级来剥夺资源。
-
破坏环路等待:对系统中的资源进行排序,每个线程按序请求资源,避免形成环路。
2、进程和线程的区别是什么?
- 定义
进程:是资源分配的最小单位。
线程:是CPU调度的最小单位。 - 资源开销
进程:创建或销毁进程的开销较大,需要分配独立的内存和资源。
线程:创建或销毁线程的开销较小,共享进程的资源。 - 资源共享
进程:进程之间不共享内存,各有独立的资源。
线程:同一进程内的线程共享进程的内存空间、全局变量、文件描述符等
3、进程的通信方式有哪些?
-
管道(Pipe)
- 匿名管道:匿名管道是最简单的进程间通信方式,通常用于父子进程或同一用户的进程之间。它提供了一个字节流,允许一个进程将数据写入管道,另一个进程从管道中读取数据。匿名管道是单向的。
适用于:父子进程或在相同终端下的进程。 - 命名管道(FIFO):命名管道是管道的一种特殊形式,它有一个在文件系统中的名字,可以在不同的进程之间通信,甚至是没有亲缘关系的进程。命名管道是全双工的,可以支持双向通信。
适用于:任意两个进程之间,只要它们能访问到管道。
- 匿名管道:匿名管道是最简单的进程间通信方式,通常用于父子进程或同一用户的进程之间。它提供了一个字节流,允许一个进程将数据写入管道,另一个进程从管道中读取数据。匿名管道是单向的。
-
消息队列(Message Queue)
消息队列是操作系统提供的一种通信机制,它允许一个进程将消息写入队列,而另一个进程从队列中读取消息。消息队列具有先进先出的特性,可以通过消息的优先级来控制消息的处理顺序。 -
共享内存(Shared Memory)
共享内存是一种高效的进程间通信方式,它允许多个进程将一块内存区域映射到自己的地址空间,进程之间通过读写共享内存来交换数据。共享内存是最快的IPC方式,但需要进行同步控制以避免数据竞争。 -
信号(Signal)
信号是一种较为简单的进程间通信方式,它通过操作系统发送某些事件通知给进程。信号通常用于处理某些异常或事件,如进程终止、定时器到期、外部中断等。进程可以注册信号处理函数,在接收到特定信号时做出响应。 -
套接字(Socket)
套接字是一种网络通信机制,它允许不同主机上的进程之间进行通信。套接字通过TCP/IP协议进行通信,可以在局域网或广域网上进行进程间通信。套接字支持全双工通信,即可以同时进行数据的发送和接收。 -
信号量(Semaphore)
信号量是一种用于进程同步的机制,主要用于控制对共享资源的访问。信号量通常用于解决进程之间的互斥和同步问题,避免多个进程同时访问共享资源造成的数据冲突。信号量有计数信号量和二进制信号量两种形式。
十五、常见Java面试场景题
1、从网络角度来看,用户从输入网址到网页显示,期间发生了什么?
-
DNS解析:将用户输入的域名(如www.example.com)解析为服务器的IP地址。
-
建立TCP连接(三次握手):根据 IP 地址和端口号,发送TCP三次握手建立与目标服务器的连接(如果是HTTPS,还会进行TLS/SSL握手)。
-
浏览器发送HTTP请求:客户端向服务器发送HTTP/HTTPS请求,请求网页资源。
-
服务器处理请求:服务器接收到请求后处理动态或静态资源,并返回HTTP响应(包含HTML、CSS、JavaScript等内容)。
-
浏览器接收响应:通过TCP协议将服务器的响应数据传输到客户端。
-
浏览器渲染:
- 解析HTML:生成DOM树。
- 解析CSS:生成CSSOM树。
- 执行JavaScript:可能更新DOM和CSSOM。
- 渲染页面:合成渲染树,布局和绘制到屏幕上。
- 用户交互:页面加载完成后,用户可以与网页交互,可能触发更多的动态请求。
2、分布式锁的实现?
分布式锁用于解决分布式环境中的并发控制问题,常见的实现方式有:
1、Redis
利用 Redis 的 SETNX 命令(只在键不存在时设置值)或者 Redis 的 Redisson 客户端实现分布式锁。通过设置超时保证锁的自动释放,防止死锁。
2、Zookeeper
通过抢夺 Zookeeper 的临时有序节点,在节点上设置锁,当某个客户端获取到锁时,其他客户端只能等待前一个客户端释放锁。
3、数据库
通过在数据库中创建一张锁表,利用行级锁进行控制,获取锁的线程能够修改锁表的记录,其他线程需要等待。
3、分布式锁有效性的保证
要确保分布式锁的有效性,通常考虑以下几点:
-
锁的超时机制:为了避免死锁,需要给锁设置一个超时时间,防止程序异常后锁无法释放。
-
锁的持有者识别:锁的持有者应该有唯一标识,防止不同客户端误操作锁。
-
可重入性和公平性:对于有些场景,可能需要设置锁的可重入性或者公平性(保证锁按请求顺序分配给线程)。
4、生产环境遇到慢sql应该怎么解决?
- 查看执行计划
使用数据库提供的explain
查看 SQL 的执行计划。这样可以判断是否使用了索引,是否有全表扫描等低效操作。分析索引的使用情况,是否可以添加新的索引或优化现有索引。 - 优化查询语句以及进行分页查询
减少查询的列数:仅查询实际需要的字段,避免使用 SELECT *。
分页查询:对于大数据集的查询,使用 LIMIT 分页,不要一次性返回全部数据。 - 创建和优化索引
检查查询中经常用作过滤条件、排序条件或者连接条件的字段,确保它们上有合适的索引。
但需要注意,不要对每个字段都创建索引,过多的索引会影响插入、删除和更新操作的性能。 - 分区表
对于非常大的表,可以考虑使用数据库的分区表功能,将表数据按一定规则(如日期、地区等)分割到不同的物理存储上,从而提高查询效率。 - 数据库连接池调优
通过使用,可以通过调整数据库的配置参数(如缓存大小、连接池等)来提高性能。 - 分析慢查询日志
通过Druid进行查看慢查询日志
5、如果线上运行的Java程序导致cpu服务器突然爆满,可能哪里出现问题,应该用什么方法进行检测?
1、线程的死锁或者死循环
- 问题:死锁导致无法释放锁,线程一直运行,以及线程的死循环状态
- 解决方法:
- 使用
jstack
或者jconsole
查看线程堆栈信息,检查是否有线程处于长时间运行状态。 - 或者使用
ps -ef
查看进程ID和对应的Java线程。
- 使用
2、内存泄漏或者内存频繁 GC
- 原因:如果程序存在内存泄漏或频繁的垃圾回收(GC),也会导致 CPU 过高,因为 GC 会消耗大量 CPU 资源。
- 检测方法:
- 使用
jstat
查看JVM的垃圾回收情况,检查是否频繁发生full GC
。 - 在日志中查找GC日志,分析GC时间和频率。
3、访问数据库或者外部延迟
- 使用
- 检测方法:
- 查看数据库查询日志等等
十六、架构及设计题
1、在Java中什么是CAP?
在Java中,CAP通常指的是CAP理论(Consistency, Availability, Partition tolerance),它是分布式系统设计中的一个重要概念。CAP理论指出,在一个分布式系统中,不可能同时满足以下三个条件:
- 一致性(Consistency):所有节点在同一时刻看到的数据都是一样的,系统在某个时间点的数据状态是统一的。
- 可用性(Availability):系统在任何时候都能返回数据,即使某些节点不可用,系统也能保证响应请求。
- 分区容忍性(Partition tolerance):系统能够容忍网络分区,允许某些节点或网络断开,系统依然能继续操作。
CAP定理的核心思想是:在分布式系统中,不能同时保证一致性、可用性和分区容忍性,最多只能同时满足其中的两个条件。具体而言,分布式系统通常会面临以下几种选择:
CA(Consistency + Availability):保证数据一致性和可用性,但无法容忍网络分区。适用于不太容易出现网络分区的系统。
CP(Consistency + Partition tolerance):保证数据一致性和分区容忍性,但可能牺牲可用性。适用于网络经常发生分区的环境。
AP(Availability + Partition tolerance):保证数据可用性和分区容忍性,但可能牺牲一致性。适用于高可用性要求的系统,如社交媒体平台。
2、什么是微服务?
微服务(Microservices)是一种架构风格,它将一个大型的、复杂的应用程序拆分成一组小的、独立的服务。每个微服务都是围绕一个特定的业务功能构建的,并且通常具备以下几个特点:
-
单一职责:每个微服务都专注于一个具体的功能或业务领域,通常是一个小而明确的任务。例如,一个微服务可能只负责用户管理,另一个微服务负责订单处理。
-
独立部署:每个微服务都可以独立部署和扩展。微服务之间通过网络通信(通常是HTTP或消息队列)进行交互,每个服务可以用不同的技术栈、语言来实现。
3、什么是网关?
网关(Gateway) 是一种网络设备或服务,网关主要用于提供对外的统一入口,负责请求的转发、路由和一些额外的处理功能(如安全认证、限流、负载均衡等)。
网关的基本作用:
-
统一入口:网关作为系统对外的唯一访问入口,所有外部请求首先通过网关,然后由它转发到对应的微服务。通过这种方式,客户端只需要知道网关的地址,而不需要关心微服务的具体细节。
-
请求路由:网关根据请求的路径、参数或头部信息,将请求路由到相应的后端微服务。它充当了请求和微服务之间的中介。
-
负载均衡:网关可以在多个实例的微服务之间分配请求,从而实现负载均衡,保证系统的高可用性和高吞吐量。
-
API聚合:在微服务架构中,多个服务可能会提供不同的数据和功能。网关可以将多个服务的响应聚合到一起,简化客户端的请求过程,减少客户端需要发出的请求数量。