Java虚拟机的内存可以分为三个区域:栈stack、堆heap、方法区method area。
栈的特点:
- 栈描述的是方法执行的内存模型,每个方法被调用都会创建一个栈帧(存储局部变量、操作数、方法出口等)
- JVM为每个线程创建一个栈,用于存放改线程执行方法的信息(实际参数、局部变量等)
- 栈属于线程私有,不能实现线程间的共享
- 栈的存储特性是 先进后出,后进先出
- 栈是由系统自动分配,速度快,栈是一个连续的内存空间。
简单来说,栈是用来存放程序执行的方法的。具体过程如下:
- 当程序运行的时候,JVM会为程序创建一个栈
- 因为程序要从main方法进入,所以先将main方法放入栈中
- 如果在执行main方法时执行了A方法,那么就将A方法放入栈中。
- 现在栈中有了两个方法(main方法在栈底,A方法在栈顶),执行后入栈的A方法
- 如果A方法中要执行B方法,将B方法放入栈中
- ……
- 执行完A方法后,A方法出栈
- 现在栈中只有一个方法(main方法),继续执行main方法,直到又要执行新的方法或者程序结束
另外,每创建一个线程就要创建一个栈用来执行方法。
堆的特点如下:
1.对用于存储创建好的对象和数组(数组也是对象)
2.JVM只有一个堆,被所有线程共享
3.堆是一个不连续的内存空间,分配灵活,速度慢
方法区(又叫静态区)特点如下:
1.JVM只有一个方法区,被所有线程共享
2.方法区实际也是堆,只是用于储存类、常量相关的信息
3.用来存放程序中永远不变或唯一的内容(类信息、Class对象、静态变量、字符串常量等)
1、栈和堆在内存中是两个独立的区域,而方法区是堆的一部分。
2、方法区用来存储程序中的字符串常量、静态变量、类信息、Class对象等在程序运行过程中不会变动的变量,程序运行开始时创建,程序运行结束时销毁
3、堆里面存储在程序运行时创建的“局部变量”,在出作用域时销毁,在堆里面存储是随机存储的。
public class HtmlGet {
private String name = "聪明的王二狗";
public HtmlGet(String name) {
setName(name);
}
public void setName(String name) {
this.name = name;
}
public void printName() {
System.out.println("我是:" + name);
}
public static void main(String[] args) {
String name = "公子小白";
HtmlGet mh = new HtmlGet(name);
mh.printName();
}
}
在上面的代码中,对其内存分析:
- 首先,在方法区存储常量,有字符串常量“聪明的王二狗”、“我是:”、”公子小白“,类HtmlGet的信息如代码、静态变量、静态方法、字符串常量,
- 方法区存完,首先是程序入口main方法,JVM创建一个栈,将main方法入栈(这个时候main方法所在的类是没有被创建的,注意理解),执行main方法
- 在堆里面给name这个String类型的变量分配一块内存,让它指向方法区里面的”公子小白“字符串常量
- 调用HtmlGet的构造方法,入构造方法入栈,把mh对象中的name指向方法区中”聪明的王二狗“这个字符串常量
- 构造方法中调用了setName方法,将setName方法入栈,(此时有main,构造方法,setName三个方法在栈中,setName在栈顶)
- setName将mh的name指向了方法区中的”公子小白“字符串常量,setName方法执行完毕,出栈
- 调用完setName,构造方法执行完毕,出栈(此时只剩main方法在栈中)
- 继续执行main方法未执行的代码
- 调用mh的printName方法,printName方法入栈
- printName方法将我的名字输出,执行完毕,出栈
- main方法调用完printName方法,执行完毕,出栈
- 栈空,程序结束,(将方法区、堆里面的变量清空)
这就是在运行上述代码时的内存分析,下面是程序运行到构造方法时的图片,方便理解: