1.创建对象的过程?

“当 JVM 遇到一条字节码 new 指令时,创建对象的过程主要分为 五步

第一步:类加载检查 (Class Loading Check)

  • JVM 首先会去检查这个指令的参数,看能不能在常量池中定位到一个类的符号引用。

  • 然后检查这个符号引用代表的类是不是已经被加载、解析和初始化过。

  • 如果没有,那必须先执行相应的 类加载过程(Loading Linking Initializing)。

第二步:分配内存 (Memory Allocation) —— 核心点

  • 类加载检查通过后,JVM 就知道这个对象需要多大的内存了(类加载完大小就确定了)。

  • 接下来会在 堆 (Heap) 中划分一块内存给这个对象。

  • (这里涉及‘指针碰撞’和‘空闲列表’两种分配方式,视堆内存是否规整而定)。

第三步:初始化零值 (Zero Initialization)

  • 内存分配完后,JVM 需要将分配到的内存空间都初始化为 零值(不包括对象头)。

  • 这就是为什么我们在 Java 代码中定义成员变量 int i,不赋值直接用也不会报错(因为默认是 0)。

第四步:设置对象头 (Setting Object Header)

  • JVM 要对对象进行必要的设置:比如这个对象是哪个类的实例、如何找到类的元数据、对象的哈希码、对象的 GC 分代年龄等信息。

  • 这些信息都存放在 对象头 (Object Header) 中。

第五步:执行 <init> 方法 (Execution)

  • 上面 4 步做完,从 JVM 角度看对象已经造好了,但从 Java 程序的角度看,对象才刚刚开始。

  • 此时会执行构造函数(也就是字节码里的 <init> 方法),按照我们的意愿对对象进行初始化(赋值)。

  • 执行完这一步,一个真正可用的对象才算完全产生。”

2.对象的生命周期

创建:对象通过关键字new在堆内存中被实例化,构造函数被调用,对象的内存空间被分配。 使用:对象被引用并执行相应的操作,可以通过引用访问对象的属性和方法,在程序运行过程中被不断使用。 销毁:当对象不再被引用时,通过垃圾回收机制自动回收对象所占用的内存空间。垃圾回收器会在适当的时候检测并回收不再被引用的对象,释放对象占用的内存空间,完成对象的销毁过程。

3.类加载器有哪些?

“在 Java (JDK 8) 中,主要有 3 个核心类加载器,它们是层级关系的:

1. 启动类加载器 (Bootstrap ClassLoader)

  • 地位: 老祖宗。它是最顶层的加载器。

  • 实现:C++ 编写(所以在 Java 代码里拿不到它的引用,是一个 null)。

  • 职责: 负责加载 Java 的核心类库

  • 路径: 加载 JAVA_HOME/lib 目录下的 jar 包(比如 rt.jar,里面有 String, System 这些最基础的类)。

2. 扩展类加载器 (Extension ClassLoader)

  • 地位: 二把手。

  • 实现: Java 编写。

  • 职责: 负责加载 Java 的扩展库

  • 路径: 加载 JAVA_HOME/lib/ext 目录下的 jar 包。里面有压缩,js 引擎,加密,国际语言

3. 应用程序类加载器 (Application ClassLoader)

  • 地位: 干活的主力。也叫系统类加载器 (System ClassLoader)。

  • 实现: Java 编写。

  • 职责: 负责加载我们自己写的代码和引用的第三方 jar 包

  • 路径: 加载 CLASSPATH 环境变量指定的路径。

4. 自定义类加载器 (Custom ClassLoader)

  • 职责: 如果我们需要从网络、加密文件或非标准路径加载类,可以继承 ClassLoader 类来实现自己的加载逻辑(比如 Tomcat 就自定义了很多)。”

形象比喻:家族企业

把 JVM 想象成一个 “家族企业”,找东西(加载类)的时候有严格的等级制度:

  • Bootstrap (爷爷 - 创始人):

    • 只管最核心的机密文件(JDK 核心库)。他看不懂中文(Java),只懂底层(C++)。
  • Extension (爸爸 - 分公司老总):

    • 管一些扩展业务(Ext 库)。
  • Application (你 - 基层员工):

    • 管所有日常杂活(你的业务代码)。
  • Parent Delegation (双亲委派):

    • 当你需要找一个文件(类)时,你不敢自己先找,必须先问爸爸:“爸,这东西你有吗?”

    • 爸爸也不敢自己找,先问爷爷:“爹,这东西你有吗?”

    • 爷爷说“没有” 爸爸才敢找

    • 爸爸也说“没有” 你才敢自己找

4.为什么要设计成这样一层层往上问?(为什么要双亲委派?)

“主要为了解决 安全唯一性 的问题:

  1. 避免重复加载 (唯一性):

    • 如果没有双亲委派,我自己写了个 java.lang.String,你又写了个 java.lang.String

    • JVM 就乱套了,不知道该用哪一个。

    • 有了双亲委派,无论谁要加载 String,最终都会委派给爷爷 (Bootstrap)。爷爷发现他已经加载过官方的 String 了,直接返回,保证了核心类在内存中只有一份。

  2. 保证安全 (沙箱安全机制):

    • 如果黑客自定义了一个 java.lang.System 类来替换核心类,里面写了恶意代码。

    • 如果没有双亲委派,这个恶意类就被加载进来了。

    • 有了双亲委派,JVM 根本不会理会黑客写的这个类,因为爷爷那里已经有一份正版的 System 了。”

_5.双亲委派模型的作用

6.讲一下类加载过程?

阶段术语比喻解释
1加载买房拿钥匙找到房子(Class文件),拿到钥匙(Class对象),进门了。
2.1验证验房检查墙体有没有裂缝,是不是危房(由 CAFEBABE 验证)。
2.2准备刮大白/硬装先把墙刷白,家具还没进场。此时只有空壳子(变量赋零值:int=0)。
2.3解析接通水电把图纸上的“插座位置”变成墙上真正的“电线接口”(符号引用 内存地址)。
3初始化软装/入住按照你的喜好摆放沙发、挂画(执行 static 赋值和代码块),房子真正可以用了。
  • 顺序:类加载(备案图纸) $\rightarrow$ 对象创建(按图盖房)。

  • 关系:类是模板(1个),对象是实例(N个)。对象引用类,但不被类包含

  • 空间:类在元空间,对象在堆。

7.讲一下类的加载和双亲委派原则

双亲委派模型其实是服务于类加载过程中‘加载 (Loading)’这一阶段的。

比如我要 new User()

  1. 加载阶段:JVM 发现还没加载 User 类,于是启用双亲委派,AppClassLoader 问 Ext,Ext 问 Boot,Boot 说没有,最后 AppClassLoader 自己把 User.class 文件读进来。

  2. 连接阶段:给 User 里的 static 变量赋零值。

  3. 初始化阶段:执行 static 代码块。

这样 User 类就算彻底加载完了。”

维度类的加载过程 (Process)双亲委派原则 (Mechanism)
关注点生命周期 (做什么事?)层级规则 (谁来做?)
核心步骤加载 连接(验证/准备/解析) 初始化App Ext Boot (向上委托,向下加载)
关系包含关系 (双亲委派是”加载”步骤的策略)服务关系 (服务于类的安全性与唯一性)