Java面试题、八股文——JVM篇最终篇
1.如何选择垃圾收集器?
选择合适的垃圾收集器(Garbage Collector, GC)对于优化Java应用程序的性能至关重要。不同的应用场景和系统需求可能需要不同类型的垃圾收集器来满足。以下是一些考虑因素以及常见的垃圾收集器选项,帮助您做出选择:
考虑因素:
- 应用响应时间:如果您正在构建一个需要低延迟的应用程序,例如在线交易系统或实时游戏,那么您可能需要选择能够最小化暂停时间的垃圾收集器。
- 吞吐量:对于后台处理或者批处理任务来说,高吞吐量比低延迟更重要,这意味着您可以接受更长的垃圾回收暂停时间以换取更高的整体效率。
- 内存占用:如果您的应用运行在资源受限的环境中,如嵌入式系统,则需要考虑垃圾收集器对内存使用的影响。
- 堆大小:不同垃圾收集器对大堆和小堆的支持程度不同,选择时需要考虑您的应用预计使用的堆大小。
- 并发性:现代垃圾收集器通常支持一定程度的并发操作,以减少对应用线程的影响。
常见的垃圾收集器:
- Serial Collector:适合于客户端场景,特别是小型应用或嵌入式系统。它使用单个线程进行垃圾回收,适用于单核处理器或内存有限的环境。
- Parallel Collector(也称作Throughput Collector):通过多线程并行执行垃圾回收,旨在最大化应用的整体吞吐量。适合于多核处理器上的后台处理或批处理应用。
- Concurrent Mark Sweep (CMS) Collector:旨在减少垃圾回收引起的暂停时间,适合于对响应时间有较高要求的应用。但它的缺点是可能会产生更多的内存碎片,并且在某些情况下会降低吞吐量。
- G1 Collector(Garbage First):设计用于具有大堆的多核机器上运行的应用。它将堆划分为多个区域,并优先回收那些垃圾最多的区域,从而试图平衡吞吐量和响应时间。
- ZGC 和 Shenandoah Collector:这两个都是实验性的垃圾收集器,设计目标是在大型堆上实现非常低的暂停时间(通常小于10毫秒)。它们特别适合于需要极低延迟的高性能应用。
如何选择:
- 评估需求:首先明确您的应用对延迟、吞吐量、内存使用等方面的要求。
- 测试与比较:在实际环境中测试不同的垃圾收集器,观察其对应用性能的影响。
- 调优参数:根据测试结果调整垃圾收集器的相关参数,比如初始堆大小、最大堆大小等。
- 持续监控:即使选择了垃圾收集器,也需要定期监控应用的性能指标,确保其始终符合预期。
2. 什么是类加载器?
类加载器(Class Loader)是Java虚拟机(JVM)的一部分,负责将字节码文件(.class 文件)加载到内存中,并转换成可以被虚拟机执行的方法区中的运行时数据结构。类加载器在Java中扮演着至关重要的角色,因为它不仅决定了应用程序如何访问类文件,还影响了类的可见性和安全性。
类加载器的工作机制
- 加载:类加载器从文件系统、网络或其他来源读取类的二进制数据,并将其转换为方法区中的运行时数据结构。
- 链接:包括验证、准备和解析三个步骤。
- 验证:确保加载的类文件格式正确,没有安全问题。
- 准备:为类的静态变量分配内存,并设置默认值。
- 解析:将符号引用转换为直接引用,这个过程涉及到查找和替换类、接口、字段和方法的符号引用。
- 初始化:执行类构造器 () 方法,即初始化类的静态变量和静态代码块。
类加载器的层次结构
Java中的类加载器采用了一种称为“双亲委派模型”的层级结构。这种模型确保了类加载过程的安全性和一致性。
- Bootstrap ClassLoader:这是最顶层的类加载器,由C++编写,不是Java类的一部分。它负责加载核心Java类库(如 java.lang.*),位于 rt.jar 中。
- Extension ClassLoader(扩展类加载器):负责加载标准扩展库中的类,通常位于 $JAVA_HOME/lib/ext 目录下。
- Application ClassLoader(应用类加载器):也称为系统类加载器,负责加载应用程序的类路径(classpath)中指定的类文件。
自定义类加载器
除了上述内置的类加载器外,开发人员还可以创建自定义类加载器来实现特定的功能,如从网络加载类、加密类文件等。自定义类加载器通常继承自 java.lang.ClassLoader 类,并重写 findClass() 或 loadClass() 方法。
双亲委派模型
双亲委派模型是指当一个类加载器收到类加载请求时,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一层的类加载器都是如此,因此所有的加载请求最终都应该传送到最顶层的Bootstrap类加载器中。只有当父类加载器无法加载该类(即在其加载范围之外)时,子加载器才会尝试自己加载。
这种模型的好处在于:
- 安全性:防止用户自定义的类伪装成核心类库中的类,因为核心类库总是先被加载。
- 避免重复加载:保证所有加载器之间的类加载协调一致,避免同一个类被多次加载。
3.什么是 tomcat 类加载机制?
Tomcat 是一个广泛使用的开源 Servlet 容器,它支持 Java Servlet 和 JavaServer Pages (JSP) 技术。Tomcat 的类加载机制是其核心功能之一,它确保了不同Web应用程序之间的隔离性和安全性。以下是关于 Tomcat 类加载机制的详细介绍:
类加载器层次结构
Tomcat 采用了类似于 Java 标准的双亲委派模型,但为了适应 Web 应用程序的需求,增加了一些额外的类加载器。以下是 Tomcat 类加载器的层次结构:
- Bootstrap ClassLoader:
- 这是最顶层的类加载器,负责加载核心的 Java 类库,如 java.lang.*。
- 通常由 JVM 提供,不是 Java 类的一部分。
- System ClassLoader(也称为 Application ClassLoader):
- 负责加载应用程序的类路径(classpath)中指定的类文件。
- 包括 JDK 的扩展库(如 $JAVA_HOME/lib/ext 目录下的类)。
- Common ClassLoader:
- 加载 $CATALINA_HOME/lib 目录下的类文件。
- 这些类对所有 Web 应用程序都是可见的。
- Catalina ClassLoader:
- 加载 $CATALINA_HOME/server 目录下的类文件。
- 这些类仅对 Tomcat 服务器内部使用,对 Web 应用程序不可见。
- Shared ClassLoader:
- 加载 $CATALINA_HOME/shared 目录下的类文件。
- 这些类对所有 Web 应用程序都是可见的,但不推荐使用,因为这可能导致类加载冲突。
- WebApp ClassLoader:
- 每个 Web 应用程序都有自己的 WebApp ClassLoader。
- 负责加载 Web 应用程序的 WEB-INF/classes 目录和 WEB-INF/lib 目录下的类文件。
- 这些类只对当前 Web 应用程序可见,实现了应用程序之间的隔离。
类加载顺序
Tomcat 的类加载顺序遵循以下规则:
双亲委派模型:
- 当一个类加载器收到类加载请求时,它首先会委托给父类加载器去加载。
- 只有当父类加载器无法加载该类时,子类加载器才会尝试加载。
WebApp ClassLoader 的特殊行为: - WebApp ClassLoader 在某些情况下会优先加载本地类,而不是委托给父类加载器。
- 这是为了允许 Web 应用程序覆盖 Tomcat 或 JDK 中的类,尽管这种做法并不推荐。
示例
假设有一个 Web 应用程序部署在 Tomcat 上,其类加载路径如下:
- Bootstrap ClassLoader:加载核心 Java 类库。
- System ClassLoader:加载 JDK 扩展库和应用程序类路径中的类。
- Common ClassLoader:加载 $CATALINA_HOME/lib 目录下的类。
- WebApp ClassLoader:加载 WEB-INF/classes 和 WEB-INF/lib 目录下的类。
当应用程序请求加载某个类时,类加载器的查找顺序如下:
- Bootstrap ClassLoader 尝试加载。
- 如果未找到,System ClassLoader 尝试加载。
- 如果未找到,Common ClassLoader 尝试加载。
- 如果未找到,WebApp ClassLoader 尝试加载。
注意事项
类加载冲突:
- 避免在 WEB-INF/lib 或 WEB-INF/classes 中包含与 Tomcat 或 JDK 冲突的类。
- 使用 shared 目录时要特别小心,因为这可能导致类加载冲突。
类加载器隔离:
- 每个 Web 应用程序的 WebApp ClassLoader 是独立的,确保了不同应用程序之间的类隔离。
性能优化:
- 合理组织类文件的位置,避免不必要的类加载,可以提高应用程序的启动和运行性能。
通过理解和合理配置 Tomcat 的类加载机制,可以有效地管理 Web 应用程序的类加载行为,确保应用程序的稳定性和性能。
原文地址:https://blog.csdn.net/guihong004/article/details/144092148
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!