JVM内存模型

Posted rookiejw

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JVM内存模型相关的知识,希望对你有一定的参考价值。

what ? when ? why ? how ?

what

JVM内存结构指的是哪一块区域?

JVM内存划分的各个区域都存储什么信息?

每个划分的区域有什么特点?

when

划分的区域有没有随着jdk的变动有所改动?

how

JVM内存结构如何划分?

why


JVM内存结构指的是哪一块区域?

java程序工作原理

技术分享图片

当你写完程序后 xx.java

编译 javac xx.java

就会生成 xx.class 字节码文件

运行 java xx

就完成了上面紫色方框中的工作。JRE的来加载器从硬盘中读取class文件,载入到系统分配给JVM的内存区域--运行数据区(Runtime Data Areas). 然后执行引擎解释或者编译类文件,转化成特定CPU的机器码,CPU执行机器码,至此完成整个过程。

大红色这一部分(Runtime Data Areas)就是我们常说的JVM内存。


JVM内存结构如何划分?

技术分享图片

如上图所示:方法区、堆、虚拟机栈、本地方法栈、程序计数器


JVM内存划分的各个区域都存储什么信息?

程序计数器

JVM中的程序计数器跟汇编语言中的程序计数器的功能在逻辑上是等同的,
指示 执行哪条指令

在汇编语言中,程序计数器是指 CPU 中的寄存器,它保存的是程序当前执行的指令的地址(也可以说保存下一条指令的所在存储单元的地址),当 CPU 需要执行指令时,需要从程序计数器中得到当前需要执行的指令所在存储单元的地址,然后根据得到的地址获取到指令,在得到指令之后,程序计数器便自动加 1 或者根据转移指针得到下一条指令的地址,如此循环,直至执行完所有的指令。

虚拟机栈

技术分享图片

每个方法在执行的同时都会创建一个栈帧用于存储 局部变量表、操作数栈、动态链接、方法出口等信息。每个方法从调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程。

局部变量(包括在方法中声明的非静态变量以及函数形参)。对于基本数据类型的变量,则直接存储它的值,对于引用类型的变量,则存的是指向对象的引用。局部变量表的大小在编译器就可以确定其大小了,因此在程序执行期间局部变量表的大小是不会改变的。【 long 和 double 类型数据会占 2个局部空间其余数据类型只占 1 个】

操作数栈,想必学过数据结构中的栈的朋友想必对表达式求值问题不会陌生,栈最典型的一个应用就是用来对表达式求值。想想一个线程执行方法的过程中,实际上就是不断执行语句的过程,而归根到底就是进行计算的过程。因此可以这么说,程序中的所有计算过程都是在借助于操作数栈来完成的。

指向运行时常量池的引用,因为在方法执行的过程中有可能需要用到类中的常量,所以必须要有一个引用指向运行时常量。

方法返回地址,当一个方法执行完毕之后,要返回之前调用它的地方,因此在栈帧中必须保存一个方法返回地址。

本地方法栈

本地方法栈与Java栈的发挥的作用非常相似。区别只不过是Java栈是为执行Java方法服务的,而本地方法栈则是为执行本地方法(Native Method)服务的。在JVM规范中,并没有对本地方发展的具体实现方法以及数据结构作强制规定,虚拟机可以自由实现它。在HotSopt虚拟机中直接就把本地方法栈和Java栈合二为一。

方法区

存储已经被虚拟机加载类信息、常量、静态变量、即时编译器编译后的代码等数据。

在Class文件中除了类的字段、方法、接口等描述信息外,还有一项信息是常量池,用来存储编译期间生成的字面量和符号引用,这部分内容将雷加载后进入方法区的运行常量池中存放。

在方法区中有一个非常重要的部分就是运行时常量池,它是每一个类或接口的常量池的运行时表示形式,在类和接口被加载到JVM后,对应的运行时常量池就被创建出来。当然并非Class文件常量池中的内容才能进入运行时常量池,在运行期间也可将新的常量放入运行时常量池中,比如String的intern方法。

堆存放对象实例以及数组,需要进行垃圾回收,是垃圾收集器管理的主要区域。


每个划分的区域有什么特点?

程序计数器:线程私有,唯一一个不会有 OutOfMemoryError 情况的区域。

由于 Java 虚拟机的多线程时通过线程轮流切换并分配处理器执行时间的方式来实现的,在任何一个确定的时刻,一个处理器都只会执行一条线程中的指令,因此为了线程切换后能恢复到正确的执行位置,每条线程都需要有一个独立的程序计数器,各线程之间计数器互不影响。

虚拟机栈:线程私有,如果请求栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常,如果扩展时无法申请到足够的内存,就会抛出OutOfMemoryError异常。

本地方法栈:线程私有,有StackOverflowError异常和OutOfMemoryError异常。

方法区:线程共享,当方法区无法瞒住内存分配需要时,将抛出OutOfMemoryError异常。

:线程共享,如果堆中没有内存完成实例分配,并且堆也无法扩展时,就会抛出OutOfMemoryError异常。


划分的区域有没有随着jdk的变动有所改动?

在JVM规范中,没有强制要求方法区必须实现垃圾回收。很多人习惯将方法区称为“永久代”,是因为HotSpot虚拟机以永久代来实现方法区,从而JVM的垃圾收集器可以像管理堆区一样管理这部分区域,从而不需要专门为这部分设计垃圾回收机制。不过自从JDK7之后,Hotspot虚拟机便将运行时常量池从永久代移除了。

个人觉得其实应该还有些...


总结

参考
深入理解Java虚拟机
http://tec.5lulu.com/detail/110kcn1h9n13w8y6a.html
https://www.cnblogs.com/dolphin0520/p/3613043.html

主要是对于深入理解Java虚拟机的读后感,加油加油!

如果有什么错误欢迎指出,十分感谢!

以上是关于JVM内存模型的主要内容,如果未能解决你的问题,请参考以下文章

JVM内存模型Java内存模型 和 Java对象模型

JVM:JVM内存模型

JVM内存模型详解

细说JVM内存模型

JVM优化系列-------[3丶JVM的内存模型]

两种内存模型——JVM vs 计算机