JVM - 内存区域

全貌

盗图一张如下:

img

程序计数器

  1. 记录字节码文件当前的执行行数
  2. 是每个线程私有的,共有的话,切换线程会懵逼掉的
  3. 如果执行native方法的话,它的值是undifined。因为native方法是java通过JNI直接调用本地C/C++库,可以近似的认为native方法相当于C/C++暴露给java的一个接口,java通过调用这个接口从而调用到C/C++方法。由于该方法是通过C/C++而不是java进行实现。那么自然无法产生相应的字节码,并且C/C++执行时的内存分配是由自己语言决定的,而不是由JVM决定的。
  4. 不会oom。因为它的空间很小并且不是我们申请的,是jvm自己管理的。

虚拟机栈

  1. 它是跟方法对应的,是用来描述方法调用过程中的内存模型。(方法区是跟class对应的)
  2. 栈帧:他的数据结构是个栈,每个方法对应一个栈帧,假如执行a方法,a方法中又调用了b方法,那么会先把a 压栈,执行到b的时候把b压栈,b执行完b出栈,之后接着执行a,a执行完a出栈。
  3. 局部方法表:每个方法执行需要的内存大小在编译的时候就确定了,局部方法表存放执行方法需要的变量,如果该变量是个对象的话,存放的是引用。局部方法表是在栈帧中的。(所有对象的真实存放区域是堆)
  4. 虚拟机的栈的深度是有最大值的,如果栈太深会报stackoverflow
  5. 如果方法执行没有到达栈的最大值,但是没有内存空间可用了,那么会报oom
  6. 线程私有的

本地方法栈

  1. 虚拟机栈跟本地方法栈基本一致,只不过不是java方法,而是对应native方法
  2. 线程私有的
  3. 会有oom跟stackoverflow

堆

  1. 存放对象的
  2. 线程共享的
  3. gc主要的目标
  4. 会oom

方法区

  1. 存放类信息(跟class对应),存放常量(常量池,1.8 将常量池放入了堆),存放静态变量等
  2. 线程共享的
  3. 会oom
  4. 在java8中被移除了,取而代之的是metaspace(元数据空间)

eg:

运行如下代码:以下只分析1.8

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
String a = "123";
String b = "123";
//true 两个都是常量池中 123 的地址
System.out.println(a == b);

String c = new String("123");
//一个堆 一个常量池,不一样 false
System.out.println(a == c);

//java 1.8 因为常量池已经有 123 了,所以返回常量池中的123的地址
String d = c.intern();
//所以true
System.out.println(a == d);

//下面的代码将 "我是wzq" 放入常量池,并创建了一个堆对象e
String e = new String("我是wzq");
//java 1.8 因为常量池有 我是wzq ,所以将 我是wzq 在常量池中的地址返回,
String f = e.intern();
//e是堆地址,f是常量池地址 false
System.out.println(e == f);

//往常量池中放入 我是wzq,以为已经有了,所有返回 我是wzq 在常量池中的地址,所以是true
String g = "我是wzq";
System.out.println(f == g);