JVM之JVM的体系结构

摘自:https://www.cnblogs.com/jalja365/p/12184872.html

JVM之JVM的体系结构

一、JDK的组成

JDK:JDK是Java开发工具包,是Sun Microsystems针对Java开发员的产品。JDK中包含JRE(在JDK的安装目录下有一个名为jre的目录,里面有两个文件夹bin和lib,在这里可以认为bin里的就是jvm,lib中则是jvm工作所需要的类库,而jvm和 lib和起来就称为jre)和一堆Java工具(javac/java/jdb等)和Java基础的类库(即Java API 包括rt.jar)。

Java Runtime Environment(JRE):是运行基于Java语言编写的程序所不可缺少的运行环境。也是通过它,Java的开发者才得以将自己开发的程序发布到用户手中,让用户使用。JRE中包含了Java virtual machine(JVM),runtime class libraries和Java application launcher,这些是运行Java程序的必要组件。

JVM(java virtual machine):就是我们常说的java虚拟机,它是整个java实现跨平台的最核心的部分,所有的java程序会首先被编译为.class的类文件,这种类文件可以在虚拟机上执行。

二、JVM的位置

JVM就是运行在操作系统之上的一个软件

三、JVM体系结构

JVM的组成:

  • 类加载子系统 Class loader
  • 运行时数据区 JVM 内存模型
  • 执行引擎

四、类加载子系统

 ======================类加载器=======================

 类加载器(ClassLoader):负责加载class文件(classs文件在文件开头有特定的文件标识),将class文件字节码内容加载到内存中,并将这些内容转换成方法区中的运行时数据结构;ClassLoader只负责加载class文件的加载,至于它是否可以运行,则由Execution Engine决定。

1、BootStrapLoader(引导类加载器):类加载器也是java类,他们也需要类加载器加载进入内存,显然必须要有第一个不是java类的类加载器,来完成这个工作,这个正是BootStrap。负责加载存放在D:\Program Files (x86)\Java\jdk1.7.0_79\jre\lib下,或被-Xbootclasspath参数指定的路径中的,并且能被虚拟机识别的类库(如rt.jar,所有的java.*开头的类均被Bootstrap ClassLoader加载);启动类加载器是无法被Java程序直接引用的;rt.jar 里面的类的加载器都是BootStrapLoader。

 2、Extension ClassLoader(扩展类加载器):该加载器由sun.misc.Launcher$ExtClassLoader实现,它负责加载D:\Program Files (x86)\Java\jdk1.7.0_79\jre\lib\ext目录中,或者由java.ext.dirs系统变量指定的路径中的所有类库(如javax.*开头的类),开发者可以直接使用扩展类加载器。ext 目录下所有的类的加载器都是Extension ClassLoader

 3、Application ClassLoader(应用程序类加载器):该类加载器由sun.misc.Launcher$AppClassLoader来实现,它负责加载用户类路径(ClassPath)所指定的类,开发者可以直接使用该类加载器,如果应用程序中没有自定义过自己的类加载器,一般情况下这个就是程序中默认的类加载器。

====================JVM类加载机制==============

全盘负责:当前线程的类加载器负责加载某个Class时,该Class所依赖的和引用的其他Class也将由该类加载器负责载入,除非显示使用CLassLoader.loadClass()指定类加载器来载入

父类委托:先让父类加载器试图加载该类,只有在父类加载器无法加载该类时才尝试从自己的类路径中加载该类。所以我们在开发中尽量不要使用与JDK相同的类(例如自定义一个java.lang.System类),因为父类加载器中已经有一份java.lang.System类了,它会直接将该类给程序使用,而你自定义的类压根就不会被加载。

双亲委派模型:

  双亲委派模型的工作流程是:如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把请求委托给父加载器去完成,依次向上,因此,所有的类加载请求最终都应该被传递到顶层的启动类加载器中,
只有当父加载器在它的搜索范围中没有找到所需的类时,即无法完成该加载,子加载器才会尝试自己去加载该类。
双亲委派机制:

  • 1、当AppClassLoader加载一个class时,它首先不会自己去尝试加载这个类,而是把类加载请求委派给父类加载器ExtClassLoader去完成。
  • 2、当ExtClassLoader加载一个class时,它首先也不会自己去尝试加载这个类,而是把类加载请求委派给BootStrap ClassLoader去完成。
  • 3、如果BootStrap ClassLoader加载失败(例如在$JAVA_HOME/jre/lib里未查找到该class),会使用ExtClassLoader来尝试加载;
  • 4、若ExtClassLoader也加载失败,则会使用AppClassLoader来加载,如果AppClassLoader也加载失败,则会报出异常ClassNotFoundException。

双亲委派模型意义:

  •   -系统类防止内存中出现多份同样的字节码
  •   -保证Java程序安全稳定运行

 ==================类的加载过程======================

类的加载过程:JVM将javac编译好的class字节码文件加载到内存中,并对该数据进行验证、解析和初始化、形成JVM可以直接使用的JAVA类,最终回收(卸载)的过程。

字节码(.class)文件来源:

  • – 从本地系统中直接加载
  • – 通过网络下载.class文件
  • – 从zip,jar等归档文件中加载.class文件
  • – 从专有数据库中提取.class文件
  • – 将Java源文件动态编译为.class文件

1、加载:加载阶段其实就是JVM通过一个类的全限定名来获取其定义的二进制字节流,并将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构且在Java堆中生成一个代表这个类的java.lang.Class对象,作为对方法区中这些数据的访问入口。在该阶段我们开发人员可以干预,例如:我们可以指定类加载器来加载该字节数组或者自定义类加载器来加载。

2、链接:将java类的二进制代码合并到JVM的运行状态中的过程

  • a、验证:验证是为了确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。
  • b、准备:该阶段是在方法区中为类变量(static变量)分配内存并设置类变量初始值。例如:public static int flag=1;该阶段初始化值为0。
  • c、解析:虚拟机将常量池中的符号引用替换为直接引用的过程。(直接引用就是直接指向目标的指针、相对偏移量或一个间接定位到目标的句柄)

3、初始化:初始化为类的静态变量赋予正确的初始值,JVM负责对类进行初始化,主要对类变量进行初始化。

  • 初始化阶段就是执行类构造器<clinit>()的过程,类构造器<clinit>()是由编译器自动收集类中的所有类变量的赋值动作和静态语句块中的语句合并产生的。
  • 当初始化一个类的时候,如果发现其父类还没有进行过初始化,则需要先初始化其父类。
  • 虚拟机会保证一个类的<clinit>()方法在多线程环境中被正确加锁和同步。
  • 当访问一个java 类的静态域时,只有正真申明这个域的类才会被初始化。

4、使用:程序使用JVM加载的类

5、卸载 

  • 执行了System.exit()方法
  • JVM垃圾回收机制触发回收
  • 程序正常执行结束
  • 程序在执行过程中遇到了异常或错误而异常终止
  • 由于操作系统出现错误而导致Java虚拟机进程终止

五、运行时数据区

1、方法区(Method Area):方法区是各个线程共享的内存区域;方法区用于存储已被虚拟机加载的类的模板信息、常量、静态变量等;虽然Java虚拟机规范把方法区描述为堆的一部分,但是他还有个别名叫做Non-heap(非堆),目的应该是与Java堆区分开来;根据Java虚拟机规范的规定,当方法区无法满足内存分配需求时,将抛出OutOfMemoryError 异常;相对而言,垃圾收集在这个区域是比较少出现的,但并非数据进入了方法区就如永久代的名字一样永久存在了。这区域的内存回收目标重要是针对常量池的回收和类型的卸载。
方法区只是一个规范:

  • 在HotSpot虚拟机上开发、部署程序我们把方法区称为“永久代”(Permanent Generation);
  • 他虚拟机(如 BEA JRockit、IBM J9 等)来说是不存在永久代的概念的。
  • HotSpot虚拟机在JKD.8中已经没有方法区的概念了,他使用元空间代替该区域

2、PC寄存器(程序计数器):每个线程都有一个程序计数器,是线程私有的;就是一个指针,指向方法区中的方法字节码(用来存储指向下一条指令的地址,既将要执行的指令代码),由执行引擎读取下一条指令,是一个非常小的内存空间,几乎可以忽略不记;它是当前线程所执行的字节码的行号指示器,字节码解释器通过改变这个计数器的值来选取下一条需要执行的字节码指令。如果执行的是一个Native方法,那这个计数器是空的;用以完成分支、循环、跳转、异常处理、线程恢复等基础功能。不会发生内存溢出OOM错误
本地方法栈(Native Stack):与虚拟机栈基本类似,区别在于虚拟机栈为虚拟机执行的java方法服务,而本地方法栈则是为Native方法服务。(栈的空间大小远远小于堆)

3、虚拟机栈(Vm Stack)
  栈也叫栈内存,主管 Java 程序的运行,是在线程创建时创建,它的生命期是跟随线程的生命期,线程结束栈内存也就仔放,对于栈来说不存在垃圾回收问题,只要线程结束该栈就释放,生命周期和线程一致,是线程私有的。8种基木类型的变量+对象的引用变量+实例方法都是在函数的栈内存中分配。

栈的运行原理:栈中的数据都是以栈帧(Stack Frame)的格式存在,栈帧是一个内存区块,是一个数据集,是一个有关方法( Method )和运行期数据的数据集,当一个方法A被调用时就产生了一个栈帧 Fl ,并被压入到栈中, A方法又调用了B方法,于是产生栈帧 F2 也被压入栈,B方法又调用了C方法,于是产生栈帧 F3 也被压入栈,执行完毕后,先弹出 F3 栈帧,再弹出 F2 栈帧,再弹出 Fl 栈帧 以此类推, 遵循“先进后出” / “后进先出”原则。每个方法执行的同时都会创建一个栈帧,用于存储局部变量表、操作数、动态链接、方法出口等信息,每一个方法从调用直至执行完毕的过,就对应着一个栈帧在虚拟机中入栈到出栈的过程。栈的大小和具体JVM的实现有关,通常在 256K~1024K 之间, 1M 左右。

JVM栈的特点:

  • 局部变量表所需的内存空间在编译期间完成内存分配。当进入一个方法时,这个方法需要在帧中分配多大的内存空间是完全确定的,在方法运行期间不会改变局部变量表的大小。
  • 在Java虚拟机规范中,对这个区域规定了两种异常状态:如果线程请求的栈的深度大于虚拟机允许的深度,将抛出StackOverFlowError异常(栈溢出);如果虚拟机栈可以动态扩展(现在大部分Java虚拟机都可以动态扩展,只不过Java虚拟机规范中也允许固定长度的java虚拟机栈),如果扩展时无法申请到足够的内存空间,就会抛出OutOfMemoryError异常(没有足够的内存)。

4、本地方法栈(Native Method Stacks):与虚拟机栈所发挥的作用是非常相似的,他们之间的区别不过是虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,而本地方法栈则为虚拟机使用到的本地Native方法服务‘;在虚拟机规范中对本地方法栈中的使用方法、语言、数据结构并没有强制规定,因此具体的虚拟机可以自由实现它。甚至有的虚拟机(例如Sun HotSpot虚拟机)直接就把本地方法栈和虚拟机栈合二为一。本地方法栈也会抛出StackOverFlowError和OutOfmMemoryError异常。

5、Java堆(Java Heap):是Java虚拟机管理内存中的最大一块;Java堆是所有线程共享的一块内存管理区域。此内存区域唯一目的就是存放对象的实例,几乎所有对象实例都在堆中分配内存。这一点在Java虚拟机规范中的描述是:所有对象实例以及数组都要在堆上分配,但是随着JIT编译器的发展与逃逸技术逐渐成熟,栈上分配、标量替换优化技术将会导致一些微妙的变化发生,所有的对象都分配在堆上也不是变的那么“绝对”了。详解请学习我的:JVM之堆的体系结构



原文地址:https://www.cnblogs.com/xichji/p/12208594.html

时间: 2024-08-01 06:15:36

JVM之JVM的体系结构的相关文章

JVM解毒——JVM与Java体系结构

你是否也遇到过这些问题? 运行线上系统突然卡死,系统无法访问,甚至直接OOM 想解决线上JVM GC问题,但却无从下手 新项目上线,对各种JVM参数设置一脸懵逼,直接默认,然后就JJ了 每次面试都要重新背一遍JVM的一些原理概念性东西 这段广告语写的好,趁着在家办公学习下JVM,先列出整体知识点 点赞+收藏 就学会系列,文章收录在 GitHub JavaEgg ,N线互联网开发必备技能兵器谱 Java开发都知道JVM是Java虚拟机,上学时还用过的VM也叫虚拟机,先比较一波 虚拟机与Java虚拟

深入理解JVM之JVM内存区域与内存分配

深入理解JVM之JVM内存区域与内存分配 在学习jvm的内存分配的时候,看到的这篇博客,该博客对jvm的内存分配总结的很好,同时也利用jvm的内存模型解释了java程序中有关参数传递的问题. 博客出处: http://www.cnblogs.com/hellocsl/p/3969768.html?utm_source=tuicool&utm_medium=referral 看了此博客后,发现应该去深入学习下jvm的内存模型,就是去认真学习下<深入理解Java虚拟机>,其内容可能会<

Jvm(32),理解升级----(挺不错的)图解深入理解JVM之JVM内存区域与内存分配

解释了java中对象的在内存中的模型,学习了对象的内存模型后,对理解多态.参数传递等的理解都有帮助. 前言:这是一篇关于JVM内存区域的文章,由网上一些有关这方面的文章和<深入理解 Java虚拟机>整理而来,所以会有些类同的地方,也不能保证我自己写的比其他网上的和书本上的要好,也不可能会这样.写博客的目的是为了个人对这方面自己理解的分享与个人的积累,所以有写错的地方多多指教. 看到深入两字,相信很多的JAVA初学者都会直接忽略这样的文章,其实关于JVM内存区域的知识对于初学者来说其实是很重要的

Java内存管理-初始JVM和JVM启动流程(二)

勿在流沙住高台,出来混迟早要还的. 做一个积极的人 编码.改bug.提升自己 我有一个乐园,面向编程,春暖花开! 上一篇分享了什么是程序,以及Java程序运行的三个阶段.也顺便提到了Java中比较重要的一个东西就是JVM(JAVA 虚拟机),那么今天在先了解一下JVM和JVM的启动流程. 知识地图: 1.什么是虚拟机 虚拟机(Virtual Machine)指通过软件模拟的具有完整硬件系统功能的.运行在一个完全隔离环境中的完整计算机系统.[百度百科] 虚拟机通过仿真引擎(称为管理程序)处理虚拟硬

【JVM】JVM系列之Class文件(三)

有大牛带学java,那就上路吧! 上手推荐学此篇,啃下来.记录备复习. 一.前言 随着我们学习的不断深入,我相信读者对class文件很感兴趣,class文件是用户编写程序与虚拟机之前的桥梁,程序通过编译形成class文件,class文件之后会载入虚拟机,被虚拟机执行,下面我么来一起揭开class文件的神秘面纱. 二.什么是class文件 class文件是二进制文件,通常是以.class文件结尾的文件,它是以8位字节为基础单位的二进制流,各个数据项紧密排列在class文件中,数据项的基本类型为u1

【JVM】JVM系列之JVM体系(一)

一.前言 为什么要学习了解Java虚拟机 1.我们需要更加清楚的了解Java底层是如何运作的,有利于我们更深刻的学习好Java. 2.对我们调试错误提供很宝贵的经验. 3.这是合格的Java程序必须要了解的内容. 基于此,笔者打算出一个Java虚拟机的系列,加深自己对知识点的理解,同时也方便各位有需要的园友. 二.Java虚拟机的定义 Java虚拟机(Java Virtual Machine),简称JVM.当我们说起Java虚拟机时,可能指的是如下三种不同的东西: 1.抽象规范. 2.一个具体的

JVM入门——JVM内存结构

一.java代码编译执行过程 1.源码编译:通过Java源码编译器将Java代码编译成JVM字节码(.class文件) 2.类加载:通过ClassLoader及其子类来完成JVM的类加载 3.类执行:字节码被装入内存,进入JVM虚拟机,被解释器解释执行 注:Java平台由Java虚拟机和Java应用程序接口搭建,Java语言则是进入这个平台的通道,   用Java语言编写并编译的程序可以运行在这个平台上 二.JVM简介 1.java程序经过一次编译之后,将java代码编译为字节码也就是class

【JVM】JVM系列之垃圾回收(二)

一.为什么需要垃圾回收 如果不进行垃圾回收,内存迟早都会被消耗空,因为我们在不断的分配内存空间而不进行回收.除非内存无限大,我们可以任性的分配而不回收,但是事实并非如此.所以,垃圾回收是必须的. 二.哪些内存需要进行垃圾回收 对于虚拟机中线程私有的区域,如程序计数器.虚拟机栈.本地方法栈都不需要进行垃圾回收,因为它们是自动进行的,随着线程的消亡而消亡,不需要我们去回收,比如栈的栈帧结构,当进入一个方法时,就会产生一个栈帧,栈帧大小也可以借助类信息确定,然后栈帧入栈,执行方法体,退出方法时,栈帧出

深入理解JVM:JVM执行时数据区域分类

JVM在运行java程序的过程中会把他所管理的内存划分为若干个不同的数据区域. 这些区域都有各自的用途和创建.销毁时间.有些区域随着虚拟机的启动而存在.有些区域则依赖用户线程的启动和结束而建立和销毁.依据<Java虚拟机规范1.7>规定,Java虚拟机所管理的内存分为下面几个区域: 程序计数器.Java虚拟机栈.本地方法栈.Java堆.方法区.运行时常量池.直接内存 程序计数器 是一块较小的内存空间.他能够看作是当前线程所运行的字节码的行号指示器. 在虚拟机的概念模型里,字节码解释器工作时就是