目录
一、Java基础
1.JDK 和 JRE 的区别?
2.== 和 equals 的区别?
3.hashCode() 和 equals() 的关系?
4. final 在 java 中的作用?
5.Math.round(-1.5) 的结果?
6.String 是否属于基础数据类型?
7.Java 中操作字符串的类及区别?
8.String str="i" 和 String str=new String("i") 的区别?
9.如何将字符串反转?
10.String 类的常用方法?
11.抽象类必须要有抽象方法吗?
12.普通类和抽象类有哪些区别?
13.抽象类能使用 final 修饰吗?
14.接口和抽象类有什么区别?
15.Java 中 IO 流分为几种?
16.BIO、NIO、AIO 有什么区别?
17.Files 的常用方法都有哪些?
二、容器
1.Java 容器都有哪些?
2.Collection 和 Collections 有什么区别?
3.List、Set、Map 之间的区别是什么?
4.HashMap 和 Hashtable 有什么区别?
5.如何决定使用 HashMap 还是 TreeMap?
6.说一下 HashMap 的实现原理?
7.说一下 HashSet 的实现原理?
8.ArrayList 和 LinkedList 的区别是什么?
9.如何实现数组和 List 之间的转换?
10.ArrayList 和 Vector 的区别是什么?
11.Array 和 ArrayList 有何区别?
12.在 Queue 中 poll()和 remove()有什么区别?
13.哪些集合类是线程安全的?
14.迭代器 Iterator 是什么?
15.Iterator 怎么使用?有什么特点?
16.Iterator 和 ListIterator 有什么区别?
17.怎么确保一个集合不能被修改?
三、多线程
1.并行和并发有什么区别?
2.线程和进程的区别?
3.守护线程是什么?
4.创建线程有哪几种方式?
5.说一下 runnable 和 callable 有什么区别?
6.线程有哪些状态?
7.sleep() 和 wait() 有什么区别?
8.notify()和 notifyAll()有什么区别?
9.线程的 run()和 start()有什么区别?
10.创建线程池有哪几种方式?
11.线程池都有哪些状态?
12.线程池中 submit()和 execute()方法有什么区别?
13.在 java 程序中怎么保证多线程的运行安全?
14. 多线程锁的升级原理是什么?
15.什么是死锁?
16.怎么防止死锁?
17.ThreadLocal 是什么?有哪些使用场景?
18.说一下 synchronized 底层实现原理?
19.synchronized 和 volatile 的区别是什么?
20.synchronized 和 Lock 有什么区别?
21.synchronized 和 ReentrantLock 区别是什么?
22.说一下 atomic 的原理?
四、反射
1.什么是反射?
2.什么是 java 序列化?什么情况下需要序列化?
3.动态代理是什么?有哪些应用?
4.怎么实现动态代理?
五、对象拷贝
1.为什么要使用克隆?
2.如何实现对象克隆?
3.深拷贝和浅拷贝区别是什么?
六、Java Web
1. jsp 和 servlet 有什么区别?
2. jsp 有哪些内置对象?作用分别是什么?
3.说一下 jsp 的 4 种作用域?
5.说一下 session 的工作原理?
7.spring mvc 和 struts 的区别是什么?
8.如何避免 SQL 注入?
9.什么是 XSS 攻击,如何避免?
10.什么是 CSRF 攻击,如何避免?
七、异常
1.throw 和 throws 的区别?
2.final、finally、finalize 有什么区别?
3.try-catch-finally 中哪个部分可以省略?
4.try-catch-finally 中,如果 catch 中 return 了,finally 还会执行吗?
5.常见的异常类有哪些?
八、网络
1.http 响应码 301 和 302 代表的是什么?有什么区别?
2.forward 和 redirect 的区别?
3.简述 TCP 和 UDP 的区别?
4.TCP 为什么要三次握手,两次不行吗?为什么?
5.说一下 TCP 粘包是怎么产生的?
6.OSI 的七层模型都有哪些?
7.GET 和 POST 请求有哪些区别?
8.如何实现跨域?
9.说一下 JSONP 实现原理?
九、设计模式
1.说一下你熟悉的设计模式?
2.简单工厂和抽象工厂有什么区别?
十、Spring/Spring MVC
1.为什么要使用 Spring?
2.解释一下什么是 AOP?
3.解释一下什么是 IOC?
4.Spring 有哪些主要模块?
5.Spring 常用的注入方式有哪些?
6.Spring 中的 bean 是线程安全的吗?
7.Spring 支持几种 bean 的作用域?
8.Spring 自动装配 bean 有哪些方式?
9.Spring 事务实现方式有哪些?
10.说一下 Spring 的事务隔离?
11.说一下 Spring MVC 运行流程?
12.Spring MVC 有哪些组件?
13.@RequestMapping 的作用是什么?
14.@Autowired 的作用是什么?
十一、Spring Boot/Cloud
1.什么是 Spring Boot?
2.为什么要用 Spring Boot?
3.Spring Boot 核心配置文件是什么?
4.Spring Boot 配置文件有哪几种类型?它们有什么区别?
5.Spring Boot 有哪些方式可以实现热部署?
6.JPA 和 Hibernate 有什么区别?
7.什么是 Spring Cloud?
8.Spring Cloud 断路器的作用是什么?
9.Spring Cloud 的核心组件有哪些?
十二、Mybatis
1.MyBatis 中 #{}和 ${}的区别是什么?
2.MyBatis 有几种分页方式?
3.RowBounds 是一次性查询全部结果吗?为什么?
4.MyBatis 逻辑分页和物理分页的区别是什么?
5.MyBatis 是否支持延迟加载?延迟加载的原理是什么?
6.说一下 MyBatis 的一级缓存和二级缓存?
7.MyBatis 和 Hibernate 的区别有哪些?
8.MyBatis 有哪些执行器(Executor)?
9.MyBatis 分页插件的实现原理是什么?
10.MyBatis 如何编写一个自定义插件?
十三、RabbitMQ
1. RabbitMQ 的使用场景有哪些?
2.RabbitMQ 有哪些重要的角色?
3.RabbitMQ 有哪些重要的组件?
4.RabbitMQ 中 vhost 的作用是什么?
5.RabbitMQ 的消息是怎么发送的?
6.RabbitMQ 怎么保证消息的稳定性?
7.RabbitMQ 怎么避免消息丢失?
8.要保证消息持久化成功的条件有哪些?
9.RabbitMQ 持久化有什么缺点?
10.RabbitMQ 有几种广播类型?
11.RabbitMQ 怎么实现延迟消息队列?
12.RabbitMQ 集群有什么用?
13.RabbitMQ 节点的类型有哪些?
14.RabbitMQ 集群搭建需要注意哪些问题?
15.RabbitMQ 每个节点是其他节点的完整拷贝吗?为什么?
16.RabbitMQ 集群中唯一一个磁盘节点崩溃了会发生什么情况?
17.RabbitMQ 对集群节点停止顺序有要求吗?
十四、MySQL
1.数据库的三范式是什么?
2.一张自增表里面总共有 7 条数据,删除了最后 2 条数据,重启 MySQL 数据库,又插入了一 条数据,此时 id 是几?
3.如何获取当前数据库版本?
4.说一下 ACID 是什么?
5.char 和 varchar 的区别是什么?
6.float 和 double 的区别是什么?
7.MySQL 的内连接、左连接、右连接有什么区别?
8.MySQL 索引是怎么实现的?
9.怎么验证 MySQL 的索引是否满足需求?
10.说一下数据库的事务隔离?
11.说一下 MySQL 常用的引擎?
12.说一下 MySQL 的行锁和表锁?
13.说一下乐观锁和悲观锁?
14.MySQL 问题排查都有哪些手段?
15.如何做 MySQL 的性能优化?
十五、Redis
1.redis 是什么?都有哪些使用场景?
2.redis 有哪些功能?
3.redis 和 memcached 有什么区别?
4. redis 为什么是单线程的?
5.什么是缓存穿透?怎么解决?
6.redis 支持的数据类型有哪些?
7.redis 支持的 Java 客户端都有哪些?
8.jedis 和 redisson 有哪些区别?
9.怎么保证缓存和数据库数据的一致性?
10.redis 持久化有几种方式?
11. redis 怎么实现分布式锁?
12.redis 分布式锁有什么缺陷?
13.redis 如何做内存优化?
14.redis 淘汰策略有哪些?
15.redis 常见的性能问题有哪些?该如何解决?
十六、JVM
1.说一下 JVM 的主要组成部分?及其作用?
2.说一下 JVM 运行时数据区?
3.说一下堆栈的区别?
4.队列和栈是什么?有什么区别?
5.什么是双亲委派模型?
6. 说一下类加载的执行过程?
7.怎么判断对象是否可以被回收?
8.Java 中都有哪些引用类型?
9.说一下 JVM 有哪些垃圾回收算法?
10.说一下 JVM 有哪些垃圾回收器?
11.详细介绍一下 CMS 垃圾回收器?
12.新生代垃圾回收器和老生代垃圾回收器都有哪些?有什么区别?
13. 简述分代垃圾回收器是怎么工作的?
14.说一下 JVM 调优的工具?
15.常用的 JVM 调优的参数都有哪些?
JDK ( Java Development Kit )是 Java 开发工具包,用于开发和编译 Java 程序。它包含了编译器、调试器等开发工具,还有Java 类库。 JRE ( Java Runtime Environment )是 Java 运行时环境, 用于运行已编译的Java 程序。它包含 Java 虚拟机( JVM )和运行时类库。简而言之, JDK 用于开发, JRE用于运行。
== 运算符用于比较两个对象的引用是否相同,即它们是否指向同一内存地址。 equals 方法用于比较两个对象的内容是否相等。默认情况下, equals 与 == 的效果相同,都是比较引用。但在类中可以重写 equals 方法,实现自定义的内容比较逻辑。
hashCode 是一个用于散列和查找的值,而 equals 用于比较对象的内容是否相等。如果两个对象的 hashCode 相同,表示它们在散列存储结构中可能位于同一位置,但 hashCode 相同并不意 味着equals 一定为 true 。两个对象相等( equals 返回 true )时,它们的 hashCode 通常应相同,但 并非绝对要求。
在 Java 中, final 关键字可以用于变量、方法和类。用于变量时,表示该变量是一个常量,只能被赋值一次。用于方法时,表示方法不能被子类重写。用于类时,表示类不能被继承。 final 修饰的变量会在编译时进行常量折叠,优化代码执行效率。
Math.round(-1.5) 的结果是 -1 。 Math.round 方法对小数进行四舍五入, -1.5 四舍五入后取到更接近的整数 -1 。
不是, String 不属于基础数据类型。基础数据类型包括 int 、 double 、 boolean 等,用于存储基本的数值类型。而 String 是引用类型,用来表示一串字符。
在 Java 中,操作字符串的类有 String 、 StringBuilder 和 StringBuffer 。 String 是不可变的,每次修改都会生成新的 String 对象,适合不经常修改的情况。 StringBuilder 和 StringBuffer 是可变的,可以高效地进行字符串的修改, StringBuilder 适用于单线程环境,而 StringBuffer 适用于多线 程环境。
String str="i" 是将字符串 "i" 放入字符串池,如果池中已存在则返回引用,否则创建新引用。String str=new String("i") 在堆内存中创建新的 String 对象,不论池中是否已存在相同字符串。
可以使用 StringBuilder 的 reverse 方法来反转字符串。
String 类的常用方法包括 charAt 、 length 、 substring 、 concat 、 trim 、 toUpperCase 、toLowerCase 、 startsWith 、 endsWith 等,用于不同的字符串操作。
不一定。抽象类可以包含抽象方法,也可以不包含。抽象方法是没有实际实现的方法,需要在子类中被具体实现。如果一个类中包含了至少一个抽象方法,那么这个类必须声明为抽象类。
普通类可以被实例化,即可以创建对象。抽象类不能被直接实例化,只能被继承。普通类可以有实例变量和方法,而抽象类可以包含抽象方法(可以有非抽象方法)。子类继承普通类时,不需要实现父类的方法;但继承抽象类时,必须实现父类的抽象方法。
是的,抽象类可以使用 final 修饰。在 Java 中, final 修饰的类不能被继承,无法有子类。如果一个抽象类被 final 修饰,它就不能再被其他类继承,相当于限定了其子类的数量为零。
接口和抽象类都是用于实现多态性和定义规范。主要区别在于:
- 一个类只能继承一个抽象类,但可以实现多个接口。
- 抽象类可以包含抽象方法和非抽象方法,而接口只能包含抽象方法。
- 抽象类可以有构造方法,接口不能有构造方法。
- 接口中的字段默认为常量,而抽象类中可以有各种字段。
Java 中的 IO 流可以分为两类:字节流( Byte Stream )和字符流( Character Stream )。每一类又分为输入流和输出流,共计四种类型:字节输入流、字节输出流、字符输入流和字符输出流。
BIO ( Blocking IO )是同步阻塞 IO ,每个连接都需要一个独立的线程处理,适合连接数较少的情况; NIO ( New IO , Non-Blocking IO )是同步非阻塞 IO ,通过单一线程管理多个连接,适合高并发场景;AIO ( Asynchronous IO )是异步非阻塞 IO ,适用于连接数较多且数据传输较大的情况,通过异步事件通知方式工作。
Files 类是 java.nio.file 包下用于操作文件和目录的工具类。其常用方法包括:
- readAllLines (读取所有行)
- write (写入数据到文件)
- createDirectory (创建目录)
- copy (复制文件或目录)
- move (移动文件或目录)
- delete (删除文件或目录)
- isDirectory (判断是否是目录)
- isRegularFile (判断是否是普通文件)等。
Java 提供了丰富的容器类,包括 Collection 接口的实现类(如 List 、 Set 等)和 Map 接口的实现类(如 HashMap 、 TreeMap 等),它们分别用于存储不同类型的元素和键值对。
Collection 是 Java 集合框架的根接口,定义了基本的集合操作方法,如添加、删除、遍历等。 Collections 是一个包含静态方法的工具类,提供了一些集合操作的实用方法,如排序、查找等。
List 是有序集合,允许存储重复元素,可以通过索引访问元素;Set 是无序集合,不允许重复元素;Map 是键值对的映射,每个键都唯一对应一个值。
HashMap 是非线程安全的,允许有 null 键和值,效率较高;Hashtable 是线程安全的,不允许有null 键和值,效率相对较低。
如果需要无序、高效的键值对存储,可使用 HashMap ;如果需要按键有序存储,可使用TreeMap ,它会根据键的自然顺序或指定的比较器进行排序。
HashMap 基于哈希表实现,内部使用一个数组来存储 Entry 对象,通过键的哈希码确定数组位置,当多个键映射到同一个位置时,使用链表或红黑树解决冲突。
HashSet 基于 HashMap 实现,它实际上是在 HashMap 的键部分存储元素,而值部分存储一个常量。
ArrayList 是基于动态数组实现,通过索引访问元素速度快,插入和删除元素较慢;LinkedList 是基于双向链表实现,插入和删除元素速度快,但访问元素较慢。
可以使用 Arrays.asList() 方法将数组转换为 List ,或者使用 ArrayList 的构造函数将 List 转换为数组。
ArrayList 是非线程安全的,因此在高并发环境下性能较好; Vector 是线程安全的,通过锁来保证线程安全,因此性能相对较低。
Array 是固定长度的,元素类型可以是基本类型,创建后大小不可改变; ArrayList 是可变长度的,只能存储对象,可以动态添加和删除元素。
poll() 方法从队列头部获取并删除元素,如果队列为空则返回 null ; remove() 方法从队列头部获取并删除元素,如果队列为空会抛出 NoSuchElementException 异常。
Vector 、 Hashtable 、 Collections 类的 synchronizedXxx 方法生成的集合,以及ConcurrentHashMap等集合类是线程安全的。
迭代器是一种用于遍历集合元素的接口,提供了统一的遍历方式,使得遍历过程更加简洁和灵活。
通过调用集合的 iterator() 方法获取迭代器对象,然后使用 hasNext() 判断是否有下一个元素,使用next() 获取下一个元素。迭代器的特点是只能单向遍历,不支持修改操作。
Iterator 用于遍历集合,只能单向遍历,不支持修改操作; ListIterator 是 Iterator 的扩展,支持双向遍历,还可以在遍历过程中修改集合。
可以使用 Collections.unmodifiableXxx 方法,将集合转换为不可修改的视图,尝试修改会抛出 UnsupportedOperationException 异常。
并行是指多个任务同时进行,每个任务有自己的执行线程;并发是指多个任务交替进行,通过时间片轮转或优先级调度实现。
进程是独立的程序执行单位,拥有独立的内存空间;线程是进程内的执行单元,共享进程的内存空间。
守护线程( Daemon Thread )是在程序后台提供服务的线程,当所有非守护线程结束时, 守护线程会自动终止。
可以通过继承 Thread 类、实现 Runnable 接口、实现 Callable 接口、使用线程池等方式创建线程。
Runnable 用于定义一个线程任务,不返回结果; Callable 也用于定义一个线程任务,但可以返回结果,并且可以抛出异常。
线程有多个状态,包括 New (新建)、 Runnable (可运行)、 Blocked (阻塞)、 Waiting(等待)、Timed Waiting (计时等待)和 Terminated (终止)。
sleep() 方法是 Thread 类的方法,会让线程休眠一段时间; wait() 方法是 Object 类的方法,会让线程等待并释放锁。
notify() 方法唤醒一个正在等待该对象锁的线程; notifyAll() 方法唤醒所有正在等待该对象锁的线程。
run() 方法定义了线程的任务逻辑,直接调用会在当前线程中执行; start() 方法启动线程,在新的线程中执行run() 方法。
可以使用 Executors 工厂类的静态方法创建线程池,或者直接使用 ThreadPoolExecutor 构造函数。
线程池的状态有 RUNNING (运行中)、 SHUTDOWN (关闭中,不接受新任务)、 STOP(立即关闭,中断正在执行任务的线程)和 TERMINATED (终止)。
submit() 方法可以提交 Callable 任务,并返回 Future 对象; execute() 方法只能提交 Runnable任务,无返回值。
可以使用 synchronized 关键字、 Lock 接口、原子类等机制,确保多个线程访问共享资源时不会出现数据竞争。
在 JVM 中,锁会根据竞争情况从无锁升级为偏向锁、轻量级锁,最终升级为重量级锁,以适应不同场景的线程竞争。
死锁是指两个或多个线程互相持有对方需要的锁,导致所有线程都无法继续执行。
可以使用避免加锁顺序破坏、使用定时锁等方法来避免死锁的发生。
ThreadLocal 是一种线程本地变量,每个线程都拥有自己的变量副本,常用于实现线程封闭和线程上下文信息传递。
synchronized 使用了对象的内部锁(监视器锁),它可以用来修饰代码块或方法,保证在同一时刻只有一个线程可以进入临界区。
synchronized 是一种独占锁,可以实现原子操作和临界区的同步; volatile 是一种轻量级的同步机制,用于保证可见性和禁止指令重排序。
synchronized 是 Java 内置的关键字,自动管理锁的获取和释放; Lock 是 Lock 接口的实现类,需要手动管理锁的获取和释放,提供了更灵活的锁控制。
synchronized 是关键字,无法中断等待获取锁的线程; ReentrantLock 是 Lock 接口的实现类,可以中断等待获取锁的线程。
atomic 包提供了一些原子操作类,通过 CAS ( Compare and Swap )操作实现了多线程环境下的线程安全,确保操作的原子性。
反射是 Java 中的一种机制,允许在运行时获取类的信息、访问对象的属性和方法,以及调用对象的方法,使得编程更加灵活,但也需要注意性能和安全问题。
Java 序列化是指将对象转换为字节流,以便在网络传输或持久化存储中使用。需要序列化的情况包括将对象存储到文件、传递对象给远程方法、将对象存储到缓存等。
动态代理是在运行时创建代理对象的机制,可以通过代理对象拦截并重写方法,实现 AOP 编程、远程调用等功能。应用场景包括日志记录、事务管理、权限控制等。
Java 提供了两种动态代理实现:基于接口的动态代理( JDK Proxy )和基于类的动态代理( CGLIB )。基于接口的动态代理需要目标类实现接口,而基于类的动态代理通过继承目标类来创建代理。可以使用InvocationHandler 接口实现代理类,实现其中的 invoke 方法来拦截目标方法的调用。
使用克隆可以在不影响原始对象的情况下创建对象的副本,用于防止修改原始对象或用作对 象的临时备份,以及避免重复创建新对象
在 Java 中,可以通过实现 Cloneable 接口并重写 clone 方法来实现对象克隆。在 clone 方法内部,可以使用 super.clone() 获取对象的浅拷贝,然后再对需要的属性进行深拷贝。
浅拷贝是创建一个新对象,新对象的属性和原始对象的属性引用相同的对象;深拷贝是创建一个新对象,新对象的属性是原始对象属性的副本,包括引用类型的属性。深拷贝会复制所有引用对象,不会共享引用。
JSP ( JavaServer Pages )是一种在 HTML 中嵌入 Java 代码的技术,适合用于生成动态内容;Servlet是 Java 编写的服务器端程序,用于处理 HTTP 请求和响应。
JSP 有 9 个内置对象,包括 request 、 response 、 session 、 application 、 out 、 page 、pageContext 、 config 和 exception ,用于在 JSP 页面中访问 HTTP 请求和其他上下文信息。
JSP 的四种作用域分别是 page (页面)、 request (请求)、 session (会话)和 application(应用程序),用于在不同范围内存储和共享数据。
Session 是在服务器端维护的一种状态信息,保存在服务器内存中; Cookie 是在客户端维护的一种状态信息,保存在客户端的浏览器中。
当客户端发送请求时,服务器为每个客户端创建一个唯一的 Session ID ,将 Session ID 保存在 Cookie 中或通过 URL 重写传递给客户端。服务器通过 Session ID 来查找对应的 Session 对象,从而维护客户端会话状态。
如果客户端禁止了 Cookie ,仍然可以通过 URL 重写的方式传递 Session ID 来实现 Session 功能。
答案: Spring MVC 和 Struts 都是 Java Web 开发框架,但 Spring MVC 更加灵活、模块化,支持依赖注入和AOP ;而 Struts 是基于 MVC 模式的框架,较老,相对来说更加限制。
可以使用预编译语句、参数化查询、 ORM 框架等方式来避免 SQL 注入,不要直接将用户输入 的数据拼接到SQL 语句中。
XSS ( Cross-Site Scripting )攻击是指攻击者通过在网页中插入恶意脚本,从而窃取用户信息。可以避免 XSS 攻击的方法包括对用户输入进行合适的转义、过滤和验证。
CSRF ( Cross-Site Request Forgery )攻击是指攻击者通过欺骗用户在未经授权的情况下执行操作。可以通过使用验证码、检查 Referer 头、使用 Token 验证等方式来避免 CSRF 攻击。
throw 用于在方法内部抛出一个异常对象,将控制权交给调用者; throws 用于方法声明中,表示方法可能抛出异常,调用者需要处理这些异常。
final 用于修饰类、方法和变量,表示不可变、不可继承或不可重写; finally 用于 try-catch 语句块,表示无论是否发生异常都会执行的代码; finalize 是 Object 类的方法,在对象被垃圾回收前调用。
在 try-catch-finally 中, catch 和 finally 两部分都可以省略,但 try 部分不能省略。
会,不管 catch 中是否有 return 语句, finally 中的代码都会执行。
常见的异常类包括
- NullPointerException(空指针异常)
- IllegalArgumentException(非法参数异常)
- ArrayIndexOutOfBoundsException(数组越界异常)
- ArithmeticException (算术异常)
- ClassCastException(类转换异常)
- IOException(输入输出异常)
- FileNotFoundException(文件未找到异常)等
HTTP 响应码 301 表示永久重定向,告诉客户端请求的资源已经永久移到了新位置; HTTP 响应码 302 表示临时重定向,告诉客户端请求的资源临时移到了新位置。区别在于 301 会导致浏览器缓存重定向信息,而302 不会。
forward 是服务器内部的跳转,将请求转发给另一个资源处理,客户端不知道发生了转发;redirect 是客户端重定向,服务器返回一个响应码和新的 URL ,客户端会重新发起请求。
TCP ( Transmission Control Protocol )是面向连接、可靠的协议,提供字节流传输,确保数据完整性; UDP ( User Datagram Protocol )是无连接、不可靠的协议,提供数据包传输,不保证数据完整性。
三次握手是为了确保客户端和服务器都已准备好发送和接收数据。如果只有两次握手,假设客户端发出连接请求后由于网络问题导致的延迟,而连接请求又在超时时间内到达服务器,服务器会认为客户端仍然想建立连接。
TCP 粘包是指发送方发送的若干个数据包被接收方一次性接收的现象。这可能是因为发送方连续发送数据,接收方缓冲区尚未读取完毕,导致多个数据包合并到一个接收窗口中。
OSI 的七层模型包括物理层、数据链路层、网络层、传输层、会话层、表示层和应用层,每一层负责不同的功能。
GET 请求将数据附加在 URL 上,有长度限制,适合请求数据; POST 请求将数据放在请求体内,没有长度限制,适合发送大量数据,如提交表单。
可以通过设置 CORS (跨源资源共享)响应头、使用 JSONP 、代理服务器等方式实现跨域。
JSONP 利用了浏览器允许跨域请求不受同源策略限制的特性。在 JSONP 中,页面通过动态添 加标签来请求另一个域的资源,服务端返回一个包裹回调函数调用的JavaScript 代码,实现数据的 获取。
我熟悉的设计模式包括单例模式、工厂模式、抽象工厂模式、建造者模式、原型模式、适配器模式、桥接模式、装饰者模式、代理模式、策略模式、观察者模式、模板方法模式、命令模式、 状态模式、责任链模式、访问者模式、迭代器模式、中介者模式和备忘录模式。
简单工厂模式通过一个工厂类根据传入的参数来创建相应的产品类的实例;抽象工厂模式定 义一个接口用于创建一系列相关或相互依赖的对象,将工厂的抽象和产品的抽象解耦,可以创建不同产品族的对象。简单工厂只有一个工厂类,适用于产品种类较少的情况;抽象工厂有多个工厂 类,适用于产品种类较多或需要支持多个产品族的情况。
Spring 是一个轻量级的开发框架,提供了丰富的特性和模块,帮助开发者简化开发过程,提高代码的可测试性、可维护性和可扩展性,同时支持面向切面编程、依赖注入等。
AOP ( Aspect-Oriented Programming )是一种编程范式,用于将与业务逻辑无关的横切关注点(如日志、事务管理)从主要业务逻辑中分离出来,以便重用和管理。
IOC ( Inversion of Control )是一种设计原则,它将对象的创建和管理交给容器,而不是由程序员手动管理。 Spring 的 IOC 容器负责创建、注入和管理对象。
Spring 包括核心容器、 AOP 模块、数据访问 / 集成模块、 Web 模块和消息模块等主要模块,用于支持不同的开发需求。
Spring 的常用注入方式有构造器注入、属性注入和方法注入。
默认情况下, Spring 中的单例( Singleton ) bean 是线程不安全的,多个线程可能共享同一个实例。如果需要线程安全的 bean ,可以使用原型( Prototype )作用域。
Spring 支持五种 bean 的作用域,分别是单例( Singleton )、原型( Prototype )、会话( Session )、请求( Request )和全局会话( Global Session )。
Spring 自动装配 bean 有三种方式,分别是 no (默认不自动装配)、 byName (根据属性名自动装配)和 byType (根据属性类型自动装配)。
Spring 事务实现方式包括编程式事务管理和声明式事务管理。声明式事务管理可以基于 XML配置或基于注解。
Spring 的事务隔离级别包括 READ_UNCOMMITTED 、 READ_COMMITTED 、REPEATABLE_READ 和 SERIALIZABLE 。不同的隔离级别决定了事务之间的可见性和并发性。
Spring MVC 的运行流程包括请求的分发、处理器映射、处理器适配、处理器执行、视图解析、视图渲染等步骤。
Spring MVC 包括控制器( Controller )、处理器映射( Handler Mapping )、处理器适配( Handler Adapter )、视图解析器( View Resolver )等组件。
@RequestMapping 是 Spring MVC 中的注解,用于映射请求路径到处理方法,指定请求的HTTP 方法、路径等。
@Autowired 是 Spring 的注解,用于自动装配 bean 的属性,可以实现依赖注入,避免手动创建和设置对象。
Spring Boot 是一个用于简化 Spring 应用程序开发的框架,它集成了各种常用的开发配置和工具,使得开发者可以更快速、更轻松地创建独立运行的、生产级别的 Spring 应用。
Spring Boot 简化了 Spring 应用程序的开发、部署和维护过程,提供了自动化配置、快速开发的特性,让开发者可以更专注于业务逻辑而不是繁琐的配置。
Spring Boot 的核心配置文件是 application.properties 或 application.yml ,用于配置应用程序的各种属性和特性。
Spring Boot 的配置文件有 application.properties 和 application.yml 两种类型,区别在于语法和格式。 .properties 使用键值对, .yml 使用缩进格式。
Spring Boot 实现热部署的方式包括使用开发工具(如 Spring Boot DevTools )和使用Spring Boot 的热部署插件(如 spring-boot-starter-thymeleaf )。
JPA ( Java Persistence API )是 Java EE 标准中的一部分,定义了持久层的 API 接口;Hibernate 是一个 JPA 的实现,是一个 ORM (对象关系映射)框架,用于将 Java 对象映射到数据库表。
Spring Cloud 是基于 Spring Boot 的微服务架构开发工具,提供了一套开发分布式系统的解决方案,包括配置管理、服务发现、负载均衡、断路器等功能。
Spring Cloud 断路器用于处理分布式系统中的故障和超时问题,当某个微服务不可用时,断路器可以阻止请求继续发送,从而避免系统级联故障。
Spring Cloud 的核心组件包括服务注册与发现( Eureka 、 Nacos )、配置中心( Nacos )、负载均衡( Ribbon )、断路器( Hystrix 、 sentinel )、 API 网关( Gateway )等。
#{} 是预编译的占位符, MyBatis 会将其转化为一个占位符参数,安全性较高,可以防止 SQL注入;${} 是字符串替换,直接将内容替换到 SQL 语句中,不会进行参数处理,潜在风险是SQL注入。
MyBatis 有两种分页方式,一种是使用 RowBounds 进行内存分页,另一种是使用插件进行物理分页。
是的, RowBounds 方式是一次性查询全部结果, MyBatis 会将整个结果集读取到内存中,然后进行分页操作。
逻辑分页是在数据库中查询所有结果,然后在应用层进行分页;物理分页是通过数据库的特 定语法(如 LIMIT 、 OFFSET )进行分页,只查询所需数据。
是的, MyBatis 支持延迟加载。延迟加载是指在需要时才真正查询关联对象,原理是在查询主对象时只查询主对象的数据,当访问关联对象属性时,再根据需要进行关联查询。
一级缓存是指在同一个 SqlSession 中,查询的结果会被缓存起来,以提高性能;二级缓存是指多个 SqlSession 之间共享缓存,可以跨 Session 共享数据。
MyBatis 是基于 SQL 和映射配置的持久化框架,需要手写 SQL ,更加灵活; Hibernate 是ORM 框架,将 Java 对象映射到数据库,不需要写 SQL ,更加面向对象。
MyBatis 有三种执行器,分别是 SimpleExecutor 、 ReuseExecutor 和 BatchExecutor ,用于控制 SQL 语句的执行。
MyBatis 分页插件通过拦截 SQL 执行,将原始 SQL 改写为带有分页参数的 SQL ,然后执行修改后的 SQL ,最终返回分页结果。
编写 MyBatis 自定义插件需要实现 Interceptor 接口,然后通过在 MyBatis 配置文件中配置插件,使其生效。插件可以在 SQL 执行前后进行拦截,实现自定义的功能。
RabbitMQ 适用于异步通信、消息分发、任务队列、发布 / 订阅、 RPC 等场景,可以实现解耦、异步处理、高可用性等。
RabbitMQ 的重要角色包括生产者( Producer )、交换机( Exchange )、队列 (Queue )、消费者( Consumer )。
RabbitMQ 的重要组件包括交换机( Exchange )、队列( Queue )、绑定( Binding )、连接( Connection )、信道( Channel )等。
虚拟主机( vhost )是 RabbitMQ 提供的逻辑隔离机制,可以让多个应用在同一个 RabbitMQ服务器上使用独立的虚拟环境,各自独立管理交换机、队列等。
消息从生产者发送到交换机,然后根据交换机的规则被分发到一个或多个队列,消费者从队列中获取消息进行处理。
保证消息的稳定性可以通过消息确认机制、持久化、事务等方式来实现,确保消息被成功发送和存储。
避免消息丢失的方法包括使用持久化队列和消息、开启消息确认机制、设置备份交换机等。
要保证消息持久化成功,需要将消息设置为持久化,同时队列和交换机也需要进行持久化配置。
RabbitMQ 持久化会增加系统的开销,降低系统的吞吐量,影响性能。
RabbitMQ 的广播类型包括扇形交换机( fanout exchange )广播、主题交换机( topicexchange )广播等。
RabbitMQ 可以通过插件或自定义代码实现延迟消息队列,常见的方式是使用插件rabbitmq_delayed_message_exchange 。
RabbitMQ 集群可以提高可用性和性能,实现消息的高可靠、高可用、负载均衡等。
RabbitMQ 节点的类型包括内存节点( RAM Node )和磁盘节点( Disk Node )。
搭建 RabbitMQ 集群需要注意网络配置、节点命名、镜像队列设置、负载均衡等问题。
不是, RabbitMQ 的集群中每个节点都包含完整的元数据信息,但不是其他节点的完整拷贝,各节点之间通过复制元数据实现数据同步。
如果唯一一个磁盘节点崩溃,会导致无法提供磁盘存储的功能,影响消息的持久化。
是的, RabbitMQ 对集群节点的停止顺序有要求,应先停止镜像队列,再停止从节点,最后停止主节点。
数据库的三范式是指数据库设计的一种规范,用来规定如何将数据进行组织和存储,以便减少冗余、提高数据的一致性和准确性。具体来说,三范式分为三个层次:
- 第一范式(1NF)要求每个表中的每个字段都是原子性的,不可再分。即每个字段中不能包含多个值或者数组。
- 第二范式(2NF)要求表中的非主键字段完全依赖于主键,也就是说,非主键字段必须完全取决于整个主键,而不是只取决于部分主键。
- 第三范式(3NF)要求表中的非主键字段不依赖于其他非主键字段。换句话说,任何字段不能由其他字段派生出来,而应该直接与主键相关。
在 MySQL 中,自增字段(比如 AUTO_INCREMENT )的值是在表创建的时候确定的,而且重启数据库并不会重置自增字段的值。所以,重启后插入的数据的自增字段值会从上一次插入的最 大值继续递增,不会回到初始值。因此,在这个情况下,新插入的数据的 id 取决于之前最大的 id 值,加上重启后新插入的数据。
可以通过执行 SQL 语句 SELECT VERSION() ; 来获取当前数据库的版本信息。这将返回一个包 含数据库版本信息的结果集。
ACID 是数据库事务的四个关键特性,用于确保事务的正确执行和数据的可靠性:
- 一致性(Consistency):事务执行前后,数据库的状态应该保持一致。
- 隔离性(Isolation):并发执行的事务之间应该互不干扰,各自独立进行。
- 持久性(Durability):事务一旦提交,其结果应该被永久保存,不受系统故障影响。
- 原子性(Atomicity):事务是一个原子操作,要么全部执行,要么全部不执行。
CHAR 和 VARCHAR 是用于存储字符数据的数据类型,它们的主要区别在于存储方式和存储长 度:VARCHAR 可变长度: VARCHAR 类型会根据实际存储的数据长度来动态分配存储空间,更加节 省存储空间。CHAR 固定长度: CHAR 类型会固定占用指定长度的存储空间,不管实际存储的数据长度。如 果存储的字符数少于指定长度,会在后面用空格填充。
FLOAT 和 DOUBLE 是用于存储浮点数的数据类型,它们的主要区别在于精度和存储范围:DOUBLE :双精度浮点数,占用 8 字节,精度约为 15 位有效数字,适用于存储较大范围和更高 精度的浮点数。FLOAT :单精度浮点数,占用 4 字节,精度约为 7 位有效数字,适用于存储较小范围的浮点数。
这三种连接是用于联合查询多个表的方式,它们之间的区别在于返回的结果集的不同:
- 左连接(LEFT JOIN):返回左表的全部记录和右表中符合连接条件的交集。
- 右连接(RIGHT JOIN):返回右表的全部记录和左表中符合连接条件的交集。
- 内连接(INNER JOIN):返回两个表中符合连接条件的交集。
MySQL 索引的底层实现常用的数据结构是 B+ 树,这是一种平衡树结构。 B+ 树的特点是每个节点可以存储多个键值对,同时维护了一个有序的叶子节点链表,使得范围查询非常高效。
可以通过使用 EXPLAIN 关键字分析 SQL 语句的执行计划来验证 MySQL 的索引是否满足需 求。执行 EXPLAIN 后,可以查看查询的执行计划,包括使用的索引、访问类型等信息,从而判断是 否需要调整索引。
事务隔离是指在多个事务并发执行时,保证各个事务之间互不干扰,从而保证数据的一致性。数据库的事务隔离级别包括四个级别: READ UNCOMMITTED 、 READ COMMITTED 、 REPEATABLE READ 和 SERIALIZABLE ,随着隔离级别的提高,隔离性越强,但并发性越差。
MySQL 常用的存储引擎包括 InnoDB 、 MyISAM 、 MEMORY 、 CSV 等。其中, InnoDB 支持事务和外键,具有 ACID 特性,适合于事务处理; MyISAM 不支持事务,但在读密集型应用中性能较好;MEMORY 引擎将数据存储在内存中,适用于高速读写操作。
MySQL 支持行级锁和表级锁,区别如下:
- 表锁:锁定整个表,会影响整个表的操作。适用于少量更新操作或需要保证数据一致性的情况。
- 行锁:只锁定操作的行,不影响其他行的操作。适用于并发性能要求较高的情况。
乐观锁和悲观锁是并发控制的两种策略:
- 悲观锁:假设并发冲突的可能性较高,先加锁再进行操作,保证操作的独占性。适用于对数据 一致性要求较高的情况。
- 乐观锁:假设并发冲突的可能性很低,不加锁进行操作,只在操作完成后根据数据版本等校验数据的一致性。常用的方式有CAS(Compare and Swap)等。
MySQL 问题排查可以使用以下手段:
- 执行计划分析:使用 EXPLAIN 关键字分析SQL语句的执行计划,查看索引使用情况。
- 锁信息查看:使用 SHOW ENGINE INNODB STATUS 查看当前锁信息,了解是否有锁等待问题。
- 系统状态查看:使用 SHOW STATUS 查看MySQL的系统状态信息,了解系统的负载情况。
- 慢查询分析:使用 SHOW VARIABLES 查看 slow_query_log 和 long_query_time 设置,分析慢查询日志。
- 查看错误日志:查看MySQL的错误日志,了解是否有异常或错误发生。
MySQL 性能优化的方法有很多,可以从以下几个方面入手:
- 使用索引:根据查询的情况添加适当的索引,加快数据检索速度。
- 优化 SQL 语句:编写高效的 SQL 语句,避免全表扫描、使用合适的连接方式等。
- 适当分区:根据数据的特点进行分区存储,提高查询效率。
- 控制并发:合理设置事务隔离级别、避免锁冲突、使用乐观锁等。
- 配置优化:根据硬件资源和负载情况,适当调整MySQL的配置参数。
- 定期维护:定期清理无用数据、优化索引、重新分析表等,保持数据库的良好状态。
- 合理设计表结构:避免冗余字段和多余的关联表,减少查询的复杂性。
Redis ( Remote Dictionary Server )是一个开源的内存数据存储系统,它支持键值对存储,并提供了多种数据结构如字符串、哈希、列表、集合、有序集合等。Redis 主要用于高速缓存、会话存储、排行榜、实时消息发布订阅等场景。
Redis 具有以下主要功能:
- 数据持久化:支持将内存数据持久化到硬盘,确保数据不会丢失。
- 分布式数据存储:支持分布式集群部署,实现数据的高可用和负载均衡。
- 发布订阅:支持实时消息的发布和订阅。
- 数据结构支持:提供多种数据结构,如字符串、哈希、列表、集合、有序集合等,方便存储不 同类型的数据。
- 事务支持:支持基于MULTI、EXEC、WATCH等命令的事务操作。
- 位图计算、地理位置计算等特定功能。
- 内存缓存:将热点数据存储在内存中,加速读取速度。
Redis 和 Memcached 都是内存中的键值存储系统,但有一些区别:数据持久化: Redis 支持数据的持久化,可以将数据保存到硬盘中,而 Memcached 不支持数据持久化。数据安全: Redis 支持数据的备份和恢复,支持主从复制和数据分片,保证数据安全性和高可用性。单线程: Redis 是单线程的,通过事件循环实现高效的并发处理,而 Memcached 是多线程的。扩展性: Redis 支持分布式集群部署,支持更多的扩展性和高可用性。数据结构: Redis 支持更多的数据结构,如哈希、有序集合等, Memcached 只支持简单的键值对。
Redis 之所以采用单线程的方式,是因为它主要用于高速缓存和高速读取的场景。单线程可以避免多线程之间的上下文切换开销,同时通过事件循环( Event Loop )机制实现高效的并发处理。在典型的缓存场景下,CPU 并不是瓶颈,而是内存和网络带宽。单线程可以更好地利用 CPU 缓 存,同时降低了线程同步和锁的开销。
缓存穿透是指恶意请求访问缓存中不存在的数据,导致每次请求都需要访问数据库,增加了数据库的负担。解决缓存穿透问题的方式有:空值缓存:如果数据库中不存在某个键的值,将其对应的缓存设置为空值,避免重复查询。预热缓存:系统启动时预先将热点数据加载到缓存中,减少冷启动时的穿透情况。布隆过滤器:在缓存层引入布隆过滤器,用于过滤掉不存在的请求,从而避免访问数据库。
Redis 支持多种数据类型,包括字符串( String )、哈希( Hash )、列表( List )、集合( Set )、有序集合( Sorted Set )等。
Redis 支持多种 Java 客户端,常用的有 Jedis 、 Lettuce 、 Redisson 等。
Jedis 和 Redisson 都是 Redis 的 Java 客户端,区别如下:
- 功能支持:Redisson支持分布式锁、分布式集合、分布式对象等更丰富的分布式特性。
- 性能:Redisson在某些场景下性能更优,如分布式锁的实现。
- 实现方式:Jedis使用直连方式,Redisson使用Netty框架,支持更多的特性。
保证缓存和数据库数据一致性的方式有:Write-Through 模式:数据写入时,先写入数据库,再写入缓存。Write-Behind 模式:数据写入时,先写入缓存,然后异步写入数据库。Cache-Aside 模式:数据读取时,先从缓存读取,如果不存在再从数据库读取,并将数据写入 缓存。
Redis 支持两种持久化方式: RDB 快照和 AOF 日志文件。
Redis 可以通过以下方式实现分布式锁:使用 RedLock 算法:多个 Redis 节点组合使用,通过竞争锁来达到分布式锁的效果。使用 SETNX 命令:利用 SETNX ( SET if Not eXists )命令尝试将锁的值设置为特定值,成功表示获取锁。
Redis 分布式锁可能存在以下缺陷:
- 锁过期问题:如果持有锁的客户端执行时间过长,锁可能过期,其他客户端获取到锁。
- 高并发问题:在高并发场景下,竞争锁可能导致性能下降。
- 死锁问题:如果持有锁的客户端在执行期间发生故障,可能导致死锁。
Redis 可以通过以下方式进行内存优化:
- 合并小对象:将多个小对象合并为一个大对象,减少存储空间。
- 选择合适的数据结构:根据数据的特点选择合适的数据结构,减少冗余。
- 删除过期数据:使用TTL设置过期时间,自动删除过期数据。
- 分区存储:将不同的数据类型分开存储,提高数据存储效率。
- 压缩数据:对于存储的数据进行压缩,减少内存占用。
Redis 有以下几种淘汰策略:
- allkeys-lru:最近最少使用的键会被淘汰。
- noeviction:当内存不足以容纳新写入数据时,新写入操作会报错。
- volatile-lru:在设置了过期时间的键中,最近最少使用的键会被淘汰。
- allkeys-random:随机淘汰一个键。
- volatile-random:在设置了过期时间的键中,随机淘汰一个键。
- volatile-ttl:在设置了过期时间的键中,根据键的过期时间进行淘汰。
Redis 常见的性能问题包括内存占用过高、 CPU 占用过高、响应延迟等,解决方法有:
- 使用持久化方式:选择合适的持久化方式,避免数据丢失。
- 优化数据结构:选择合适的数据结构,减少内存占用。
- 避免大批量操作:避免一次性大批量读写操作,分批进行。
- 使用集群部署:通过分布式部署提高性能和可用性。
- 使用连接池:避免频繁创建和关闭连接,提高性能。
- 监控和调优:监控性能指标,根据情况调整配置
- 合理设置过期时间:对于热点数据设置适当的过期时间,避免内存占用过高。
JVM ( Java Virtual Machine )主要由以下组成部分构成运行时数据区(Runtime Data Area ):包括方法区、堆、栈、本地方法栈和程序计数器等, 用于存储程序运行时的数据和执行过程中的信息。
- 执行引擎(Execution Engine):负责执行字节码指令,将字节码翻译成机器码并执行。
- 本地方法接口(Native Interface):与底层操作系统交互的接口,可以调用本地库中的方法。
- 垃圾回收系统(Garbage Collector):负责自动回收不再使用的内存,防止内存泄漏。
- 类库(Java API Libraries):包含 Java 标准库中的各种类和接口,提供丰富的功能和工具。
- 类加载器(Class Loader):负责加载字节码文件,将其加载到内存中,生成对应的 Class 对象。
JVM 运行时数据区包括:
- 堆(Heap):存储对象实例和数组,是Java程序中动态分配的最大一块内存区域。
- 栈(Stack):存储局部变量、方法参数、操作数栈、返回值等,以及方法的调用和返回信息。
- 本地方法栈(Native Method Stack):为本地方法服务,类似于栈,但用于本地方法的调用。
- 程序计数器(Program Counter):用于指示当前线程执行的字节码指令的地址。
- 方法区(Method Area):存储类的元数据、常量、静态变量、编译后的代码等。
堆和栈是 JVM 运行时数据区的两个重要部分,区别如下:生存周期:堆中的对象生命周期由垃圾回收器管理,栈中的数据随方法的调用和返回而创建和销毁。内存分配:堆中的内存动态分配,栈中的内存分配是静态的。大小:堆通常比栈大,因为堆需要存储所有对象实例和数组,而栈只需要存储方法调用信息。多线程:堆可以被多个线程共享,栈为每个线程独立分配。内容:堆用于存储对象实例和数组,栈用于存储局部变量、方法调用信息等。
队列和栈都是数据结构,区别如下:栈( Stack ):后进先出( LIFO )的数据结构,类似于堆叠,新元素压入栈顶,从栈顶弹出元素。队列( Queue ):先进先出( FIFO )的数据结构,类似于排队,新元素加入队尾,从队头移除元素。区别:队列和栈在数据添加和移除顺序上的区别,以及对应的操作。
双亲委派模型是指在类加载器加载类时,优先将请求交给父类加载器处理,只有在父类加载器无法找到类时,才由子类加载器自行加载。这样可以保证类的加载是从上至下的层次关系,避免 类的重复加载和冲突。
类加载的执行过程包括:加载( Loading ):根据类的全限定名查找并加载对应的字节码文件。验证( Verification ):对加载的字节码进行验证,确保字节码文件合法且没有恶意代码。准备( Preparation ):为类的静态变量分配内存并设置默认初始值。解析( Resolution ):将类的符号引用转换为直接引用,以便调用类的方法和字段。初始化( Initialization ):执行类的初始化代码,包括静态代码块和静态变量初始化。
对象是否可以被回收主要通过垃圾回收器判断,常用的判断条件有:可达性分析:通过根对象遍历判断对象是否可从根对象访问到,如果不可达,则可以被回收。终结器( Finalizer ):对象的终结器被调用后,会加入到即将被回收的对象列表中。引用计数:通过引用计数判断对象是否没有引用指向。
Java 中的引用类型包括:强引用( Strong Reference ):最常见的引用类型, GC 时不会被回收。软引用( Soft Reference ):内存不足时才会被回收。弱引用( Weak Reference ):只要 GC 发生,就会被回收。虚引用( Phantom Reference ):不能通过虚引用访问对象,用于监控对象被回收的情况。
JVM 的垃圾回收算法包括:标记 - 清除算法( Mark-Sweep ):标记未使用对象,然后清除这些对象。复制算法( Copy ):将内存分为两个区域,一次只使用其中一块,当一块用完时,将存活的对象复制到另一块。标记 - 整理算法( Mark-Compact ):标记未使用对象,然后将存活的对象移动到内存的一端,然后清除未使用的空间。分代收集算法:根据对象的生命周期将内存划分为不同的代,针对不同代使用不同的垃圾回收 算法。
JVM 的垃圾回收器包括:
- Serial收集器:新生代收集器,使用复制算法。
- Parallel收集器:新生代收集器,使用复制算法,多线程并行执行。
- CMS收集器:老生代收集器,使用标记-清除算法,以最短停顿时间为目标。
- G1收集器:新生代和老生代收集器,使用分代收集算法,以低延迟为目标。
CMS ( Concurrent Mark-Sweep )是一种老生代收集器,具有并发标记和并发清除的特点。它的主要流程包括:
- 初始标记(Initial Mark):标记GC Roots直接关联的对象,暂停应用程序的线程。
- 并发标记(Concurrent Mark):并发标记整个老生代,不暂停应用程序。
- 重新标记(Remark):标记在并发标记阶段发生变化的对象,暂停应用程序。
- 并发清除(Concurrent Sweep):并发清除未使用的对象,不暂停应用程序。
- 并发重置(Concurrent Reset):重置CMS内部的数据结构,不暂停应用程序。
常见的新生代垃圾回收器包括 Serial 收集器、 Parallel 收集器、 G1 收集器;常见的老生代垃圾回收器包括 CMS 收集器、 G1 收集器。区别如下:
- 新生代:主要用于存放新创建的对象,通常采用复制算法,回收速度较快,但内存较小。
- 老生代:主要用于存放长生命周期的对象,通常采用标记-清除算法,回收速度较慢,内存较大。
- G1收集器:可以同时用于新生代和老生代,通过分区划分内存,以低延迟为目标。
分代垃圾回收器根据对象的生命周期将内存划分为不同的代,一般分为新生代和老生代。新生代使用复制算法,适合频繁创建和销毁的对象,老生代使用标记 - 清除算法,适合长生命周期的 对象。新生代的回收速度快,老生代的回收速度较慢,通过不同的回收策略实现内存的高效使用。
JVM 调优的工具包括:jconsole :图形化的监控工具,用于查看内存、线程、垃圾回收等信息。jvisualvm :全能性监控和性能分析工具,包含多个插件和功能。jmap :用于生成堆转储快照,分析内存使用情况。jstat :用于查看垃圾回收、内存使用等统计信息。jstack :用于生成线程转储快照,分析线程问题。
常用的 JVM 调优参数包括:-Xmx :设置最大堆内存。-Xms :设置初始堆内存。-Xmn :设置新生代内存。-XX:NewRatio :设置老生代和新生代的比例。-XX:MaxPermSize :设置最大永久代内存。-XX:SurvivorRatio :设置 Eden 区和 Survivor 区的比例。-XX:+UseParallelGC :使用 Parallel 收集器。-XX:+UseConcMarkSweepGC :使用 CMS 收集器。-XX:+UseG1GC :使用 G1 收集器。-XX:MaxGCPauseMillis :设置垃圾回收暂停时间的目标。注意: JVM 调优参数根据不同的应用和需求,需要根据具体情况进行选择和调整