JVM的组成分为整体组成部分和运行时数据区组成部分。

JVM的整体组成

JVM的整体组成可以分为4个部分:类加载器(Classloader)、运行时数据区(Runtime Data Area)、执行引擎(Execution Engine)和本地库接口(Native Interface)。

程序在执行之前,先要把Java代码编译成字节码(.class文件),JVM首先需要把字节码通过类加载器把文件加载到内存中的运行时数据区中。因为字节码文件是JVM的一套指令集规范,并不能直接交给底层操作系统去执行,因此需要执行引擎将字节码翻译成底层系统指令再交由CPU去执行,而这个过程中需要调用本地库接口来实现整个程序的功能。

我们通常所说的JVM组成指的是运行时数据区,因为通常需要程序员调试分析的区域就是运行时数据区,或者更具体地说是运行时数据区里面的堆(Heqp)模块。

JVM的运行时数据区组成

JVM的运行时数据区,不同虚拟机实现可能略微有所不同,但都会遵从Java虚拟机规范。Java8的虚拟机规范规定,Java虚拟机所管理的内存将会包括以下几个运行时数据区域:程序计数器(Program Counter Register)、Java虚拟机栈(Java Virtual Machine Stacks)、本地方法栈(Native Method Stack)、Java堆(Java Heap)和方法区(Method Area)。

程序计数器(Program Counter Register)

程序计数器是一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器。在虚拟机的概念模型里,字节码解析器的工作是通过改变这个计数器的值来选取下一条需要执行的字节码指令,比如分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。

程序计数器的特性是内存私有。由于JVM的多线程是通过线程轮流切换并分配处理器执行时间的方式来实现的,也就是说在任何时刻,一个处理器(或者说一个内核)都只会执行一条线程中的指令。为了线程切换后额能够恢复到正确的执行位置,每个线程都有独立的程序计数器。

程序计数器是没有异常规定的。如果线程正在执行Java中的方法,程序计数器记录的就是正在执行虚拟机字节码指令的地址,但如果是Naive方法,这个计数器就为空(undefined)。因此该内存区域是唯一一个在Java虚拟机规范中没有规定OutOfMemoryError的区域。

Java虚拟机栈(Java Virtual Machine Stacks)

Java虚拟机栈描述的是Java方法执行的内存模型,每个方法在执行的同时都会创建一个线帧(Stack Frame)用来存储局部变量表、操作数栈、动态链接、方法出口等信息,每个方法从调用直至执行完成的过程,都对应着一个线帧在虚拟机栈中入栈到出栈的过程。

Java虚拟机栈的特性是内存私有,生命周期和线程相同。

Java虚拟机栈的异常规定是StackOverflowError和OutOfMemeoryError。如果线程请求的栈深度大于虚拟机所允许的栈深度,就会抛出StackOverflowError异常;如果虚拟机是可以动态扩展的,当扩展时无法申请到足够的内存就会抛出OutOfMemoryError异常。

本地方法栈(Native Method Stack)

本地方法栈与虚拟机栈的作用是一样的,只不过虚拟机栈是服务于Java方法的,而本地方法栈是虚拟机调用Native方法服务的。在Java虚拟机规范中对于本地方法栈没有特殊的要求,虚拟机可以自由实现它,因此在Sun HotSpot虚拟机实现中直接把本地方法栈和虚拟机栈合二为一了。

本地方法栈的特性和异常规定和Java虚拟机栈相同。

Java堆(Java Heap)

Java堆是Java虚拟机中内存最大的一块,是被所有线程共享的,在虚拟机启动时创建。Java堆唯一的目的就是存放对象实例,几乎所有的对象实例都在这里分配内存,随着JIT编译器的发展和逃逸分析技术的逐渐成熟,栈上分配、标量替换优化的技术将会导致一些微妙的变化,所有的对象都分配在堆上渐渐变得不那么绝对了。

Java堆的特性是内存共享,即所有线程共享Java堆的内存。

Java堆的异常规定是OutOfMemoryError。如果在堆中没有内存完成实例分配,并且堆不可以再扩展时,将会抛出OutOfMemoryError。

方法区(Method Area)

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

很多人把方法区称为永生代,实际上是错误的,本质上两者并不等价。会产生这样的理解误区只是因为HotSpot虚拟机垃圾回收器团队把GC分代收集扩展到了方法区,或者说是用永生代来实现方法区而已,这样做是为了省去专门为方法区编写内存管理的代码。但是再JDK8中也移除了永生代的概念,使用Native Memory来实现方法区。

方法区的特性是内存共享。

方法区的异常规定是OutOfMemoryError。当方法无法满足内存分配需求的时候会抛出OutOfMemoryError异常。

JVM的其它内存分配知识

运行时常量池

运行时常量池是方法区的一部分,Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池(Constant Pool Table),是用来存放编译器生成的各种字面量和符号引用的。这部分在类加载后进入方法区的运行时常量池中,如String的intern()方法。

直接内存(Direct Memory)

直接内存并不是虚拟机运行时数据区的一部分,但是这部分内存也会被频繁使用,而且可能会导致OutOfMemoryError。在JDK1.4中新加入了一个NIO类,引入了一种基于Channel与缓冲区Buffer的IO方式,它通过一个存储在Java堆中的DirectByteBuffer对象作为这块内存的引用操作,它因此更高效,它避免了Java堆和Native堆来回交换数据的时间。

要注意的是,直接内存的分配虽然不会受到Java堆大小的限制,但是会受到本机总内存大小的限制。在设置虚拟机参数的时候,不能忽略直接内存,如果把实际内存设置为-Xmx,使得内存区域的总和大于物理内存的限制,会导致动态扩展时出现OutOfMemoryError异常。

总结

程序计数器、虚拟机栈和本地方法都是私有内存,会随着线程而生,随着线程而灭,通常情况下开发人员不需要关心,Java虚拟机也没有开放让你关系的权力。而Java堆作为最大的内存区域,则是开发人员需要重点关心的内存区域。还有方法区和运行时常量区与永生代的关系也是开发人员该侧重关心的重点。

"你在哪里呢,做着什么事情,是怎样的心情。如果可以,你愿意和我说说嘛。"

jvm的组成入门的更多相关文章

  1. JVM内存模型入门

    JVM内存模型入门 本文是学习笔记,原文地址在:https://www.bilibili.com/video/av62009886 综述 其实没有太多新东西 JVM主要分为五个区域:栈区.堆区.本地方 ...

  2. 想写一篇jvm的工具入门

    为什么要写一个jvm的工具入门呢,一是自己也不会,二是因为不会所以想学一下,还有就是这个确实很重要,对学习jvm有很多的用处,对定位问题有很大的便利,以前毕业那会我记得有个笔试,知道JAVA_HOME ...

  3. JVM性能优化入门指南

    兵器谱 jps 列出正在运行的虚拟机进程,用法如下: jps [-option] [hostid] 选项 作用 q 只输出LVMID,省略主类的名称 m 输出main method的参数 l 输出完全 ...

  4. JVM调优入门之初探

    JVM:程序计数器,jvm栈,本地方法栈,堆,方法区 JVM:虚拟机内存又分有:年轻代(eden,servivor s0,servivor s1),年老代(tenured),永久代() 问题1:如何查 ...

  5. [零] Java 语言运行原理 JVM原理浅析 入门了解简介 Java语言组成部分 javap命令使用

    Java Virtual Machine  官方介绍 Java虚拟机规范官方文档 https://docs.oracle.com/javase/specs/index.html 其中以java8的为 ...

  6. JVM基础快速入门篇

    Java是一门可以跨平台的语言,但是Java本身是不可以实现跨平台的,需要JVM实现跨平台.javac编译好后的class文件,在Windows.Linux.Mac等系统上,只要该系统安装对应的Jav ...

  7. 如何把Java代码玩出花?JVM Sandbox入门教程与原理浅谈

    在日常业务代码开发中,我们经常接触到AOP,比如熟知的Spring AOP.我们用它来做业务切面,比如登录校验,日志记录,性能监控,全局过滤器等.但Spring AOP有一个局限性,并不是所有的类都托 ...

  8. 一文带你深入理解JVM,看完之后你还敢说你懂JVM吗?颠覆you认知

    前言 今天带大家深入理解JVM,从入门到精通,希望大家能够喜欢~~~ 概念 JVM是可运行 Java 代码的假想计算机 ,包括一套字节码指令集.一组寄存器.一个栈.一个垃圾回收,堆 和 一个存储方法域 ...

  9. 【Java】几道让你拿offer的知识点

    前言 只有光头才能变强 之前在刷博客的时候,发现一些写得比较好的博客都会默默收藏起来.最近在查阅补漏,有的知识点比较重要的,但是在之前的博客中还没有写到,于是趁着闲整理一下. 文本的知识点: Inte ...

随机推荐

  1. .NET MVC5简介(六)HttpHandler

    浏览器到网站程序 上一篇中,介绍IHttpModule的时候,自定义一个类CustomHttpModule继承自IHttpModule,自定义一个事件,并配合配置文件,就可以执行自定义Module中的 ...

  2. vue-商品管理案例改进

    案例改进 vue-resource全局配置: Vue.http.options.root = 'http://vue.studyit.io/'; 全局启用 emulateJSON 选项 Vue.htt ...

  3. Spring Boot 中如何定制 Banner

    本人免费整理了Java高级资料,涵盖了Java.Redis.MongoDB.MySQL.Zookeeper.Spring Cloud.Dubbo高并发分布式等教程,一共30G,需要自己领取.传送门:h ...

  4. 易优CMS:小白学代码之notempty

    [基础用法] 名称:notempty 功能:判断某个变量是否为空,可以嵌套到任何标签里面使用,比如:channel.type等 语法: {eyou:notempty name='$eyou.field ...

  5. Spring之 JDBC 异常

    JDBC异常抽象 Spring会将数据操作的异常转换为DataAccessException 解析错误码 SQLErrorCodeSQLExceptionTranslator ErrorCode定义 ...

  6. Flask(Jinja2) 服务端模板注入漏洞(SSTI)

    flask Flask 是一个 web 框架.也就是说 Flask 为你提供工具,库和技术来允许你构建一个 web 应用程序.这个 wdb 应用程序可以使一些 web 页面.博客.wiki.基于 we ...

  7. Android App自动更新解决方案(DownloadManager)

    一开始,我们先向服务器请求数据获取版本 public ObservableField<VersionBean> appVersion = new ObservableField<&g ...

  8. mssql sqlserver 使用sql脚本实现相邻两条数据相减的方法分享

    摘要: 下文讲述使用sql脚本实现相邻两条数据相减的方法,如下所示: 实验环境:sql server 2008 R2 实现思路: 1.使用cte表达式,对当前表进行重新编号 2.使用左连接对 表达式 ...

  9. fiddler---Fiddler抓取https协议

    上一篇简单的介绍了Fiddler的使用方法和页面布局介绍,Fiddler默认抓取的是HTTP协议,现在的协议基本上都是基于HTTPS协议的,今天写一篇如何通过Fiddler抓取HTTPS协议 什么是H ...

  10. 使用ghpage(github服务)搭建文档网站几种方式

    可以通过github提供的ghpage服务来搭建网站,有以下三种方式来实现: 1.文档放在master分支,作为一个子目录. 仓库:https://github.com/Ourpalm/ILRunti ...