Java中对象创建和使用的深层次解析


一、JVM是如何管理内存的?

  下图为java虚拟机内存管理图:

在这里插入图片描述

1. 程序计数器

  • 概念:可以看做当前线程所执行的字节码的行号指示器
  • 特点:线程私有内存

2. Java虚拟机栈

  • 概念:描述的是java方法执行的内存模型。(每个方法在执行的时候都会创建一个栈帧,用于存储局部变量表,操作栈帧,动态链接,方法出口等信息。每个方法从调用直至完成的过程,就对应一个栈帧从入栈到出栈的过程)
  • 特点:线程私有,生命周期和线程相同。这个区域会出现两种异常:StackOverflowError异常——若线程请求的深度大于虚拟机所允许的深度。OutOfMemoryError异常——若虚拟机可以动态扩展,如果狂战士无法申请到足够的内存。

3. 本地方法栈

  • 概念:它与虚拟机栈所发挥的作用是相似的,区别是java虚拟机栈为执行java方法服务,而本地方法栈是为本地方法服务
  • 特点:线程私有,也会抛出两类异StackOverflowError和OutOfMemoryError。

4. 堆区

  • 概念:是被所有线程共享的一块区域,在虚拟机启动时创建
  • 特点:线程共享,存放的是对象实例(所有对象实例和数组),GC管理的主要区域。可以处于物理上不连续的内存空间。

5. 方法区

  • 概念:存储已被虚拟机加载的类信息、常量、静态变量,即时编译器编译后的代码等数据。
  • 特点:线程共享的区域,抛出异常OutOfMemory异常——当方法区无法满足内存分配需求时。

  对于初学者而言,我们必须要知道的Java虚拟机的三块主要内存空间,是“虚拟机栈”(简称栈)、“方法区”、“堆区”。

  • 方法区存储类的信息
  • 存储方法执行时的栈帧以及局部变量
  • 主要存储new出来的对象,以及对象内部的实例变量

方法区最先有数据,因为程序执行之前会先进行类加载。栈内存活动最繁琐,因为方法不断的执行并结束,不断的进行压栈弹栈操作。
在这里插入图片描述

二、简述创建对象时JVM虚拟机的内存变化

代码:

public class StudentTest {
    
    
    public static void main(String[] args) {
    
    
        int i=10;
        Student s1=new Student();
    }
}

学生类代码:

class Student{
    
    
    int no;
    String name;
    int age;
    boolean sex;
}

以上代码在执行过程中内存的变化如下:

  1. 首先第一步进行类的加载
    在这里插入图片描述
  2. 第二步main方法调用,给main方法分配栈帧(压栈)
    在这里插入图片描述
  3. 第三步执行int i=10
    在这里插入图片描述
  4. 第四步执行new student(),在堆中创建对象,同时初始化实例变量。
    在这里插入图片描述
  5. 第五步将堆区中学生对象的内存地址赋值给局部变量s1
    在这里插入图片描述注意
      图中i变量和s1变量都是局部变量,都在栈内存当中,只不过i是基本数据类型int,s1是引用数据类型Student。
      堆中创建的对象s1,该对象的内部属性no、name、age、sex都是实例变量,这些变量在new对象的时候初始化,如果没有手动赋值,系统会默认赋值。
    关于“引用”的理解
      上图堆区中“对象”创建完成之后,该对象在堆区当中的内存地址是:0x1111,程序中的=代表,将0x1111这个堆内存的地址赋值给s1变量,也就是说s1变量保存了堆内存对象的内存地址。s1不是对象,而是对象的一个引用,对象实际上是在堆区当中,s1持有这个对象的内存地址。
      java中没有指针的概念,所以java程序员没有权利直接操作堆内存,只能通过“引用”去访问堆内存中的对象。

总结

  通过上文的讲解,我们应该知道局部变量存储在栈区实例变量存储在堆区,实例变量的初始化是在创建对象的同时进行初始化赋值。java是通过对象名.属性的方式去访问对象的实例变量。

猜你喜欢

转载自blog.csdn.net/m0_46988935/article/details/110144235