Zer0e's Blog

2024面试复盘9

字数统计: 2.4k阅读时长: 8 min
2024/08/02 Share

前言

今天经历了一场长时间面试,接近快两小时,晚上又经历了一场,把我干蒙了,尤其是晚上那场,一共问了三个问题,我都没答上来。寄。。。

复盘

JAVA代码加载执行过程

涉及到类加载过程,根本记不住,就算文档在旁边我也答不上来。

Java代码的加载和执行过程涉及多个步骤,包括编译、类加载、链接、初始化和运行。

1. 编译阶段

Java源代码文件(.java)通过Java编译器(javac)编译成字节码文件(.class)。字节码是一种中间表示形式,它可以在Java虚拟机(JVM)上运行。

2. 类加载阶段

类加载阶段是将类的字节码加载到JVM内存中的过程。类加载器(ClassLoader)负责加载类。

  • 启动类加载器(Bootstrap ClassLoader):加载核心Java类库,如java.langjava.util等,通常从JVM的运行时环境(rt.jar)中加载。
  • 扩展类加载器(Extension ClassLoader):加载扩展类库,通常从lib/ext目录中加载。
  • 应用类加载器(Application ClassLoader):加载应用程序的类,通常从类路径(classpath)中加载。

3. 链接阶段

链接阶段将类的字节码合并到JVM中,并为其分配内存。链接阶段包括三个子阶段:

  • 验证(Verification):确保字节码符合JVM的规范,并且没有安全问题。
  • 准备(Preparation):为类的静态变量分配内存,并初始化为默认值。
  • 解析(Resolution):将常量池中的符号引用替换为直接引用。

4. 初始化阶段

初始化阶段是执行类构造器方法 <clinit> 的过程。类构造器方法 <clinit> 是由编译器自动生成的,它包含类静态变量的赋值和静态代码块的执行。

5. 执行阶段

在执行阶段,JVM开始执行应用程序的main方法。整个应用程序的执行过程由JVM的执行引擎来处理。执行引擎包括以下几个部分:

  • 解释器(Interpreter):逐行解释执行字节码。
  • 即时编译器(Just-In-Time Compiler, JIT):将热点代码(执行频繁的代码)编译成本地机器码,以提高执行效率。
  • 垃圾收集器(Garbage Collector, GC):管理内存,回收不再使用的对象。
1
2
3
4
5
public class Main {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}

假设我们有上述简单的Java代码,以下是详细的加载和执行过程

编译

  • 使用 javac Main.java 编译器将 Main.java 编译为 Main.class 字节码文件。

类加载

  • JVM启动时,启动类加载器首先加载核心类库。
  • 应用类加载器加载 Main 类的字节码,将其放入方法区,并创建一个 Class 对象表示 Main 类。

链接

  • 验证:JVM验证 Main.class 字节码是否合法。
  • 准备:为 Main 类的静态变量分配内存并初始化为默认值(此例中没有静态变量)。
  • 解析:将常量池中的符号引用解析为直接引用。

初始化

  • 执行 Main 类的静态初始化代码(如有),包括静态变量的初始化和静态代码块(此例中没有静态初始化代码)。

执行

  • JVM调用 Main 类的 main 方法。
  • 解释器解释执行 main 方法的字节码,将 Hello, World! 打印到控制台。
  • JIT编译器在执行频繁的代码时将其编译为本地机器码,以提高执行效率。

JAVA中能否加载相同类名?自定义java.lang.String能否加载?

在Java中,可以加载具有相同类名的类,但需要通过不同的类加载器来实现。每个类加载器都有其独立的命名空间,因此可以加载同名类而不冲突。这种机制在实现Java应用程序中的模块化、插件化和动态加载时非常有用。

类加载器:负责加载类文件到JVM内存中。Java中的类加载器包括引导类加载器(Bootstrap ClassLoader)、扩展类加载器(Extension ClassLoader)和应用类加载器(Application ClassLoader)。还可以自定义类加载器。

命名空间:每个类加载器都有自己的命名空间,包含其加载的所有类。不同类加载器的命名空间是独立的,这意味着两个不同类加载器可以加载同名的类而不冲突。

简单来说就是使用不同的类加载器来加载类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class ClassLoaderTest {
public static void main(String[] args) {
try {
// Load MyClass from dir1
CustomClassLoader classLoader1 = new CustomClassLoader("bin/dir1/");
Class<?> class1 = classLoader1.loadClass("com.example.MyClass");
Object obj1 = class1.newInstance();
class1.getMethod("print").invoke(obj1);

// Load MyClass from dir2
CustomClassLoader classLoader2 = new CustomClassLoader("bin/dir2/");
Class<?> class2 = classLoader2.loadClass("com.example.MyClass");
Object obj2 = class2.newInstance();
class2.getMethod("print").invoke(obj2);

} catch (Exception e) {
e.printStackTrace();
}
}
}

在Java中,自定义 java.lang.String 类并尝试加载它是非常困难的,因为 java.lang.String 是由引导类加载器(Bootstrap ClassLoader)加载的核心类。引导类加载器是JVM的一部分,用于加载核心Java类库,它有最高的优先级并且不允许由其他类加载器覆盖这些核心类。

引导类加载器的优先级:引导类加载器首先加载核心类库,包括 java.lang.String。由于它是最先被加载的类加载器,并且在JVM启动时就已经加载了核心类,其他类加载器不能覆盖或重新定义这些类。

双亲委派模型:Java的类加载器遵循双亲委派模型,即当一个类加载器加载类时,它首先委派给其父类加载器。如果父类加载器已经加载了这个类,子类加载器就不会再加载它。由于 java.lang.String 是由引导类加载器加载的,所有其他类加载器在加载 java.lang.String 时都会被委派给引导类加载器,从而无法加载自定义的 java.lang.String

虽然理论上可以通过一些非常规手段尝试加载自定义的 java.lang.String 类,但在标准JVM实现中,这样的操作通常会失败或导致异常。以下是一些可能的方式和其局限性:

  1. 通过自定义类加载器:即使你编写了一个自定义类加载器并尝试加载自定义的 java.lang.String,由于双亲委派模型,最终还是会委派给引导类加载器。
  2. 修改引导类路径:可以尝试将自定义的 java.lang.String 放在引导类路径上,但这需要修改JVM的启动参数,且一般来说这不被推荐,因为它会破坏JVM的稳定性和安全性。

mysql也能存储json,为什么mongo比mysql快

1. 数据模型和存储机制

MongoDB

  • MongoDB 是一个文档数据库,设计上天然支持 JSON(实际上是 BSON,Binary JSON)。
  • 数据以文档的形式存储,每个文档是一个键-值对的集合,可以嵌套和具有多样的结构。
  • MongoDB 的文档模型更接近 JSON 的原始结构,因此存储和读取 JSON 数据时更加高效。

MySQL

  • MySQL 是一个关系型数据库,虽然它支持 JSON 数据类型,但其设计主要针对结构化数据。
  • MySQL 在存储 JSON 数据时,通常将其作为字符串处理,需要额外的解析和转换。
  • 对 JSON 数据的查询和操作需要额外的步骤,如解析 JSON 字符串,这会影响性能。

2. 索引和查询优化

MongoDB

  • MongoDB 支持在文档中的嵌套字段上创建索引,这使得对嵌套 JSON 结构的查询更高效。
  • MongoDB 的查询语言和引擎专为文档结构设计,能够直接对 BSON 结构进行操作,无需额外的解析步骤。
  • 由于设计上的优势,MongoDB 在进行复杂的嵌套查询时性能更好。

MySQL

  • MySQL 也支持对 JSON 字段创建索引,但其索引机制和查询优化主要为关系型数据设计。
  • 查询 JSON 数据时,MySQL 需要先解析 JSON 字符串,然后再执行查询,增加了额外的开销。
  • 对嵌套 JSON 结构的查询在 MySQL 中较为复杂,可能需要使用函数和额外的步骤来解析和提取数据。

3. 数据更新和写入

MongoDB

  • MongoDB 的写入操作设计上更加灵活,支持文档的部分更新。
  • 对于 JSON 数据的更新,MongoDB 能够直接修改文档中的嵌套字段,而无需整体替换。
  • 这种灵活性使得对 JSON 数据的写入和更新操作更高效。

MySQL

  • MySQL 对 JSON 数据的更新通常需要解析和重新构建整个 JSON 字符串,尤其是对于嵌套结构。
  • 这种操作增加了写入和更新的开销,导致性能下降。

4. 并发处理和扩展性

MongoDB

  • MongoDB 的架构设计为分布式存储和高并发处理进行了优化。
  • 通过分片和复制集机制,MongoDB 能够轻松扩展以应对大规模的数据量和高并发请求。

MySQL

  • MySQL 也支持分布式和集群架构,但其扩展性主要针对关系型数据。
  • 对于大规模 JSON 数据的处理,MySQL 的性能和扩展性可能不如 MongoDB。

总结

这次真的一问三不知,另一场两小时面试基础问题比较多,周末整理下看看再发一篇。

CATALOG
  1. 1. 前言
  2. 2. 复盘
    1. 2.1. JAVA代码加载执行过程
      1. 2.1.1. 1. 编译阶段
      2. 2.1.2. 2. 类加载阶段
      3. 2.1.3. 3. 链接阶段
      4. 2.1.4. 4. 初始化阶段
      5. 2.1.5. 5. 执行阶段
    2. 2.2. JAVA中能否加载相同类名?自定义java.lang.String能否加载?
    3. 2.3. mysql也能存储json,为什么mongo比mysql快
      1. 2.3.1. 1. 数据模型和存储机制
      2. 2.3.2. 2. 索引和查询优化
      3. 2.3.3. 3. 数据更新和写入
      4. 2.3.4. 4. 并发处理和扩展性
  3. 3. 总结