在看《Java并发编程的艺术》一书时,涉及到了Java内存模型,也就是JVM中各个区域的内存分布,每一块区域都存储了哪些内容,这篇文章主要就是总结这些的。我们首先来看虚拟机是如何来划分内存的,JVM中 的内存共划分为程序计数器、堆、虚拟机栈、本地方法栈和方法区五个部分。其中JDK8以后取消了永久代(方法区是JVM的规范,永久代是方法区的具体实现),使用元空间取而代之。之前存储在方法区的静态成员 变量、字符串常量池就转移到了堆中,类元数据转移到了元空间中,其中元空间位于本地内存(不再是虚拟机内存),
程序计数器:Program Counter,存放下一条即将要执行的指令的地址,CPU根据PC寄存器的内容找到该指令并将其放到指令寄存器中;同时,PC寄存器的中的地址加1,具体加几个字节 要看机器的字长,或者由转移指令给出下一条指令的地址。因此每个线程都有自己私有的程序计数器。
虚拟机栈:也是各个线程私有的,每个Java方法在执行的同时,会创建一个栈帧,用于存储局部变量表、操作数栈、常量池引用等信息;方法的调用过程,就是一个栈帧在 Java 虚拟 机栈中入栈和出栈的过程。
本地方法栈:也是各个线程私有的,和虚拟机栈很类似,区别在于虚拟机栈为Java方法服务,本地方法栈为 Native 方法服务;其中 Native 方法可以看做用其它语言(C、C++ 或汇编语言等)编写的方法。
堆:一个Java进程中的所有线程共享的,运行时动态申请的内存都在堆上进行分配,包括new的对象和数组。JDK8以后,静态变量,字符串常量池也存放在堆上。堆中存对象,栈中存 放堆中对象的引用以及基本数据类型。
方法区:Java8之前,由永久代实现,主要存放类的信息、常量池、方法代码等,JDK8之后,取消了永久代,提出了元空间,并且常量池、静态成员变量等迁移到了堆中;元空间不在 虚拟机内存中,而是放在本地内存中。
- JDK 1.6,字符串常量池位于永久代的运行时常量池中,永久代和堆互相隔离,永久代的大小在启动JVM时可以设置一个固定值,不可变。
- JDK 1.7,存储在永久代的部分数据转移到了堆或者本地内存中,但永久代仍存在于JDK 1.7中,并没有完全移除,譬如符号引用(Symbols)转移到了native memory;字符串常量池(interned strings)转移 到了Java heap;类的静态变量(class statics)转移到了Java heap。
- JDK 1.8,元空间取代了永久代,并放入了本地内存中(Native Memory)。