[db:标题]

摘要:A公司一面:类加载的过程是怎么样的? 双亲委派的优点和缺点? 产生fullGC的情况有哪些? spring的动态代理有哪些?区别是什么? 如何排查CPU使用率过高?
摘要 A公司的面经 JVM的类加载的过程是怎么样的? 双亲委派模型的优点和缺点? 产生fullGC的情况有哪些? spring的动态代理有哪些?区别是什么? 如何排查CPU使用率过高? JVM的类加载的过程是怎么样的? 这个问题有些抽象,是指要说出具体步骤,还是要深入每一步的细节?再次确认一下范围,给出的回答是,你自己了解多少就说多少。这就有意思,那我就凭自己的语言进行总结发挥了。 简述 类加载,是指JVM将.class文件的数据加载到内存中,并进行校验、解析以及初始化等一系列操作后,最终生成可被JVM直接使用的数据的过程。 解释 我们知道一个类在JVM的生命周期大致可以分为7个阶段:加载、验证、准备、解析、初始化、使用、卸载。 类加载的过程,主要就是类生命周期的前5个阶段,所以类加载的主要步骤为: 加载、验证、准备、解析、初始化。 因为【验证】、【准备】、【解析】有时候被统一称为链接阶段,因此有时候类加载也会被分三个步骤:加载、链接、初始化。 加载(Loading) 第一步,加载,主要是通过类的全限定名(如:java.lang.String)获取类的二进制字节流,将字节流转换为JVM运行时的数据结构,在堆中生成一个 java.lang.Class 对象,作为该类的访问入口。 触发方式: ClassLoader.getSystemClassLoader().loadClass("com.jimoer.Test") Class.forName("com.jimoer.Test") // 加载并初始化 创建实例(new Test())、调用静态方法或访问静态字段。 验证(Verification) 主要是校验,.class文件的正确性。 校验类的正确性(文件格式,元数据,字节码,二进制兼容性),保证类的结构符合JVM规范。 准备(Preparation) 为类的 静态变量(static 字段)分配内存并设置 默认值。 这里只初始化类变量,即static变量,所以都是在方法区里面进行分配内存的。而实例变量是会在对象实例化的时候进行初始化的,并在Java堆里分配内存。 解析(Resolution) 将常量池中的 符号引用 转换为 直接引用。 把类的符号引用转为直接引用(类或接口、字段、类方法、接口方法、方法类型、方法句柄和访问控制修饰符7类符号引用)。 初始化(Initialization) 执行类的 初始化逻辑(即 <clinit>() 方法),完成静态变量赋值和静态代码块的执行。 public class ClassInit { static int a = 10; // 准备阶段:a = 0;初始化阶段:a = 10 static { a = 20; // 最终 a = 20 } } 双亲委派模型的优点和缺点? Java应用是由 启动类加载器(Bootstrap Class Loader)、扩展类加载器(Extension Class Loader)、应用程序类加载器(Application Class Loader),这三类加载器互相配合来完成加载的,如果有自定义的类加载器,会先执行自定义的类加载器。 各种的类加载器之间的层次关系被称为类加载器的“双亲委派模型(Parents Delegation Model)”。 双亲委派模型要求除了顶层的启动类加载器外,其余的类加载器都应有自己的父类加载器。 双亲委派模型的工作过程 如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到最顶端的启动类加载器中,只有当父加载器反馈自己无法完成这个加载请求(它的搜索范围中没有找到所需的类)时,子加载才会尝试自己去完成加载。 双亲委派模型的优点 避免类的重复加载。确保了不同类加载器加载的相同类是同一个实例,避免类型冲突(如java.lang.Object的唯一性)。 防止恶意代码篡改核心类,以及避免因类版本不一致导致的兼容性问题。例如,攻击者无法通过自定义类加载器替换java.lang.String为恶意实现,从而保障JVM运行安全。 提高类加载效率。通过层级委托机制,减少重复搜索类路径(ClassPath)的次数。类加载器只需尝试一次父类加载器的加载,若失败再自行加载,避免了全盘扫描,提升性能。 双亲委派模型的缺点 限制自定义类的动态更新。一旦类被父类加载器加载(如BootStrapClassLoader),即使类文件被修改,子类加载器也无法重新加载该类。
阅读全文