图片内容源自B站黑马Java基础公开课
内存分析:单个对象的实例化过程。给定手机类的实例化流程代码,分析手机对象在内存中的流动过程
一、绘制内存结构图
二、类加载
JVM依次将Phone类和Demo01PhoneOne类的字节码(.class)加载到方法区中。加载内容包括各自的成员变量和成员方法,并记录它们在方法区中的地址
三、压入main方法栈帧
main方法是Java程序运行的起点,JVM在类加载的过程中找到main方法所在的类,进而将main方法压入栈内存,并为main方法开辟栈帧
四、对象实例化
Phone one = new Phone(); // 对象实例化
实例化 Phone 对象,并赋值给 Phone 类型的局部变量 one:
- 在 main 方法栈帧中开辟一段地址空间命名为 one
- 实例化 Phone 对象,在堆中开辟一段合适的地址空间,参考方法区中 Phone 类字节码在这段地址空间中初始化成员变量,同时根据成员变量的不同类型为它们赋初值
- 将成员方法在方法区中的地址添加到堆内存中 Phone 对象的地址空间
- 将 Phone 对象在堆中的首地址赋值给 main 方法栈帧中的局部变量 one
五、访问和修改成员变量
System.out.println(one.brand); // 访问对象中的成员变量并打印输出
one.brand = "苹果"; // 更新成员变量
- one.brand:根据 main 栈帧中局部变量 one 保存的堆空间地址值找到具体的 Phone 对象,进而通过成员变量名 brand 访问到该字符串的值 null
- one.brand = “苹果”:找到 Phone 对象的成员变量 brand,将字符串对象"苹果"在字符串常量池中的地址值赋值给 brand
六、调用执行对象方法
one.call("乔布斯"); // 调用对象中包含的call()方法
- 根据 main 栈帧中变量 one 保存的地址值找到在堆中的 Phone 对象
- 根据 Phone 对象中保存的成员方法的地址值找到在方法区中的 Phone 类方法 call(String who)
- 将 Phone 类的 call 方法压入栈内存,并为其开辟栈帧
- 将实参字符串对象"乔布斯"在字符串常量池中的地址值传入 call 方法的栈帧,顺序执行 call 方法中的代码指令
- call 方法中的指令全部执行完毕后,将 call 方法的栈帧弹栈,回到 main 方法中对应的代码位置
one.sendMessage(); // 调用对象中包含的sendMessage()方法
- 根据 main 栈帧中变量 one 保存的地址值找到在堆中的 Phone 对象
- 根据 Phone 对象中保存的成员方法的地址值找到在方法区中的 Phone 类方法 sendMessage()
- 将 Phone 类的 sendMessage 方法压入栈内存,并为其开辟栈帧
- 执行 sendMessage 方法,然后将 sendMessage 方法弹栈,回到 main 方法中对应的代码位置
可以看出,成员方法的访问过程是间接的
七、弹出main方法栈帧
将 main 方法栈帧弹出栈内存,堆和方法区中的所有对象和变量都失去引用,将全部被GC自动回收