基本概念
堆区:
1.存储的全部是对象,每个对象都包含一个与之对应的class的信息。(class的目的是得到操作指令)。
2.jvm只有一个堆区(heap)被所有线程共享,堆中不存放基本类型和对象引用,只存放对象本身。
栈区:
1.每个线程包含一个栈区,栈中只保存基础数据类型的对象和自定义对象的引用(不是对象),对象都存放在堆区中。
2.每个栈中的数据(原始类型和对象引用)都是私有的,其他栈不能访问。
3.栈分为3个部分:基本类型变量区、执行环境上下文、操作指令区(存放操作指令)。
方法区:
1.又叫静态区,跟堆一样,被所有的线程共享。方法区包含所有的class和static变量。
2.方法区中包含的都是在整个程序中永远唯一的元素,如class,static变量。
说明:这里对象与内存地址是相对关系,对象引用指的是对象在内存中的地址。
具体案例如下:
public class Test1 {
public static final int s=1;
public static void main(String[] args) {
int t=1;
//这里的A类已在同一个包下创建好了。
A a1 = new A();
System.out.println(t);
System.out.println(s);
System.out.println(a1);
}
}
运行结果为:
1
1
test.A@6e1408
这个案例分为三种情况:
第一种为 int t=1;
这条语句应该分解为两个步骤:
int t;//我们结合上述概念,这句是告诉JVM在运行期间该线程中的私有栈中为变量t分配一个存放内存地址的空间。
t=1;//这一步,JVM判断1为基本数据类型,则在该私有栈中查字面值为1的地址,如果没有则创建该地址,并且该地址为私有栈内部变量共享,就是其他内部变量值也为1的话,就不需要再在该私有栈中创建1的地址,返回给变量t的就是私有栈内的地址。输出结果为1是根据JVM内部规则运行的,这里注意一点1这个地址在私有栈内是唯一。
第二种为 public static final int s=1;
这条语句就一个步骤:
public static final int s=1;//这句是告诉JVM在编译期间就在该线程中的私有栈中为变量t分配一个存放内存地址的固定空间,并由于是static及int修饰的,并已经赋值,则在该私有栈中直接为1分配一个固定空间,地址反馈给t,相当于t这个变量及1这个基本数据类型均固定不能更改任何特性,直到该线程结束,也就是在编译期均已经确定,运行期对其无法更改。
这里public static final int s;//这句就与上面的不一样,分为编译期与运行期,编译期就是在该线程中的私有栈中为变量t分配一个存放内存地址的固定空间,由于是static及int修饰的,则被指定赋值为0,直到运行期初始化时,可以通过被赋值来改变指向的对象,也就是说变量t一直无法改变其空间位置,而其内的对象地址可以更改。
第三种 为 A a1 = new A();
我们也分为两步:
A a1;//JVM在该线程中的私有栈中为变量t分配一个存放内存地址的空间,该空间的值是无法确定的,需要被初始化才能知道。
a1 = new A();//JVM先判断数据类型,是引用数据类型,则在方法区中查询 A()的class原码,如果没有则返回错误,但一般都是已经加载进内存的了,正常情况下均会找到,找到了后,在堆中根据class大小分配内存空间(堆是动态分配内存空间的),然后拷贝一份A()的class到该空间中,这里可以明白如果A()的class内的某个变量被赋值,方法区内的该变量还是保持原来的状态,改变的只是堆中的class拷贝,也就是实例化后的对象。然后返回堆中的内存地址给私有栈。这里的输出结果不是1而是test.A@6e1408这个内存地址,这可能是JVM内部的运行机制,以后再细细研究。这里可以看出堆中的对象是无法共享的,因为即使是同一个class的拷贝,运行中其成员变量可以被赋予不同的值,这就无法共享了。
以上是详细讲解java创建对象的三个基本过程,以供参考,具体JVM为什么一下输出具体值,一下又输出内存地址,有人知道吗?
联系方式:379877741@qq.com