Java虚拟机的内存可以分为三个区域:栈stack、堆heap、方法区method area。
栈(stack)的特点如下:
1、栈描述的是方法执行的内存模型,每个方法被调用都会在栈中创建一个栈帧(存储局部变量、操作数、出口等)。
2、Java虚拟机(JVM)为每个线程创建一个栈,用于存放该线程执行方法的信息(局部变量,实际参数等)。
3、栈属于线程私有,不能实现线程间的共享!
4、栈的存储特性是“先进后出”,(可以比喻为手枪弹夹的子弹)
5、栈是由系统自动分配的,速度块!栈是一个连续的内存空间!
堆(heap)的特点如下:
1. 堆用于存储创建好的对象和数组(数组也是对象)
2. JVM只有一个堆,被所有线程共享
3. 堆是一个不连续的内存空间,分配灵活,速度慢!
方法区(method area)(又叫静态区)特点如下:
1. JVM只有一个方法区,被所有线程共享!
2. 方法区实际也是堆,只是用于存储类、常量相关的信息!
3. 用来存放程序中永远是不变或唯一的内容。(类信息【Class对象】、静态变量、字符串常量等)
使用文字和图画描述,如下程序的执行过程:
public class TestObject {
public static void main(String[] args) {
Car c1 = new Car();
c1.changeColor("红色");
c1.showColor();
System.out.println(Car.tyreNum);
System.out.println(c1.tyreNum);
Car c2 = new Car();
Engine e = new Engine();
e.speed = 1000;
e.weight = 10;
c2.engine = e;
c2.color = "黑色";
c2.tyreNum = 10;
System.out.println(c1.tyreNum);
}
}
class Car {
static int tyreNum = 4;
Engine engine;
String color; // char sequence :字符序列
void changeColor(String c) {
color = c;
}
void showColor() {
System.out.println("我的颜色是:" + color);
}
}
class Engine {
int speed;
int weight;
}
上述代码的内存分析如下:
文字解释:
1、javacTestObject.java //先将源代码编译成.class的字节码文件
2、java TestObject //启动JVM,TestObject类、Car类、Engine类先加载到空间(方法区)里边去
3、(1)main方法是程序的入口,同时也是静态方法,在方法区的TestObject类相关信息中找到main方法,JVM在栈中创建一个main方法的栈帧,同时开辟了一个Car类型的变量c1,当执行 new Car()的时候,在堆里面开辟空间,创建了一个Car类型的对象,使用默认的初始值,然后把该对象的地址值引用给变量c1。
(2)当执行c1.changeColor("红色")方法的时候,会在栈中创建一个changeColor方法的栈帧,该方法的作用是给变量c1指向的对象的color属性赋值,然后在方法区找到“红色”之后赋值,方法结束,栈帧销毁。
(3)执行c1.showColor()方法,首先在栈中创建栈帧,然后该方法的作用是调用System.out.println("我的颜色是:"+color),然后再去栈中创建一个println方法的栈帧,该方法是打印字符串出来,字符串在方法区的字符串常量中找到,然后color因为之前赋值是红色,所以控制台打印出:我的颜色是:红色
(4)
System.out.println(Car.tyreNum);
System.out.println(c1.tyreNum);
上面的两行代码也是两个方法,去创建栈帧,然后输出方法区里面的静态变量的值,一个是类名调用,一个是对象名调用,都能找到,因为静态变量从属于类,对象是可以访问加载到方法区类中的信息的,方法结束栈帧销毁。
(5)、在main方法的栈帧中,开辟空间存储Car类型变量c2,new Car()和上面一样都是去堆中开辟空间创建对象,把地址传给变量c2,这时候,c2指向的对象的成员变量还是使用默认值,都是null,null。
(6)、然后,在main方法的栈帧中,开辟空间存储Engine类型变量e,new Engine()在堆中开辟空间创建创建对象并且把地址值给变量e,然后执行代码给e指向的对象赋值,然后再把e的地址值赋值给对象c2的地址值,所以c2的成员变量engine指向对象e
(7)、通过对象c2去方法区中找到静态变量typeNum,并且赋值为10,可以修改静态变量,不过不建议使用,还是使用类名调用吧,最后在打印出来
(8)、main方法结束,销毁对应的栈帧。
执行结果如下: