本系列笔记目前主要基于《深入理解Java虚拟机:JVM高级特性与最佳实践 第2版》,后续还会加入《实战Java虚拟机:JVM故障诊断与性能优化》、《Java性能权威指南》、《Java性能优化权威指南》的阅读笔记。

概述

Java 虚拟机为程序员分担了很多内存管理的工作,不再像 C/C++ 那样容易出现内存泄漏和内存溢出问题了,也正是这样,导致一旦出现了内存泄漏和溢出方面的问题,就难以排查。因此一个优秀的 Java 程序员应该对 Java 虚拟机有充足的了解,JVM 是你的必修课。

运行时数据区域

根据《Java虚拟机规范(Java SE 7版)》,JVM 所管理的内存区域的划分如下:

image

每个内存区域都有各自的用途,以及创建和销毁时间,有的区域会随着虚拟机的启动而存在,有的区域依赖用户线程而建立和销毁,接下来一次介绍这些内存区域。程序计数器

程序计数器(Program Counter Register)是线程私有的内存区域,是当前线程所执行的字节码的行号指示器,是一块较小的内存空间。

字节码解释器(java源码编译成字节码,运行时解释成机器码执行)工作时,就是通过改变程序计数器的值,来选取下一条要执行的字节码。分支、循环、跳转等都依赖程序计数器执行。

当线程执行 Java 方法时,程序计数器记录的是正在执行的虚拟机字节码指令地址;当线程执行 Native 方法时,程序计数器的值为空(Undefined)。程序计数器是唯一一个在 Java 虚拟机规范中没有规定 OutOfMemoryError 的内存区域。

Java虚拟机栈

Java 虚拟机栈(Java Stack)也是线程私有的内存区域,它的生命周期与线程相同。虚拟机栈描述的是 Java 方法执行的内存模型:每个方法在执行时都会创建一个栈帧(Stack Frame)用以存储局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法从开始调用到执行完成的过程,就对应一个栈帧在虚拟机栈中从入栈到出栈的过程。

局部变量表存放了编译期可知的各种基本数据类型(boolean byte char short int long double float)、对象引用,以及 returnAddress 类型(指向一条字节码指令的地址)。局部变量表所需内存空间在编译期间完成分配,当进入一个方法时,需要在栈帧中分配多大的局部变量空间是完全确定的,在方法运行期间不会改变局部变量表大小。

在 Java 虚拟机规范中,Java 虚拟机栈有可能会出现两种异常:StackOverflowError 和 OutOfMemoryError。如果线程请求的栈深度大于虚拟机栈的深度,则会 StackOverflowError。如果虚拟机栈动态扩展时申请不到足够的内存,则会 OutOfMemoryError。

本地方法栈

本地方法栈(Native Method Stack)与 Java 虚拟机栈的作用一样,是线程私有的,区别就是 Java 虚拟机栈为执行 Java 方法服务,本地方法栈为 Native 方法服务,虚拟机规范并没有对本地方法栈做强制规定,在 HotSpot 虚拟机中把本地方法栈和虚拟机栈合二为一了。此内存区域也会抛出 StackOverflowError 和 OutOfMemoryError。

Java堆

Java 堆(Java Heap)是一块被所有线程共享的内存区域,同时也是 Java 虚拟机所管理的内存中最大的一块,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。

Java 堆是垃圾收集器管理堆主要区域,也被称做“GC堆”。现在垃圾收集器基本都采用分代收集算法,所以从内存回收角度看,Java 堆还可以细分为:新生代和老年代;新生代可以再细分为 Eden 空间、From Survivor 空间、To Survivor空间。

如下图所示:

image

从内存分配角度看,线程共享的 Java 堆中可能划分出多个线程私有的分配缓冲区(Thread Local Allocation Buffer,TLAB)。虚拟机为新生对象分配内存时,为每个线程在 Java 堆 中预先分配一小块内存,称做本地线程分配缓冲(TLAB)。哪个线程要分配内存,就在哪个线程的 TLAB 上分配,只有 TLAB 用完并分配新的 TLAB 时,才会同步锁定(为了并发情况下的线程安全)。

Java 堆可以处于物理上不连续的内存空间中,只要逻辑上是连续的即可。如果堆中没有足够内存完成实例分配,并且也无法扩展时,会抛出 OutOfMemoryError。

方法区

方法区(Method Aera)与 Java 堆一样,也是线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。在虚拟机规范中,它被描述为堆的一个逻辑部分,但实际它应该要与 Java 堆区分开来。

从分代收集的角度看,方法区也被称做永久代(Permanent Generation),实际上两者并不等价,只是因为 HotSpot 虚拟机使用永久代实现了方法区,对于其他虚拟机(JRockit、IBM J9)是不存在永久代概念的。

使用永久代实现方法区,更容易出现内存溢出问题(永久代有 -XX:MaxPermSize 的上限),所以在 JDK1.8 中,HotSpot 就取消了永久代(JEP122),取而代之的是元空间(MetaSpace),元空间是方法区新的实现,而且使用的是本地内存不是虚拟机内存。原先永久代中类的元信息会放入元空间,类的静态变量和常量会放入 Java 堆。

永久代也并不是指真的“永久”存在,只是说这部分内存回收(常量池回收和对类型的卸载)的成绩难以令人满意,条件也非常苛刻。

方法区会有 OutOfMemoryError 异常。

运行时常量池

运行时常量池(Runtime Constant Pool)是方法区的一部分。Class文件中除了有类的版本信息、字段、方法、接口等描述信息外,还有一项信息就是常量池,用于存放编译期间生成的各种字面量和符号引用,这部分内容将在类加载后进入方法区的运行时常量池中,另外翻译出来的直接引用也会存储在这个区域中。

这个区域另外一个特点就是动态性,Java并不要求常量就一定要在编译期间才能产生,运行期间也可以在这个区域放入新的内容,String.intern()方法就是这个特性的应用。此区域有 OutOfMemoryError 异常。

在JDK1.6及之前,常量池是位于方法区中的,但在JDK1.7的时候常量池挪到了堆内存,也就是常量池和对象共享 Java 堆,所以在 Java7 以后,常量池就不在方法区分配了,而是在 Java 堆 中分配。

直接内存

直接内存(Direct Memory)并不是虚拟机运行时数据区的一部分,也不是 Java 虚拟机规范中定义的内存区域。但这部分区域被频繁的使用,也可能导致 OutOfMemory 异常出现。

JDK1.4 中加入了 NIO,这是一种基于通道(Channel)和缓冲区的 I/O 方式,它可以使用 Native 函数库直接分配堆外内存,然后通过一个存储在 Java 堆中的 DirectByteBuffer 对象作为这块内存的引用进行操作。这样显著提高了性能,因为避免了在 Java 堆和 Native 堆中来回复制数据。显然,本机直接内存的分配不会受到 Java 堆大小的限制,但是,既然是内存,肯定还是会受到本机总内存(包括RAM、SWAP区)大小以及处理器寻址空间的限制。在配置虚拟机参数时,会根据实际内存配置 -Xmx 等,但经常忽略直接内存,使得各个内存区域总和大于了物理内存,从而导致动态扩展时出现 OutOfMemoryError。

元空间和直接内存_JVM探秘:Java内存区域相关推荐

  1. java jvm 查看内存_JVM:查看java内存情况命令

    jinfo:可以输出并修改运行时的java 进程的opts. jps:与unix上的ps类似,用来显示本地的java进程,可以查看本地运行着几个java程序,并显示他们的进程号. jstat:一个极强 ...

  2. linux查看java虚拟机内存_JVM:查看java内存情况命令

    jmap (linux下特有,也是很常用的一个命令) 观察运行中的jvm物理内存的占用情况. 参数如下: -heap :打印jvm heap的情况 -histo: 打印jvm heap的直方图.其输出 ...

  3. java 堆内存结构_JVM内存结构、Java内存模型和Java对象模型

    JVM内存结构 我们都知道,Java代码是要运行在虚拟机上的,而虚拟机在执行Java程序的过程中会把所管理的内存划分为若干个不同的数据区域,这些区域都有各自的用途.其中有些区域随着虚拟机进程的启动而存 ...

  4. JVM自动内存管理机制——Java内存区域(下)

    一.虚拟机参数配置 在上一篇<Java自动内存管理机制--Java内存区域(上)>中介绍了有关的基础知识,这一篇主要是通过一些示例来了解有关虚拟机参数的配置. 1.Java堆参数设置 a) ...

  5. JVM(一)JVM虚拟机内存结构 和 JAVA内存模型(JMM)

    本文转自:浅析java内存模型--JMM(Java Memory Model) - 路易小七 - 博客园,尊重作者,转载请注明出处~ JVM虚拟机内存结构 和 JAVA内存模型 是两个不同的概念 JV ...

  6. JVM内存模型 和 Java内存模型 对比学习

    前言 首先要知道这两者不是同一个东西,Jvm内存模型 也叫 Java内存区域.Java运行时数据区域 而Java内存模型 是 JMM (Java Memory Model,简称 JMM),是定义了线程 ...

  7. JVM之深入理解JVM内存结构(Java内存结构/Java内存区域)、Java内存模型

    Java作为一种面向对象的,跨平台语言,其对象.内存等一直是比较难的知识点.而且很多概念的名称看起来又那么相似,很多人会傻傻分不清楚.比如本文我们要讨论的JVM内存结构.JAVA内存结构.JAVA内存 ...

  8. Java内存管理:Java内存区域 JVM运行时数据区

    Java内存管理:Java内存区域 JVM运行时数据区 在前面的一些文章了解到javac编译的大体过程.Class文件结构.以及JVM字节码指令. 下面我们详细了解Java内存区域:先说明JVM规范定 ...

  9. Java内存模型和Java内存结构精读

    以前看过很多遍JVM相关知识的文章,无非都是直接来张图片开搞,一来就甩张图片上来,这是方法区,这是堆,这是线程独享的一堆堆的概念,看得真的是头大,死记硬背也记不下来,更别说理解了. 最近一段时间在看j ...

  10. 区分 JVM 内存结构、 Java 内存模型 以及 Java 对象模型 三个概念

    本文由 简悦 SimpRead 转码, 原文地址 https://www.toutiao.com/i6732361325244056072/ 作者:Hollis 来源:公众号Hollis Java 作 ...

最新文章

  1. 触摸屏之linux3.4.2安装tslib
  2. 金蝶系统怎么清理服务器,【金蝶KIS专业版】如何注销加密服务器记录
  3. 4016-二叉排序树的判定(C++,附思路)
  4. LoadRunner调用Oracle存储过程
  5. VC++ SetLayeredWindowAttributes 部分窗口透明鼠标穿透
  6. VS2010/VS2012/VS2015下openGL环境配置(转)
  7. Java动态规划走金字塔_【动态规划基础】数字金字塔
  8. php将数组转成字符串的,php怎样将数组转化成字符串
  9. 防抖 节流_坚持造轮子第二天 防抖与节流
  10. ubuntu系统瘦身-清理系统垃圾文件
  11. 批量下载基因的蛋白质氨基酸序列
  12. 利用requests库批量下载PDF文件
  13. 我的世界刷猪人塔java版_我的世界1.11.2自动猪人塔制作指南 猪人塔存档下载 | 我的世界 | MC世界侠...
  14. 普乐蛙大型5d动感影院4d影院设备价格4d动感影院座椅
  15. 电池三段式充电 matlab,一种锂离子电池三段式充电方法与流程
  16. 嵌入式系统基础:点阵汉字的字模读取与显示
  17. 计算机学院早操规定,柚通知 | 南京邮电大学早操管理规定(暂行)
  18. layui 的简单使用
  19. 闲来无事,写一款扎金花小游戏,先练练手,打的时候好赢钱
  20. DirectX11学习笔记十 imGUI入坑

热门文章

  1. ruby 安装和管理
  2. SkGradientShader::CreateLinear 的参数。
  3. javaweb学习总结(四十三)——Filter高级开发
  4. 解决JDBC连接MySQL时发出的警告WARN: Establishing SSL connection without server‘s identity verification ...
  5. 新版谷歌浏览器怎么查找和改变编码格式
  6. 【大型网站技术实践】初级篇:海量图片的分布式存储设计与实现
  7. Linux的vagrant配置vmware
  8. Yii的Querybuild ActiveRecord
  9. PHP的mysqli操作DB
  10. PHP json_encode不转义中文