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);