热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

jvm虚拟机_原理|一文入门JVM虚拟机

一文带你理解JVM1、jdk、jre、jvm的区别与联系jdk的全称是JavaDevelopmentkit(java开发工具包),我们可以把程序设计语言、java虚拟

一文带你理解JVM

1、jdk、jre、jvm的区别与联系

jdk的全称是Java Development kit(java开发工具包),我们可以把程序设计语言、java虚拟机、java类库这三部分统称为jdk,jdk是用于支持java程序开发的最小环境。Developer可以很容易的使用里面的方法以减少代码量,里面同时包含jre和一些开发的小工具(如编译工具javac),同时包含了jre。

jre的全称是Java Running Environment(java运行时环境 ),可以把java类库API中的javaSE的API子集和java虚拟机这两部分统称为JRE,JRE是支持java程序运行的标准环境。

jvm的全称java virtual machine(java 虚拟机),它只认识XXX.class文件,虚拟机可以识别这种文件的字节码指令并调用操作系统上的API,正是这个原因,java才可以跨平台使用。

ada4c0c19ad48ff8fcd3a1cb43b63758.png

2、代码是如何执行的

jvm是一个软件,它帮我们屏蔽了底层的操作系统、硬件、CPU指令层的细节

它的口号是Write Once,Run Everywhere.我们来看一下代码的执行流程。

4b5c452be2e02c5fdde5d92819d0d6b2.png

图中的Test.java文件是按照java语法规则编写的源文件,是一种高级语言,.java文件经javac编译后就生成字节码文件,字节码文件是用于给java虚拟机执行用的,该文件的格式规范受到java虚拟机的定义。而jvm的目的就是将字节码文件Test.class翻译为操作系统及硬件的指令,便于在不同的操作系统上执行。

NOTE:jvm虚拟机并不是仅仅只针对java语言,像一些其它编程语言如Groovy、Scala和Kotlin也可以在jvm虚拟机上运行上,这些语言仅仅需要实现一个编译器,通过该编译器把源代码文件编译成JVM能识别的字节码文件即可。5ded4000c82f863d727b30d8954b4621.png

3、JVM的内存结构

f8070de5d6ba5b1f7d5ee12b372d0b25.png

3.1类加载子系统

在java虚拟机中,负责查找并装载类的部分称为装载子系统,装载子系统用于定位和加载译码后的class文件。在加载阶段,虚拟机需要完成以下事情

  • 通过一个类的全限定名来获取定义此类的二进制字节流
  • 将这个字节流所代表的静态存储结构转化为元空间中运行时的数据结构
  • 在内存中生成一个代表这个类的java.lang.Class对象,作为方法这个类的各种数据访问入口

加载过程下图所示9068b45788935cd161df7ea067b59cc7.png

类的加载是通过查询路径的方式进行的,加载阶段既可以使用虚拟机里内置的引导类加载器来完成,也可以由用户自定义类加载器来完成。其加载顺序如下

1、Bootstrap ClassLoader:启动类加载器,加载存放在\lib目录,或者被Xbootclasspath选项指定的jar包,如rt.jar、tools.jar

2、Extension ClassLoader:扩展类加载器,加载\lib\ext*.jar或者-java.ext.dirs指定目录下的jar包

3、AppClassLoader:应用程序类加载器,加载Classpath或java.class.path所指定的目录下的类和jar包

4、Custom ClassLoader:通过java.lang.ClassLoader的子类自定义加载class

实际上,上面描述的仅仅是类加载过程中的加载过程,类加载的整个过程包括:加载、验证、准备、解析和初始化

字节码--->加载--->验证--->准备--->解析--->初始化,其中验证、准备和解析阶段可以统称为链接阶段。

下面我们讲解每个阶段的作用

  • 验证:验证是链接阶段的第一步,这一阶段的目的是确保Class文件的字节流包含的信息符合《java虚拟机规范》的全部约束要求,确保这些信息被当做代码运行后不会危害虚拟机自身的安全
  • 准备:正式为类中定义的变量(静态变量)分配内存并设置类变量初始值阶段。
  • 解析:java虚拟机将常量池(元数据区的一部分)内的符号引用替换为直接引用过程
  • 初始化:类的初始化是类加载过程的最后一步,它的作用是真正开始执行类中编写的java程序代码

类加载会将类的信息加入到元数据空间。

如果一个类型从被加载到虚拟机内存开始,到出卸载为止,它的整个生命周期将在类加载的基础上增加使用和卸载阶段e36eb8cdd05a76ae42a4ddf45b1f1394.png

3.2jvm内存部分(运行时数据区)

jvm在运行时会把它所管理的内存划分为若干不同的数据区域,宏观上可以划分为两部分109584b2738d48ba4863c628c795406e.png

1、线程私有数据区(3个部分)

  • 程序计数器

①程序计数器是一块内存较小的空间,它可以看做是当前线程执行的字节码的行号指示器

②它是程序控制流的指示器,分支、循环、跳转、异常处理、多线程恢复等基础功能都需要依赖这个计数器来完成

③线程私有,各条线程之间计数器互不影响,独立存储,

④随着线程的结束而结束,不需要垃圾回收

⑤不会出现OutOfMemoryError

  • 虚拟机栈

与程序计数器一样,java虚拟机栈也是线程私有的,不会被GC回收,它的生命周期与线程相同,java虚拟机栈描述的是java方法执行线程的内存模型:每个方法被执行的时候(一个方法对应着一个栈帧),java虚拟机栈都会同步创建一个栈帧用于存储局部变量表、操作数栈、动态链接、方法出口等信息。当栈的深度大于虚拟机所允许的深度,将抛出StackOverflowError异常,如果java虚拟机的容量可以动态扩展,当栈扩展时无法申请到足够的内存时将会抛出OutOfMemoryError异常。栈里面运行方法,存放方法的局部变量名,变量名所指向的值(常量值、对象值等)都存放在堆上。每一个方法被调用直至执行完毕的过程,就对应着一个栈帧在虚拟机栈从入栈到出栈的过程。ec45637793577a96237fd34806fd07d5.png

①局部变量表:是一组变量的存储空间,用于存放方法的参数和方法内部定义的局部变量,局部变量分为两种,分别是基本数据类型和引用类型(引用类型指向堆中对象的地址),常量值指向元空间。

②操作栈:操作栈也被称为操作数栈,它是一个后入先出的栈。当一个方法刚开始执行的时候,这个方法的操作数栈是空的,在方法的执行过程中,会有各种字节码指令往操作数栈中写入和提取内容,也就是出栈和入栈操作,一个完整的方法执行期间往往包含多个这样入栈和出栈的过程。操作数栈中元素的数据类型必须与字节码指令的序列严格匹配。

③动态链接:每个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用,持有这个引用是为了支持方法调用过程中的动态链接。一个方法要调用其它方法,需要将这些方法的符号引用转化为其内存地址的直接引用,而符号引用存在于方法区中的运行时常量池,所有需要在运行时动态的将这些符号引用转化为直接引用。

④返回地址:方法不管是正常执行结束还是异常退出,需要返回方法被调用的位置。

  • 本地方法栈

本地方法栈与虚拟机栈所发挥的作用是非常相似的,其区别在于虚拟机栈为虚拟机执行java方法(也就是字节码)服务,而本地方法栈则是为虚拟机使用到的本地(Native)方法服务。与虚拟机栈一样,本地方法栈也会在栈深度溢出或者栈扩展失败时分别抛出StackOverflowError和OutOfMemoryError。线程私有,不会被GC回收。

有的虚拟机直接将虚拟机栈和本地方法栈合二为一,不在单独考虑。

总结:线程私有的三个部分都是随着线程执行结束而结束(JVM就销毁了虚拟机栈里面的栈帧)。

2、线程公有数据区(2个部分)

  • 元空间

在jdk1.8之前,元空间所在的区域被称为方法区,方法区在jdk1.7时合并到了堆。

jdk1.8时,方法区所在的区域被称为元空间,但是1.8仍然保留着方法区的概念,只不过实现方式不同,元空间与堆不相连,但与堆共享物理内存,逻辑上可以认为在堆中。

元空间的特点

①线程共享

②存储类信息、常量、运行时常量池、静态变量、即时编译器编译后的代码等数据

③在jdk1.7之前,在HotSpot虚拟机上将方法区成为永久代,在jdk1.8时,完全放弃了永久代,改用了元空间

④因为效率问题,无垃圾回收

⑤空间不够时,OutOfMemoryError

⑥设置元空间的大小--XX:Metaspace=10M-XX:MetaspaceSize=10M

运行时常量池的特点

运行时常量池是元空间的一部分,Class文件除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池表,用于存放编译期生成的各种字面量的符号引用,这部分内容将在类加载后存放到元空间的运行时常量池中。

总结

jdk1.6及之前:有永久代,常量池在方法区

jdk1.7:从某个版本开始去除永久代,常量池1.7放入堆中

jdk1.8及之后:无永久代,常量池1.8在元空间。

java堆(heap)是虚拟机所管理的内存中最大的一块,java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建,几乎所有的对象实例和数组都在堆上分配。

java堆是垃圾收集器管理的内存区域,从回收的角度看,由于现代垃圾收集器大部分都是基于分代收集理论设计的,所以java堆可以分为新生代和老年代,新生代又可以分为Eden空间、From Survivor空间和To Survivor空间,无论怎样划分,都是为了更好的进行垃圾回收。

java堆可以被实现成固定大小的,也可以是扩展的(通过-Xmx和-Xms设定)。如果在java堆中没有内存完成实例分配,并且堆无法在扩展时,java虚拟机将会抛出OutOfMemory异常。c1c04ca3f001c465c6e156eaaad3af04.png

在堆中加载实例对象的顺序67e51b3bdb63ecdaef5f37f527799b53.png

当老年代空间满时,将会抛出OutOfMemory异常。

java堆溢出

不断创建对象又不释放,当对象到达一定数量时,无堆空间时将会产生堆溢出

内存泄漏:GC Roots到对象之间有可达路径却无法回收(存在对象引用,却没有释放)

内存溢出:内存溢出是指应用系统中存在无法回收的内存或使用内存过多,最终使得程序运行要用到的内存大于能提供的最大内存。在Java虚拟机中,GC Roots到对象之间无可达路径,可以被收集,但对象还存活着,此时可以根据物理机内存适当的调大虚拟机参数-Xms、-Xmx,分析代码是否对象生命周期过长。对象是否持有状态时间过长。d5a986a993dad68e74528f367a962cac.png

参考文献

[1]周志明.深入理解java虚拟机

[2]https://www.bilibili.com/video/BV13J411n72m?from=search&seid=7875422419866373500

6512aac24e9bdf2ab02be9f057e4238c.gif

●大数据计算生态之数据计算(二)

●大数据计算生态之数据计算(一)

●大数据计算生态之数据存储

●Spark训练营(一)-- 开发环境搭建及wordCount实战

●Spark训练营(二)-- SparkStreaming流计算组件wordCount实战

●Spark训练营(三)-- GraphX图计算组件最短路算法实战

●一文纵览大数据计算生态

●原创|带你厘清分布式、数据库的那些一致性

●深入浅出大数据组件之Kafka消息队列

●一个故事让你理解什么是区块链

●实时数据流计算引擎Flink和Spark剖析

●自定义Hadoop的输入格式

713b9394cc840d33dad6e926cba0ddd1.png

文章都看完了9379af366e712a92f71e3588c42cf667.gif不点个1a97af00f276ef4156f0baf52132443e.png 吗

欢迎    点赞、在看、分享  三连哦~~




推荐阅读
  • Java在运行已编译完成的类时,是通过java虚拟机来装载和执行的,java虚拟机通过操作系统命令JAVA_HOMEbinjava–option来启 ... [详细]
  • 原文地址:https:www.cnblogs.combaoyipSpringBoot_YML.html1.在springboot中,有两种配置文件,一种 ... [详细]
  • baresip android编译、运行教程1语音通话
    本文介绍了如何在安卓平台上编译和运行baresip android,包括下载相关的sdk和ndk,修改ndk路径和输出目录,以及创建一个c++的安卓工程并将目录考到cpp下。详细步骤可参考给出的链接和文档。 ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • Android源码深入理解JNI技术的概述和应用
    本文介绍了Android源码中的JNI技术,包括概述和应用。JNI是Java Native Interface的缩写,是一种技术,可以实现Java程序调用Native语言写的函数,以及Native程序调用Java层的函数。在Android平台上,JNI充当了连接Java世界和Native世界的桥梁。本文通过分析Android源码中的相关文件和位置,深入探讨了JNI技术在Android开发中的重要性和应用场景。 ... [详细]
  • 本文介绍了在sqoop1.4.*版本中,如何实现自定义分隔符的方法及步骤。通过修改sqoop生成的java文件,并重新编译,可以满足实际开发中对分隔符的需求。具体步骤包括修改java文件中的一行代码,重新编译所需的hadoop包等。详细步骤和编译方法在本文中都有详细说明。 ... [详细]
  • 本文概述了JNI的原理以及常用方法。JNI提供了一种Java字节码调用C/C++的解决方案,但引用类型不能直接在Native层使用,需要进行类型转化。多维数组(包括二维数组)都是引用类型,需要使用jobjectArray类型来存取其值。此外,由于Java支持函数重载,根据函数名无法找到对应的JNI函数,因此介绍了JNI函数签名信息的解决方案。 ... [详细]
  • 如何搭建Java开发环境并开发WinCE项目
    本文介绍了如何搭建Java开发环境并开发WinCE项目,包括搭建开发环境的步骤和获取SDK的几种方式。同时还解答了一些关于WinCE开发的常见问题。通过阅读本文,您将了解如何使用Java进行嵌入式开发,并能够顺利开发WinCE应用程序。 ... [详细]
  • 纠正网上的错误:自定义一个类叫java.lang.System/String的方法
    本文纠正了网上关于自定义一个类叫java.lang.System/String的错误答案,并详细解释了为什么这种方法是错误的。作者指出,虽然双亲委托机制确实可以阻止自定义的System类被加载,但通过自定义一个特殊的类加载器,可以绕过双亲委托机制,达到自定义System类的目的。作者呼吁读者对网上的内容持怀疑态度,并带着问题来阅读文章。 ... [详细]
  • 如何用JNI技术调用Java接口以及提高Java性能的详解
    本文介绍了如何使用JNI技术调用Java接口,并详细解析了如何通过JNI技术提高Java的性能。同时还讨论了JNI调用Java的private方法、Java开发中使用JNI技术的情况以及使用Java的JNI技术调用C++时的运行效率问题。文章还介绍了JNIEnv类型的使用方法,包括创建Java对象、调用Java对象的方法、获取Java对象的属性等操作。 ... [详细]
  • 从零基础到精通的前台学习路线
    随着互联网的发展,前台开发工程师成为市场上非常抢手的人才。本文介绍了从零基础到精通前台开发的学习路线,包括学习HTML、CSS、JavaScript等基础知识和常用工具的使用。通过循序渐进的学习,可以掌握前台开发的基本技能,并有能力找到一份月薪8000以上的工作。 ... [详细]
  • Centos7搭建ELK(Elasticsearch、Logstash、Kibana)教程及注意事项
    本文介绍了在Centos7上搭建ELK(Elasticsearch、Logstash、Kibana)的详细步骤,包括下载安装包、安装Elasticsearch、创建用户、修改配置文件等。同时提供了使用华为镜像站下载安装包的方法,并强调了保证版本一致的重要性。 ... [详细]
  • Spring框架《一》简介
    Spring框架《一》1.Spring概述1.1简介1.2Spring模板二、IOC容器和Bean1.IOC和DI简介2.三种通过类型获取bean3.给bean的属性赋值3.1依赖 ... [详细]
  • OpenMap教程4 – 图层概述
    本文介绍了OpenMap教程4中关于地图图层的内容,包括将ShapeLayer添加到MapBean中的方法,OpenMap支持的图层类型以及使用BufferedLayer创建图像的MapBean。此外,还介绍了Layer背景标志的作用和OMGraphicHandlerLayer的基础层类。 ... [详细]
  • Jboss的EJB部署描述符standardjaws.xml配置步骤详解
    本文详细介绍了Jboss的EJB部署描述符standardjaws.xml的配置步骤,包括映射CMP实体EJB、数据源连接池的获取以及数据库配置等内容。 ... [详细]
author-avatar
Z张海男_851
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有