JVM存储位置分配——java中局部变量、实例变量和静态变量在方法区、栈内存、堆内存中的分配
Java中的变量根据不同的标准可以分为两类,以其引用的数据类型的不同来划分可分为“原始数据类型变量和引用数据类型变量”,以其作用范围的不同来区分可分为“局部变量,实例变量和静态变量”。
根据“Java中的变量与数据类型”中的介绍,“变量是在内存中分配的保留区域的名称。换句话说,它是一个内存位置的名称”,也就是说我们通过这个变量名字就可以找到一个指向这个变量所引用的数据的内存指针,根据变量的类型我们可以知道这个指针之后的几个字节里存储了这个变量所引用的数据。
所以,了解变量在方法区、栈内存、堆内存中的分配要了解两部分内容,一个是“变量在内存中的分配”,另一个是“变量所引用的数据在内存中的分配”。以下简称为“变量分配”与“数据分配”。
原始数据类型变量:
原始数据类型变量的“变量分配”与“数据分配”是在一起的(都在方法区或栈内存或堆内存)
引用数据类型变量:
引用数据类型变量的“变量分配”与“数据分配”不一定是在一起的。
示例代码:
1 class Fruit { 2 static int x = 10; 3 static BigWaterMelon bigWaterMelon_1 = new BigWaterMelon(x); 4 5 int y = 20; 6 BigWaterMelon bigWaterMelon_2 = new BigWaterMelon(y); 7 8 public static void main(String[] args) { 9 final Fruit fruit = new Fruit(); 10 11 int z = 30; 12 BigWaterMelon bigWaterMelon_3 = new BigWaterMelon(z); 13 14 new Thread() { 15 @Override 16 public void run() { 17 int k = 100; 18 setWeight(k); 19 } 20 21 void setWeight(int waterMelonWeight) { 22 fruit.bigWaterMelon_2.weight = waterMelonWeight; 23 } 24 }.start(); 25 } 26 } 27 28 class BigWaterMelon { 29 public BigWaterMelon(int weight) { 30 this.weight = weight; 31 } 32 33 public int weight; 34 }
内存分配图:
同一种颜色代表变量和对象的引用关系:
由于方法区和堆内存的数据都是线程间共享的,所以线程Main Thread,New Thread和Another Thread都可以访问方法区中的静态变量以及访问这个变量所引用的对象的实例变量。ps:方法区存储的都是只加载一次的。
栈内存中每个线程都有自己的虚拟机栈,每一个栈帧之间的数据就是线程独有的了,也就是说线程New Thread中setWeight方法是不能访问线程Main Thread中的局部变量bigWaterMelon_3,但是我们发现setWeight却访问了同为Main Thread局部变量的“fruit”,这是为什么呢?因为“fruit”被声明为final了。
当“fruit”被声明为final后,“fruit”会作为New Thread的构造函数的一个参数传入New Thread,也就是堆内存中Fruit$1对象中的实例变量val$fruit会引用“fruit”引用的对象,从而New Thread可以访问到Main Thread的局部变量“fruit”。