热门标签 | 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 吗

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




推荐阅读
  • 秒建一个后台管理系统?用这5个开源免费的Java项目就够了
    秒建一个后台管理系统?用这5个开源免费的Java项目就够了 ... [详细]
  • 解决Bootstrap DataTable Ajax请求重复问题
    在最近的一个项目中,我们使用了JQuery DataTable进行数据展示,虽然使用起来非常方便,但在测试过程中发现了一个问题:当查询条件改变时,有时查询结果的数据不正确。通过FireBug调试发现,点击搜索按钮时,会发送两次Ajax请求,一次是原条件的请求,一次是新条件的请求。 ... [详细]
  • 浏览器作为我们日常不可或缺的软件工具,其背后的运作机制却鲜为人知。本文将深入探讨浏览器内核及其版本的演变历程,帮助读者更好地理解这一关键技术组件,揭示其内部运作的奥秘。 ... [详细]
  • 本文详细解析了Java类加载系统的父子委托机制。在Java程序中,.java源代码文件编译后会生成对应的.class字节码文件,这些字节码文件需要通过类加载器(ClassLoader)进行加载。ClassLoader采用双亲委派模型,确保类的加载过程既高效又安全,避免了类的重复加载和潜在的安全风险。该机制在Java虚拟机中扮演着至关重要的角色,确保了类加载的一致性和可靠性。 ... [详细]
  • Presto:高效即席查询引擎的深度解析与应用
    本文深入解析了Presto这一高效的即席查询引擎,详细探讨了其架构设计及其优缺点。Presto通过内存到内存的数据处理方式,显著提升了查询性能,相比传统的MapReduce查询,不仅减少了数据传输的延迟,还提高了查询的准确性和效率。然而,Presto在大规模数据处理和容错机制方面仍存在一定的局限性。本文还介绍了Presto在实际应用中的多种场景,展示了其在大数据分析领域的强大潜力。 ... [详细]
  • 如何利用Java 5 Executor框架高效构建和管理线程池
    Java 5 引入了 Executor 框架,为开发人员提供了一种高效管理和构建线程池的方法。该框架通过将任务提交与任务执行分离,简化了多线程编程的复杂性。利用 Executor 框架,开发人员可以更灵活地控制线程的创建、分配和管理,从而提高服务器端应用的性能和响应能力。此外,该框架还提供了多种线程池实现,如固定线程池、缓存线程池和单线程池,以适应不同的应用场景和需求。 ... [详细]
  • Spring – Bean Life Cycle
    Spring – Bean Life Cycle ... [详细]
  • 本文介绍了在 Java 编程中遇到的一个常见错误:对象无法转换为 long 类型,并提供了详细的解决方案。 ... [详细]
  • Python 数据可视化实战指南
    本文详细介绍如何使用 Python 进行数据可视化,涵盖从环境搭建到具体实例的全过程。 ... [详细]
  • oracle c3p0 dword 60,web_day10 dbcp c3p0 dbutils
    createdatabasemydbcharactersetutf8;alertdatabasemydbcharactersetutf8;1.自定义连接池为了不去经常创建连接和释放 ... [详细]
  • Java高并发与多线程(二):线程的实现方式详解
    本文将深入探讨Java中线程的三种主要实现方式,包括继承Thread类、实现Runnable接口和实现Callable接口,并分析它们之间的异同及其应用场景。 ... [详细]
  • Amoeba 通过优化 MySQL 的读写分离功能显著提升了数据库性能。作为一款基于 MySQL 协议的代理工具,Amoeba 能够高效地处理应用程序的请求,并根据预设的规则将 SQL 请求智能地分配到不同的数据库实例,从而实现负载均衡和高可用性。该方案不仅提高了系统的并发处理能力,还有效减少了主数据库的负担,确保了数据的一致性和可靠性。 ... [详细]
  • C++ 开发实战:实用技巧与经验分享
    C++ 开发实战:实用技巧与经验分享 ... [详细]
  • 在探讨Hibernate框架的高级特性时,缓存机制和懒加载策略是提升数据操作效率的关键要素。缓存策略能够显著减少数据库访问次数,从而提高应用性能,特别是在处理频繁访问的数据时。Hibernate提供了多层次的缓存支持,包括一级缓存和二级缓存,以满足不同场景下的需求。懒加载策略则通过按需加载关联对象,进一步优化了资源利用和响应时间。本文将深入分析这些机制的实现原理及其最佳实践。 ... [详细]
  • 如何精通编程语言:全面指南与实用技巧
    如何精通编程语言:全面指南与实用技巧 ... [详细]
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社区 版权所有