在面试中,经常会遇到JVM内存相关的问题。但是实际上很多情况下,大家没有把内存模型和内存区域认真区分,实际上这是两个层面的东西。
JMM的出现是为了解决在并发编程中的两个经典问题:线程之间如何通信及线程之间如何同步。
通信:在命令式编程中,线程之间的通信方式有两种
同步:同步是指程序用于控制不同线程之间操作发生 相对顺序 的机制
小结一下:
通信方式 | 通信隐显 | 同步隐显 | 程序员角度 |
---|---|---|---|
共享内存 | 隐式通信 | 显示同步 | 显示指定 |
消息传递 | 显示通信 | 隐式同步 | 透明 |
Java内存区域一般指的值运行时数据区。Java虚拟机在执行Java程序的过程中会把它管理的内存划分成若干个不同的数据区域。JDK 1.8对运行时数据区有调整,所以和之前的版本略有不同。
在JDK 1.8版本之前,如果你不知道JVM规范定义的运行时数据区和HotSpot JVM对应的实现的区别还问题不大,当然,大部分博客也是这么做的。但是在MetaSpace出现之后,就必须要搞清楚这两者的区别,不然很难理解区分方法区、永久带、元空间。
参考: JDK8的JVM规范文档
针对文档的运行时数据区我自己翻译了一版: The Java Virtual Machine Specification, Java SE 8 Edition(Java虚拟机规范)-运行时数据区(翻译)
规范中定义了6个运行时数据区,The pc Register(程序计数器)、Java Virtual Machine Stacks(Java虚拟机栈)、Heap(堆)、Method Area(方法区)、Run-Time Constant Pool(运行时常量池)、Native Method Stacks(本地方法栈)。
Each run-time constant pool is allocated from the Java Virtual Machine's method area.
运行时常量池是在JVM的方法区中分配的。 所以,这就是我们常看到的图:
HotSpot VM是Sun JDK和OpenJDK中所带的虚拟机,是目前使用范围最广的Java虚拟机。
在2006年的JavaOne大会上,Sun公司宣布最终会把Java开源,并在随后的一年,陆续将JDK的各个部分(其中当然也包括了HotSpot VM)在GPL协议下公开了源码, 并在此基础上建立了OpenJDK。这样,HotSpot VM便成为了Sun JDK和OpenJDK两个实现极度接近的JDK项目的共同虚拟机。
在2008年和2009年,Oracle公司分别收购了BEA公司和Sun公司,这样Oracle就同时拥有了两款优秀的Java虚拟机:JRockit VM和HotSpot VM。 Oracle公司宣布在不久的将来会完成这两款虚拟机的整合工作,使之优势互补。(这也是JDK 1.8中对HotSpot VM运行时数据区进行大调整的一个背景之一)
HotSpot VM将虚拟机栈和本地方法栈合二为一:
在JDK 1.8之前的版本,图上的方法区在HotSpot VM中的实现就是PermGen
(永久代),在其他JVM上不存在永久代。在JDK 1.8的时候,HotSpot VM将方法区的实现替换成了
Meatspace
(元空间)。所以在本质上,永久代和元空间都是方法区的实现,只是它们的实现细节不一样。
Java Heap
或者是 Native Heap
。譬如符号引用(Symbols)转移到了 native heap
;字面量(interned strings)转移到了 java heap
;类的静态变量(class statics)转移到了 java heap
。 在JVM规范中定义: Although the method area is logically part of the heap, simple implementations may choose not to either garbage collect or compact it.
方法区逻辑上属于堆的一部分。(JDK1.7之前HotSpot VM的方法区物理上也属于了堆的一部分)
java.lang.OutOfMemoryError: PermGen space
,元空间溢出: java.lang.OutOfMemoryError: Metaspace