0%

JVM - Java虚拟机内存模型

Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域。

jvm

这些区域包含:虚拟机栈、本地方法栈、程序计数器、堆、方法区。
每个区域都有各自的生命周期以及各自的作用范围及功能。

首先是虚拟机栈,它表示的是Java方法在运行时候的内存模型

jvm-stack

方法被调用的时候都会去创建栈帧,从方法执行的开始结束又对应着栈帧虚拟机栈中的入栈出栈
栈帧被用来保存局部变量表操作数栈动态链接方法出口等信息。
jvm-stack-1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.sankuai.test;

import com.alibaba.fastjson.JSON;

public class Test {
public static void main(String[] args) {
String json = "{\"id\":1,\"name\":\"1\"}";
int a = 1, b = 2;
int c = a + b + 6;
System.out.println(a + b);
System.out.println(c);
System.out.println(JSON.parseObject(json, User.class));
}
}

局部变量表中包含了基本数据类型对象引用类型返回地址

  • 基本数据类型:byte、short、int、long、float、double、boolean、char。
  • 对象引用类型:对象的起始地址(数组)或者对象的句柄(类)。
  • 返回地址:指向的是一条字节码指令的地址。

操作数栈,本质也是一个栈,入栈和出栈代表着变量的操作,比如:两个变量相加。
动态链接,所调用的对象会在常量池中存储并用标号标识,而常量池会指向对象的真实地址。
jvm-cp-0jvm-cp方法出口,当栈帧出栈之后,对应的这个方法应该返回到调用它的地方。

接着是本地方法栈,它跟虚拟机栈是类似的,只不过它调用的不是Java方法,而是Native本地方法
另外,虚拟机栈本地方法栈都是线程私有的,生命周期跟随着线程。
当线程越多、栈的深度越深,就可能造成栈溢出StackOverFlowError,甚至是OutOfMemoryError

程序计数器标识的是线程正在执行的字节码地址,由于多线程的存在及CPU可能会来回切换线程。
多个线程之间的程序计数器是相互独立的,可以看作是各自的行号指示器

是Java虚拟机中内存占用最大的一块区域,也被所有的线程所共享,主要是用来存放对象实例。所有的对象实例和数组都要在上分配内存,不过后来出现了栈上分配、标量替换、逃逸分析等技术,使得对象不一定需要在堆上分配内存。一个堆包含了:新生代(Eden、S0、S1)老年代,比例通常是1:2

jvm-heap也是垃圾收集器GC的重点区域,目前普遍采用的是分代收集算法

最后是方法区,它跟堆一样,是被所有线程所共享的,主要存储的是:被虚拟机所加载的类的信息、常量、静态变量、即使编译器编译之后的代码等信息,在JDK1.8之后,方法区元空间所替代,并且被挪到了堆外
方法区中存在着一个运行时常量池,它用来存储字面量符号引用
jvm-cp-2