先上一张JVM体系结构图:

1)运行时数据区:经过编译生成的字节码文件(class文件),由class loader(类加载子系统)加载后交给执行引擎执行。在执行引擎执行的过程中产生的数据会存储在一块内存区域。这块内存区域就是运行时区域

2)程序计数器:用于记录当前线程的正在执行的字节码指令位置。由于虚拟机的多线程是切换线程并分配cpu执行时间的方式实现的,不同线程的执行位置都需要记录下来,因此程序计数器是线程私有的

3)虚拟机栈:虚拟机栈是java方法执行的内存结构,虚拟机会在每个java方法执行时创建一个“栈桢”,用于存储局部变量表,操作数栈,动态链接,方法出口等信息。当方法执行完毕时,该栈桢会从虚拟机栈中出栈。其中局部变量表包含基本数据类型和对象引用;

   在java虚拟机规范中,对这个区域规定了两种异常状态:如果线程请求的栈的深度大于虚拟机允许的深度,将抛出StackOverFlowError异常(栈溢出),如果虚拟机栈可以动态扩展(现在大部分java虚拟机都可以动态扩展,只不过java虚拟机规范中也允许固定长度的java虚拟机栈),如果扩展时无法申请到足够的内存空间,就会抛出OutOfmMemoryError异常(没有足够的内存)

4)本地方法栈:类似java方法的执行有虚拟机栈,本地方法的执行则对应有本地方法栈

5)方法区:用于存储已被虚拟机加载的类信息,常量,静态变量,即时编译器编译后的代码等数据。线程共享(看存储的数据就知道了)

java虚拟机规范对方法区的限制非常宽松,除了和java堆一样不需要连续的内存和可以选择固定大小或者可扩展外,还可以选择不实现垃圾收集。相对而言,垃圾收集在这个区域是比较少出现的,但并非数据进入了方法区就如永久代的名字一样永久存在了。这区域的内存回收目标重要是针对常量池的回收和类型的卸载,一般来说这个内存区域的回收‘成绩’比较难以令人满意。尤其是类型的卸载条件非常苛刻,但是这部分的回收确实是必要的。在sun公司的bug列表中,曾出现过的若干个严重的bug就是由于低版本的HotSpot虚拟机对此区域未完成回收导致的内存溢出。

6)java堆(java Heap):堆的主要作用是存放程序运行过程中创建的对象实例,因为要存放的对象实例有可能会极多,因此也是虚拟机内存管理中最大的一块。并且由于硬件条件有限,所以需要不断回收已“无用”的实例对象来腾出空间给新生成的实例对象;因此java的垃圾回收主要是针对堆进行回收的(还有方法区的常量池),java堆很多时候也被称为GC堆(Garbage Collected Heap)。

7)类加载机制(Class Loader):类加载子系统是根据一个类的全限定名来加载该类的二进制流到内存中,在JVM中将形成一份描述Class结构的元信息对象(方法区),通过该元信息对象可以获知Class的结构信息:如构造函数,属性和方法等,Java允许用户借由这个Class相关的元信息对象间接调用Class对象的功能。

好!说了这么多关键字,再拿例子来讲解一下这些关键字:

A.图1是我们写的HelloWorld.java,通过IDE或命令:javac HelloWorld 编译生成16进制的HelloWorld.class(字节码文件,见图3),想读懂16进制字节可参考:一文让你明白java字节码 ;但一般IDE会自动转译成图2的指令;或者通过命令:javap -verbose HelloWorld 进行转译。

(图1)HelloWorld.java

(图2)HelloWorld.class

(图3)16进制的字节码:

B.接着,当我们通过IDE或者命令:java HelloWorld 运行这个class文件时,字节码文件(class文件)通过类加载机制加载完毕交付给执行引擎执行;类加载机制把HelloWrold类的信息、静态变量(例子中没加)、常量(例子中没加,常量会加载到方法区的常量池,这和静态变量不一样)等加载到方法区中,接下来如果需要创建该类的对象,需要通过new后面带的参数到方法区进行查找类相关信息。

C.类加载完后,虚拟机会检查程序的入口,虚拟机中程序的执行入口为main函数,如HelloWorld.class中,,执行引擎找到main函数开始执行指令,并生成一个“桢栈”入栈至虚拟机栈的栈顶;我们可以看到(图2)在main方法下面的命令:0  new java.lang.StringBuilder [16]  表示创建一个String对象,创建的String对象实例会在java堆(Heap)中分配内存存储(Java对象在JVM中的创建过程可以看这篇文章:Java对象是怎么创建的(通过对象的创建,了解JVM内存结构)),并把该指令位置“0”记录到当前线程的程序计数器中;3  dup 然后把该对象的引用压入虚拟机栈中,并把该指令位置“3”记录到当前线程的程序计数器中;4  ldc <String "Hello"> [18] 从字符串常量池(从jdk1.7开始,字符串常量池被移动到java堆)加载字符串常量Hello,并更新指令位置到程序计数器;...如果执行过程中有本地方法的指令,则会在本地方法栈中进行出入栈;这里有个点注意一下,请看main函数指令16的位置: 16  new java.lang.StringBuilder [31] 这里创建了一个StringBuilder对象,自jdk5开始已对这种类型的字符串拼接进行了优化,具体自行谷歌补充。

D.执行引擎执行指令过程中,按需调用本地库接口以执行本地库方法,如new指令、输出屏幕等操作

以上就是一个HelloWorld执行过程在JVM中发生的事情。

参考:

《深入理解Java虚拟机:JVM高级特性与最佳实践》一书

https://www.cnblogs.com/IUbanana/p/7067362.html

一个“Hello World”理解JVM运行时数据区的更多相关文章

  1. JVM运行时数据区和垃圾回收机制

    最近参考各种资料,尤其是<深入理解Java虚拟机 JVM高级特性和最佳实践>,大牛之作.把最近学习的Java虚拟机组成和垃圾回收机制总结一下. 你不会的都是新知识,学无止境,每天进步一点点 ...

  2. Jvm运行时数据区

    一:运行时数据区 Java虚拟机在执行Java程序的过程中会把它管理的内存分为若干个不同的数据区域.这些区域有着各自的用途,一级创建和销毁的时间,有的区域随着虚拟机进程的启动而存在,有些区域则依赖用户 ...

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

    转自:https://blog.csdn.net/tjiyu/article/details/53915869 下面我们详细了解Java内存区域:先说明JVM规范定义的JVM运行时分配的数据区有哪些, ...

  4. JVM 运行时数据区 (三)

    JVM运行时数据区 运行时数据区由 程序计数器.java虚拟机栈.本地方法栈.堆.方法区 组成: 1.程序计数器 每一个Java线程都有一个程序计数器,用于保存程序执行到当前方法的哪一个指令,它是线程 ...

  5. JVM总结(一):概述--JVM运行时数据区

    大三下,趁着寒假重温一遍JVM,准备在一个系列来总价一下学习JVM的整个过程.争取在接下来的一个星期内更新完这一个系列,然后回家过年. JVM运行时数据区 线程私有的数据区 程序计数器 虚拟机栈 本地 ...

  6. JVM运行时数据区与JVM堆内存模型小结

    前提 JVM运行时数据区和JVM内存模型是两回事,JVM内存模型指的是JVM堆内存模型. 那JVM运行时数据区又是什么? 它包括:程序计数器.虚拟机栈.本地方法栈.方法区.堆. 来看看它们都是干嘛的 ...

  7. Java中的字符串常量池和JVM运行时数据区的相关概念

    什么是字符串常量池 JVM为了减少字符串对象的重复创建,其维护了一个特殊的内存,这段内存被成为字符串常量池或者字符串字面量池 工作原理 当代码中出现字面量形式创建字符串对象时,JVM首先会对这个字面量 ...

  8. JVM运行时数据区及对象在内存中初始化的过程

    JVM运行时数据区 Java虚拟机所管理的内存区域,也称为运行时数据区,分为以下几个运行时数据区,如图所示 程序计数器:当前程序所执行字节码的行号指示器 程序计数器(Program Counter R ...

  9. JVM 运行时数据区(二)

    @ 目录 运行时数据区 共享区 堆区 方法区 隔离区 虚拟机栈 栈帧 本地方法栈 程序计数器 运行时数据区 JVM 运行时数据区主要分为5块 方法区 JDK1.8以后叫做元数据区(Metaspace) ...

随机推荐

  1. volatile的工作原理

    volatile的特性: volatile可见性:对一个volatile的读,总可以看到对这个变量最终的写: volatile原子性:volatile对单个读/写具有原子性(32位Long.Doubl ...

  2. MyBatis中#{}和${}的区别详解

    首先看一下下面两个sql语句的区别: <select id="selectByNameAndPassword" parameterType="java.util.M ...

  3. 上传文件的C#代码

    1 <%@ WebHandler Language="C#" Class="UpLoadFile" %> 2 3 using System; 4 u ...

  4. Java多线程(十三):线程池

    线程池类结构 1.Executor是顶级接口,有一个execute方法. 2.ExecutorService接口提供了管理线程的方法. 3.AbstractExecutorService管理普通线程, ...

  5. 利用HBuilder打包Vue开发的webapp为app

    众所周知,前端开发完成的webapp只能运行在浏览器上,对运行环境有一定的限制,也就是除了浏览器其他的环境下不支持:那么现在如果有个需求是这样的呢?需要一套代码三端运行呢?三端运行(黑人脸??)是的, ...

  6. helm生产环境离线安装

    1.安装helm 生产环境离线的情况下,使用tiller前端运行还是后端运行可以根据自己需求,如果希望tiller可以稳定的运行,可以尝试docker跑以下,作者没有尝试不知是否会成功.但是不通过he ...

  7. 华为Quidway S5700开启SNMP协议

    开启SNMP协议通过Console口登录主要用于交换机第一次上电或者本地配置.或者无法通过远程访问时,可通过Console口登录.如何才能通过Console口登录交换机呢?在配置通过Console口配 ...

  8. Winform中使用FastReport实现自定义PDF打印预览

    场景 Winform中使用FastReport实现简单的自定义PDF导出: https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/1009 ...

  9. ubuntu 12.04下访问windows共享文件夹

    ubuntu 12.04LTS已经不支持smbfs文件系统,所以不能用mount -smbfs 来映射windows共享文件夹. 常见有两种方法 1.terminal下 mount //192.168 ...

  10. 脱离脚手架来配置、学习 webpack4.x (二)基础搭建loader 配置 css、scss

    序 上一篇已经把基本架子搭起来了,现在来增加css.scss.自动生成html.css 提前等方面的打包.webpack 默认只能处理js模块,所以其他文件类型需要做下转换,而loader 恰恰是做这 ...