Java面经(2)
1、JVM加载流程:
1. 解析命令行参数:解析用户启动JVM时传入的参数,配置运行时环境。-classpath:指定类搜索路径。-Xmx/-Xms:设置堆内存最大/初始大小。-XX:+UseG1GC:指定垃圾回收器类型。
2. 创建Bootstrap ClassLoader:引导类加载器是 JVM 的内置加载器,负责加载 Java 核心类库。
3. 加载核心类:引导类加载器首先加载 JVM 运行所需的基础类,如java.lang.Object
、java.lang.ClassLoader
等,这些类构成了 JVM 的运行基础。
4. 初始化运行时组件和内存区域:
内存区域划分:堆:对象实例存储区域(分新生代、老年代);方法区:类元数据、常量池等(JDK8后取代永久代);虚拟机栈:线程私有的方法调用栈帧;本地方法栈:Native方法调用栈;程序计数器:记录当前线程执行位置。
运行时组件初始化:垃圾回收器:根据参数初始化对应的GC实现(如G1、CMS)。即时编译器(JIT):初始化解释器和编译器(如C1、C2)。字节码执行引擎:负责解释或编译执行字节码。
5. 创建主线程与守护线程:主线程:由JVM自动创建,负责执行用户程序的main()方法。线程栈中压入main()方法的栈帧。
守护线程:后台服务线程(如GC线程、信号处理线程)。当所有非守护线程结束时,JVM自动退出。
6. 类加载验证(执行类加载过程):在执行main
方法前,JVM 需要加载并初始化包含main
方法的类
7. 压入main
方法栈帧:类加载完成后,JVM 通过主线程调用main
方法(public static void main(String[] args)
),程序开始运行。然后在虚拟机栈中为main
方法创建栈帧,执行字节码指令。
8. 运行时:JIT优化------将热点代码编译为本地机器码,提升执行效率。
9. 关闭阶段:JVM 退出的常见场景包括:正常退出:main
方法执行完毕,或调用System.exit(0)
。异常退出:程序抛出未捕获的异常,或调用System.exit(非0)。
强制终止:通过操作系统命令(如 kill 进程)终止 JVM 进程。
退出时,JVM 会执行以下操作:
执行所有已注册的shutdown hook
(通过Runtime.addShutdownHook()
注册的线程)。释放资源(如文件句柄、网络连接)。关闭 JVM 进程。
2、classloader有两个参数是啥:
应该指的是classloader的核心方法protected Class<?> loadClass(String name, boolean resolve)。分别是要加载的类的全限定名和一个标志参数用来指定是否需要解析
3、如何打破双亲委派:
1. 自定义类加载器并重写 loadClass()
方法:默认的 loadClass()
方法实现了双亲委派逻辑,若要打破该逻辑,需重写该方法,直接调用 findClass()
进行类加载,而不先委派给父类加载器。
2. 使用线程上下文类加载器(Thread Context ClassLoader):允许子模块(如 SPI 接口的实现类)通过当前线程的类加载器加载,而非父类加载器。
4、什么场景需要打破双亲委派?
1. 实现模块化或类隔离:在一个应用中需要加载同名但不同版本的类,或者不同模块需要隔离依赖库(避免类冲突)。典型的例子是Web容器(如Tomcat),每个Web应用可能需要独立的类加载环境。Tomcat为每个Web应用使用独立的WebAppClassLoader,优先加载自身路径下的类,若找不到再委派给父类加载器(打破了默认的“先父后子”顺序)
2. 热部署或热加载:在应用不重启的情况下,动态替换或重新加载类(如开发调试、线上热修复)。双亲委派模型中,类一旦被加载,除非重新创建类加载器,否则无法重新加载。为每个模块或需要热加载的组件创建独立的类加载器,直接加载类而不委派父类加载器。
5、cs 两个操作怎么保证原子性?
在 Java 中,CAS 通过 Unsafe
类中的 native
方法实现,这些方法调用底层的硬件指令来完成原子操作。由于其实现依赖于 C++ 内联汇编和 JNI 调用,因此 CAS 的具体实现与操作系统以及 CPU 密切相关。
6、execute和submit的区别
接口归属 |
|
|
任务类型 | 仅支持 | 支持 |
返回值 |
|
|
异常处理 | 任务内部异常需自行捕获,否则静默失败 | 异常会被封装在 |