前言

Java程序的运行是通过Java虚拟机来实现的。通过类加载器将class字节码文件加载进JVM,然后根据预定的规则执行。Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域。这些内存区域被统一叫做运行时数据区。Java运行时数据区大致可以划分为5个部分。如下图所示。在这里要特别指出,我们现在说的JVM内存划分是概念模型。具体到每个JVM的具体实现可能会有所不同。具体JVM的实现我只会提到HotSpot虚拟机的实现细节。

程序计数器

程序计数器是一块较小的内存空间,它可以看成是当前线程所执行的字节码的行号指示器。程序计数器记录线程当前要执行的下一条字节码指令的地址。由于Java是多线程的,所以为了多线程之间的切换与恢复,每一个线程都需要单独的程序计数器,各线程之间互不影响。这类内存区域被称为“线程私有”的内存区域。

由于程序计数器只存储一个字节码指令地址,故此内存区域没有规定任何OutOfMemoryError情况。

虚拟机栈

Java虚拟机栈也是线程私有的,它的生命周期与线程相同。虚拟机栈描述的是Java方法执行的内存模型:每个方法在执行时都会创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态链接、方法出口等信息。

一个栈帧就代表了一个方法执行的内存模型,虚拟机栈中存储的就是当前执行的所有方法的栈帧(包括正在执行的和等待执行的)。每一个方法从调用直至执行完成的过程,就对应着一个栈帧在虚拟机中入栈到出栈的过程。我们平时所说的“局部变量存储在栈中”就是指方法中的局部变量存储在代表该方法的栈帧的局部变量表中。而方法的执行正是从局部变量表中获取数据,放至操作数栈上,然后在操作数栈上进行运算,再将运算结果放入局部变量表中,最后将操作数栈顶的数据返回给方法的调用者的过程。(关于栈帧和基于栈的方法执行,我会在之后写两篇文章专门介绍。敬请期待☺)

虚拟机栈可能出现两种异常:由线程请求的栈深度过大超出虚拟机所允许的深度而引起的StackOverflowError异常;以及由虚拟机栈无法提供足够的内存而引起的OutOfMemoryError异常。

本地方法栈

本地方法栈与虚拟机栈类似,他们的区别在于:本地方法栈用于执行本地方法(Native方法);虚拟机栈用于执行普通的Java方法。在HotSpot虚拟机中,就将本地方法栈与虚拟机栈做在了一起。

本地方法栈可能抛出的异常同虚拟机栈一样。

Java堆是Java虚拟机所管理的内存中最大的一块。Java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例:所有的对象实例以及数组都要在堆上分配(The heap is the runtime data area from which memory for all class instances and arrays is allocated)。但Class对象比较特殊,它虽然是对象,但是存放在方法区里。在下面的方法区一节会介绍。Java堆是垃圾收集器(GC)管理的主要区域。现在的收集器基本都采用分代收集算法:新生代和老年代。而对于不同的”代“采用的垃圾回收算法也不一样。一般新生代使用复制算法;老年代使用标记整理算法。对于不同的”代“,一般使用不同的垃圾收集器,新生代垃圾收集器和老年代垃圾收集器配合工作。(关于垃圾收集算法、垃圾收集器以及堆中具体的分代等知识,我之后会专门写几篇博客来介绍。再次敬请期待☺)

Java堆可以是物理上不连续的内存空间,只要逻辑上连续即可。Java堆可能抛出OutOfMemoryError异常。

方法区

方法区与Java堆一样,是各个线程共享的内存区域。它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。

所有的字节码被加载之后,字节码中的信息:类信息、类中的方法信息、常量信息、类中的静态变量等都会存放在方法区。正如其名字一样:方法区中存放的就是类和方法的所有信息。此外,如果一个类被加载了,就会在方法区生成一个代表该类的Class对象(唯一一种不在堆上生成的对象实例)该对象将作为程序访问方法区中该类的信息的外部接口。有了该对象的存在,才有了反射的实现。

在Java7之前,HotSpot虚拟机中将GC分代收集扩展到了方法区,使用永久代来实现了方法区。这个区域的内存回收目标主要是针对常量池的回收和对类型的卸载。但是在之后的HotSpot虚拟机实现中,逐渐开始将方法区从永久代移除。Java7中已经将运行时常量池从永久代移除,在Java 堆(Heap)中开辟了一块区域存放运行时常量池。而在Java8中,已经彻底没有了永久代,将方法区直接放在一个与堆不相连的本地内存区域,这个区域被叫做元空间。

关于元空间的更多信息,请参考:Java永久代去哪儿了

运行时常量池

运行时常量池是方法区的一部分,关于运行时常量池的介绍,请参考我的另一篇博文:String放入运行时常量池的时机与String.intern()方法解惑。我还是花了些时间在理解运行时常量池上的。

直接内存

JDK1.4中引用了NIO,并引用了Channel与Buffer,可以使用Native函数库直接分配堆外内存,并通过一个存储在Java堆里面的DirectByteBuffer对象作为这块内存的引用进行操作。

如上文介绍的:Java8以及之后的版本中方法区已经从原来的JVM运行时数据区中被开辟到了一个称作元空间的直接内存区域。

参考:

《深入理解Java虚拟机-JVM高级特性与最佳实践》第二版 周志明著

JVM内存区域划分(JDK6/7/8中的变化)的更多相关文章

  1. JVM内存区域划分及垃圾回收

    第一部分.闲扯+概述 近来在研读<深入理解java虚拟机>一书,读完之后做个小结,算是记录一下自己的学习所得,在成长的路上,只能死磕. 要理解JVM,就要先从其内存区域划分开始,知道其由几 ...

  2. JVM内存区域划分

    前言 Java程序的运行是通过Java虚拟机来实现的.通过类加载器将class字节码文件加载进JVM,然后根据预定的规则执行.Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同 ...

  3. JVM内存区域划分Eden Space、Survivor Space、Tenured Gen,Perm Gen解释

    以下内容转自:http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=29632145&id=4616836 jvm区域总体分两 ...

  4. JVM内存区域划分Eden Space、Survivor Space、Tenured Gen,Perm Gen解释(转)

    jvm区域总体分两类,heap区和非heap区.heap区又分:Eden Space(伊甸园).Survivor Space(幸存者区).Tenured Gen(老年代-养老区). 非heap区又分: ...

  5. JVM内存区域划分Eden Space,Survivor Space,Tenured Gen,Perm Gen

    jvm区域总体分两类,heap区和非heap区.heap区又分:Eden Space(伊甸园).Survivor Space(幸存者区).Tenured Gen(老年代-养老区). 非heap区又分: ...

  6. JVM内存区域划分总结

    发现网上有两个版本的JVM内存划分,一个是按照<深入理解JVM虚拟机>上的版本,包含程序计数器等,按照是否线程共享划分. 另一个我觉得更好记一些,也更适合我自己,在这里记录一下. 首先上思 ...

  7. [转]JVM内存区域划分Eden Space、Survivor Space、Tenured Gen,Perm Gen解释

    jvm区域总体分两类,heap区和非heap区.heap区又分:Eden Space(伊甸园).Survivor Space(幸存者区).Tenured Gen(老年代-养老区). 非heap区又分: ...

  8. JVM 内存区域划分

    一.运行时数据区包括哪几部分? 根据<Java虚拟机规范>的规定,运行时数据区通常包括这几个部分:程序计数器(Program Counter Register).Java栈(VM Stac ...

  9. JVM内存区域划分Eden Space、Survivor Space、Tenured Gen,Perm Gen解释 (生动形象)

    [转自]:https://blog.csdn.net/sd4015700/article/details/50109939 jvm区域总体分两类,heap区和非heap区.heap区又分:Eden S ...

随机推荐

  1. angualar2——八大组成

    Angular2 模块 理解: Angular 应用是模块化的,并且 Angular 有自己的模块系统,它被称为 Angular 模块或 NgModules. 组件 组件是一个项目主干,一个模块由多个 ...

  2. 【java】doc转pdf

    市场上主流的 WORD 转 PDF 工具有两个:OpenOffice 和 Microsoft Office 转换插件,可以通过部署这两个工具实现 WORD 转 PDF 功能. 1: Microsoft ...

  3. ML笔记:Classification: Logistic Regression

  4. Golang中Struct与DB中表字段通过反射自动映射 - sqlmapper

    Golang中操作数据库已经有现成的库"database/sql"可以用,但是"database/sql"只提供了最基础的操作接口: 对数据库中一张表的增删改查 ...

  5. 推送本地项目至Github遇到的问题以及解决办法记录

    在把本地新项目推送至GitHub仓库时的大致流程和步骤,首先现在GitHub上面新建一个项目,复制该项目的 带.git 后缀的地址,比如 git@github.com:XXX/XXX.git 然后在本 ...

  6. Codeforces Round #452 F. Letters Removing

    Description Petya has a string of length n consisting of small and large English letters and digits. ...

  7. hdu 5119(2014北京)

    题意: 随机选择一个数,如果后面有比他小的就进行交换,直到没有为止(算一轮).求多少轮后为递增序列 思路: 倒着找,如果有比经过的最小数大的,ans+1 #include <iostream&g ...

  8. 四个常用的 Rewrite 使用范例

    一.防盗链功能只这四行就实现了防盗链,原理是利用REFERER判断网页来源,缺点是REFERER容易伪造. RewriteEngine On RewriteCond %{HTTP_REFERER} ! ...

  9. 第五次C语言作业

    (一)改错题 输出华氏摄氏温度转换表:输入两个整数lower和upper,输出一张华氏摄氏温度转换表,华氏温度的取值范围是{lower,upper},每次增加2℉.计算公式如下: c = 5×(f-3 ...

  10. 以太坊区块链Java(EthereumJ)学习笔记:概述

    本系列文章介绍以太坊区块链基于Java语言的解决方案.通过介绍EthereumJ定义的主要模块和Class,希望为大家学习和使用EthereumJ提供一些帮助. 整体架构 以太坊的Java解决方案主要 ...