JAVA虚拟机在执行JAVA程序的过程中会把其所管理的内存划分为若干个不同的数据区域。这些区域都有各自的用途和生命周期。JAVA虚拟机所管理的内存包括以下几个运行数据区域。
程序计数器
程序计数器是一块内存区域,可以看做当前线程所执行的字节码的行号指示器。为了线程切之后能够恢复到正确的执行位置,因此每条线程都需要一个独立的程序计数器,各条线程之间计数器互不影响,独立存储。程序计数器是线程私有的。
程序计数器作用是记住下一条 jvm指令的执行地址。程序计数器是线程私有,不会存在内存溢出(OutOfMemeryError)。
虚拟机栈
与程序计数器相同,JAVA虚拟机栈也是线程私有的,生命周期与线程相同。虚拟机栈描述的是JAVA方法执行的内存模型:每个方法在执行的同时都会创建一个栈帧用于存储局部变量表,操作数栈,动态链接,方法出口等信息。每一个方法从调用直至执行完成的过程,就对应这一个栈帧在虚拟机栈中入栈到出栈的过程。
public class Demo1_1 {
public static void main(String[] args) throws InterruptedException {
method1();
}
private static void method1() {
method2(1, 2);
}
private static int method2(int a, int b) {
int c = a + b;
return c;
}
}
虚拟机栈中局部变量表存放了编译期可知的各种数据类型,对象引用(是引用类型,不等同于对象本身,可能是一个指向对象起始地址的引用指针,或者是指向一个代表对象的句柄或其他与此对象相关的位置)。局部变量表所需的内存空间是在编译期间完成分配,当进入一个方法时,这个方法需要在帧中分配多大的局部变量是完全确定的,在方法运行期间不会改变局部变量表的大小。
JAVA虚拟机规定了两种异常:线程请求的栈深度大于虚拟机所允许的深度造成的StackOverflowError异常;可以通过设置VM参数 -Xss设置虚拟机栈的大小。
public class Demo1_2 {
private static int count;
public static void main(String[] args) {
try {
method1();
} catch (Throwable e) {
e.printStackTrace();
System.out.println(count);
}
}
private static void method1() {
count++;
method1();
}
}
虚拟机动态扩增无法申请到足够内存时造成的OutOfMemoryError异常。
每个线程只有一个活动栈帧,对应当前执行方法。
其次JAVA垃圾回收不涉及栈内存垃圾回收,垃圾回收只会回收堆内存中的内存占用,栈内存不需要,栈帧运行完会自动出栈。
栈内存大小需要合理设置,因为系统内存有限,栈内存过大,会导致分配线程数变少,因为一个线程对应一个虚拟机栈。栈内存过小会容易导致StackOverflowError错误。
方法内的局部变量是否线程安全?如果方法内局部变量没有逃离方法的作用访问,它是线程安全的。如果是局部变量引用了对象,并逃离方法的作用范围,需要考虑线程安全。
public class Demo1_18 {
static int x = 0;
public static void main(String[] args) throws Exception{
new Thread(()->{
m1();
}).start();
new Thread(()->{
m1();
}).start();
}
// 多个线程同时执行此方法
static void m1() {
for (int i = 0; i < 500000; i++) {
x++;
}
System.out.println(x);
}
这里x不是线程安全的。
本地方法栈
本地方法栈与虚拟机栈所发挥的作用基本相似,也是线程私有,虚拟机栈为虚拟机执行JAVA方法服务,二本地方法栈使用Native方法服务。对本地方法栈使用的语言和方法没有强制规定,不同虚拟机有不同的实现方式,本地方法栈也会抛出StackOverflowError和OutOfMeMemoryError错误。
JAVA堆
JAVA堆是被所有线程共享的一块内存区域,在虚拟机启动时创建,是虚拟机管理的内存中最大的一块。堆用于存放对象实例,几乎所有的对象实例都在这里分配内存。虚拟机的垃圾回收主要是对JAVA堆进行回收,JAVA堆在可以处于物理上不连续的内存空间,只要逻辑上联系即可。可以通过设置VM参数-Xmx设置堆的大小。
方法区
方法区与JAVA堆一样是所有线程共享的,用于存储已经被虚拟机加载的类信息,常量,静态变量,编译器编译后的代码等数据。