本系列笔记目前主要基于《深入理解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. Docker容器中数据两种持久化存储方式:卷和挂载宿主目录
  2. java反射po转vo_三步走使用Dozer (Do,Po,Vo转换工具)
  3. python读取文件夹-Python按顺序读取文件夹中文件
  4. make编译工具使用
  5. mysql 有数据更新数据_MySQL之——实现无数据插入,有数据更新
  6. 鲁大师2014绿色版 v3.75.14.1058 免费版
  7. 以下哪些属于计算机应用领域,以下哪些计算机的应用领域?()
  8. python装饰图解_Python装饰器(Decorate)使用图解
  9. 多元回归函数regress的用法
  10. oracle 表空间转换,Oracle表空间数据文件移动的方法
  11. Windows7下网上银行U盾无法使用的解决办法
  12. python3攻击服务器_Python服务器用套接字互相攻击
  13. oracle数据库关闭失败,Oracle突然关闭原因
  14. 新年贺词大全 喜欢你就顶啦~
  15. Linux系统【Ubuntu】开机自启脚本及配置
  16. 前端如何设置字体滚动,及向左移动
  17. 如何用计算机把英文变成中文,电脑英文系统怎么设置回中文
  18. 作为360老员工,谈谈和周鸿祎的接触和印象
  19. Graphics基本用法
  20. windows环境下MySQL服务端和客户端安装,超详细

热门文章

  1. vSphere ESX 4 安装图解
  2. C#委托Action、ActionT、FuncT、PredicateT
  3. java.lang.IllegalStateException Unable to find a @SpringBootConfiguration错误解决方案(亲测)
  4. Xshell无法启动:要继续使用此程序,您必须应用最新的更新或使用新版本
  5. ELK+Kafka 企业日志收集平台(二)这是原版
  6. PHP框架的ORM思想:O类的实例化 R数据表 M映射XML
  7. android 上线apk,码云 Android apk 在线构建功能上线啦!
  8. android+5.0+ble,android5.0(Lollipop) BLE Peripheral牛刀小试(示例代码)
  9. android 仿360浮动,Android--模仿360底部导航按钮
  10. uniapp连接php,thinkphp5 对接手机uni-app的unipush推送(个推)