前言
今天经历了一场长时间面试,接近快两小时,晚上又经历了一场,把我干蒙了,尤其是晚上那场,一共问了三个问题,我都没答上来。寄。。。
复盘
JAVA代码加载执行过程
涉及到类加载过程,根本记不住,就算文档在旁边我也答不上来。
Java代码的加载和执行过程涉及多个步骤,包括编译、类加载、链接、初始化和运行。
1. 编译阶段
Java源代码文件(.java)通过Java编译器(javac)编译成字节码文件(.class)。字节码是一种中间表示形式,它可以在Java虚拟机(JVM)上运行。
2. 类加载阶段
类加载阶段是将类的字节码加载到JVM内存中的过程。类加载器(ClassLoader)负责加载类。
- 启动类加载器(Bootstrap ClassLoader):加载核心Java类库,如
java.lang
、java.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 | public class Main { |
假设我们有上述简单的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 | public class ClassLoaderTest { |
在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实现中,这样的操作通常会失败或导致异常。以下是一些可能的方式和其局限性:
- 通过自定义类加载器:即使你编写了一个自定义类加载器并尝试加载自定义的
java.lang.String
,由于双亲委派模型,最终还是会委派给引导类加载器。 - 修改引导类路径:可以尝试将自定义的
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。
总结
这次真的一问三不知,另一场两小时面试基础问题比较多,周末整理下看看再发一篇。