如果说java是跨平台的语言,那jvm就是跨语言的平台。只要是将该语言的文件遵循jvm的规范编译成jvm可以识别的字节码文件,就可以在jvm上运行。jvm的特点:一次编译,到处运行;自动内存管理;自动垃圾回收功能。

前端编译器(javac或者Eclipse JDT中的增量式编译器)把Java代码编译成字节码,字节码是可以发送给任何平台并且能在那个平台上运行的独立于平台的代码。
即时编译器(JIT compiler,just-in-timecompiler)是一个把Java的字节码(包括需要被解释的指令的程序)转换成可以直接发送给处理器(processor)的指令的程序

Java虚拟机是一台抽象的计算机,其规范定义了每个Java虚拟机都必须实现的特性,但是为每个特定实现都留下了很多选择。举个例子,虽然每个Java虚拟机都必须能够执行Java字节码,但是用何种技术来执行是可以选择的。而且,它的规范也很灵活,它允许虚拟机用纯粹软件方式来实现,也可以很大部分由硬件实现。Java规范本质上的灵活性保证了它能在很广泛的计算机和设备上得到实现

主流JVM-HotSpot的整体架构图


由上图可以看出,jvm最终的两个组件,类加载器(ClassLoader)和执行引擎(Exection Engine)。类加载子系统(ClassLoader)——负责从文件系统或者网络中加载Class字节码文件,并将加载的类信息(DNA元数据模版,jvm会根据这个模版实例化出n个一模一样的实例)存放于“方法区”。ClassLoader只负责文件的加载,而文件是否可以运行,则由执行引擎决定。

  • 类加载器(ClassLoader)
    类装载器的体系结构是Java虚拟机在安全性和网络移动性上发挥重要作用的一个方面。实际上在Java虚拟机中.存在着多个类加载器。因而结构图中的类装载器方块实际上表示的是一个可能包含多个类装载器的子系统。Java虚拟机拥有灵活的类装载器体系结构,从而使Java应用程序得以用自定义的方式来实现类的装载。
    一个Java应用程序可以使用两种类装载器:“启动”( boostrap 〉类装载器和用户定义的类装载器。启动类装载器〔这是系统中惟一的〉是Java虚拟机实现的一部分。例如,如果Java虚拟机在已有操作系统上实现为C程序、那么启动类装载器就会是此C程序的一个部分。启动类装载器通常使用某种默认方式从本地磁盘巾装载类.包括Java API的类(启动类装载器也被称为原始类装载器、系统类装载器或者默认类装载器。在l.2版本中,“系统类装载器”这个名称被赋予新的含义,此含义将在第3章讨论)。
    Java应用程序能够在运行时安装用户定义的类装载器,这种类装载器能够使用自定义的方式来装载类,例如,从网络下载class文件。尽管启动类装载器是虚拟机实现的本质部分.而用户定义的类装载器不是;但用户定义的类装载器能够用Java编写,能够被编译为class文件,能够被虚拟机装载,还能够像其他对象一样实例化。它们实际上只是运行中的Java应用程序可执行代码的一部分。
    由于有用户定义类装载器,所以不必在编译的时候就知道运行中的Java应用程序中最终会加人的所有的类用户定义的类装载器使得在运行时扩展Java应用程序成为可能。当它运行时,应用程序能够决定它需要哪些额外的类,能够决定是使用一个或是更多的用户定义的类装载器来装载。由于类装载器是使用Java编写的,所以能用任何在Java代码中可以表述的风格来进行类的装载。这些类可以通过网络下载,可以从某些数据库中获取,甚至可以动态生成。
    每一个类被装载的时候,Java虚拟机都监视这个类,看它到底是被启动类装载器还是被用户定义类装载器装载。当被装载的类引用了另外一个类时,虚拟机就会使用装载第一个类的类装载器装载被引用的类。例如,如果虚拟机使用一个特定的类装载器装载Volcano这个类,它就会使用这个类装载器装载Volcano类使用的所有类,如果Volcano使用了一个叫做Lava的类,比方说、可能是调用了Lava类的一个方法,那么虚拟机将使用装载Volcano的同一个类装载器装载Lava,这样,被这个类装载器返回的Lava类就动态地与Volcano类建立起了联系。
    由于Java虚拟机采取这种方式进行类的装载,所以被装载的类默认情况下只能看到被向一个类装载器装载的别的类通过这种方法,Java的体系结构允许在一个Java应用程序中建立多个命名空间。运行时的Java程序中的每一个类装载器都有它自己的命名空间
    双亲委派机制

如果一个类加载器收到了类加载请求,它并不会自己先去加载,而是把这个请求委托给父类的加载器去执行。如果父类加载器还存在其父类加载器,则进一步向上委托,依次递归,请求最终将到达顶层的启动类加载器。如果父类加载器可以完成类加载任务,就成功返回,倘若父类加载器无法完成此加载任务,子加载器才会尝试自己去加载,这就是双亲委派模式。

  • 一个Java应用程序能够从问一个类或者多个类中实例化多个用户定义的类装载器。因此,需要多少个(或多少种)用户自定义的类装载器,Java应用程序就叮以创建多少个(或多少种)。被不同的类装载器装载的类存放在不同的命名空间中,它们不能相互访问,除非应用程序显式地允许这样做。当编写一个Java应用程序的时候,从不同源文件装载的类可以分隔在不同的命名空间中。通过这种方法,就能够使用Java类装载器的体系结构来控制任何从不同源文件中装载的代码之间的相互影响,特别是能够阻止恶意代码获取访问和破坏善意代码的权限
    以下内容摘抄于微信公众号 阿Q说代码

    类加载过程:
    加载(Loading)

加载流程

  • 通过一个类的全限定名获取定义此类的二进制字节流;
  • 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构;
  • 在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口。
    加载.class文件的方式
    从本地系统中直接加载;
    通过网络获取,典型场景:Web Applet;
    从zip压缩包中读取,成为日后jar、war格式的基础;
    运行时计算生成,使用最多的是:动态代理技术;
    由其他文件生成,典型场景:jsp应用;
    从专有数据库中提取.class文件,比较少见;
    从加密文件中读取,典型的防Class文件被反编译的保护措施。
    链接(Linking)
    (1)验证(Verify)
    目的是保证Class文件的字节流中包含的信息符合当前虚拟机的要求,保证被加载类的正确性,不会危害虚拟机的自身安全
    主要分为四种验证方式:文件格式验证、元数据验证、字节码验证、符号引用验证
    文件格式验证:主要验证字节流是否符合Class文件格式规范,并且能被当前的虚拟机加载处理。例如:主、次版本号是否在当前虚拟机处理的范围之内。常量池中是否有不被支持的常量类型。指向常量的中的索引值是否存在不存在的常量或不符合类型的常量
    元数据验证对字节码描述的信息进行语义的分析,分析是否符合java的语言语法的规范
    字节码验证:最重要的验证环节分析数据流和控制,确定语义是合法的,符合逻辑的。主要的针对元数据验证后对方法体的验证。保证类方法在运行时不会有危害出现。
    符号引用验证:主要是针对符号引用转换为直接引用的时候,是会延伸到第三解析阶段主要去确定访问类型等涉及到引用的情况,主要是要保证引用一定会被访问到,不会出现类等无法访问的问题
    (2)准备(Prepare)
  • 为类变量(静态变量)分配内存并且设置该类变量的默认初始值,即零值
  • 这里不包含final修饰的static,因为final在编译的时候就会分配了,准备阶段会显示初始化
  • Java中被final修饰的变量为常量,它只能被赋值一次,也就是说final修饰的变量一旦被赋值,其值不能改变。如果再次对该变量进行赋值,则程序会在编译时报错。

  • 这里不会为实例变量(new的对象)分配初始化,类变量会分配在方法区中,而实例变量是会随着对象一起分配到java堆中
public class HelloWord{//准备阶段:a=0 -> 初始化阶段:a=1private static int a = 1;public static void main(){System.out.println(a);}
}

static静态变量的理解 静态变量 类型说明符是static。 静态变量属于静态存储方式,其存储空间为内存中的静态数据区(在
静态存储区内分配存储单元),该区域中的数据在整个程序的运行期间一直占用这些存储空间(在程序整个运行期间都不释放),也可以认为是其内存地址不变,直到整个程序运行结束(相反,而auto自动变量,即动态局部变量,属于动态存储类别,占动态存储空间,函数调用结束后即释放)。静态变量虽在程序的整个执行过程中始终存在,但是在它作用域之外不能使用。 另外,属于静态存储方式的量不一定就是静态变量。
例如:外部变量虽属于静态存储方式,但不一定是静态变量,必须由 static加以定义后才能成为静态外部变量,或称静态全局变量。
所有的全局变量都是静态变量,而局部变量只有定义时加上类型修饰符static,才为局部静态变量。
静态变量可以在任何可以申请的地方申请,一旦申请成功后,它将不再接受其他的同样申请。 静态变量并不是说其就不能改变值,不能改变值的量叫常量。
其拥有的值是可变的 ,而且它会保持最新的值。说其静态,是因为它不会随着函数的调用和退出而发生变化。即上次调用函数的时候,如果我们给静态变量赋予某个值的话,下次函数调用时,这个值保持不变

  • (3)解析(Resolve)
    将常量池内的符号引用(符号引用就是一组符号来描述所引用的目标)转换为直接引用(直接引用就是直接指向目标的指针、相对偏移量或一个简洁定位到目标的句柄)的过程。事实上,解析操作往往会伴随着JVM在执行完初始化之后再执行。解析动作主要针对类或接口、字段、类方法、方法类型等。对应常量池中的CONSTANT_Class_info、CONSTANT_Fieldref_info、CONSTANT_Methodref_info等。
    初始化(Initialization)
  • 初始化阶段就是执行类构造器方法 ()的过程。此方法不需要定义,是javac编译器自动收集类中的所有类变量的赋值动作和静态代码块中的语句合并而来。构造器方法中的指令按语句在源文件中出现的顺序执行。()不同于类的构造器,构造器是虚拟机视角下的()。
  • 若该类具有父类,JVM会保证子类的()执行前,父类的()已经执行完毕。
    • 虚拟机必须保证一个类的()方法在多线程下被同步加锁。

  • 执行引擎
    字节码由执行引擎来执行。
    不同的Java虚拟机中,执行引擎可能实现得非常不同。在由软件实现的虚拟机中,最简单的执行引擎就是一次性解释字节码另–种执行引擎更快,但是也更消耗内存,叫做“即时编译器”( just-in-time compiler )。在这种情况下,第一次被执行的字节码会被编译成本地机器代码,编译出的本地机器代码会被缓存,当方法以后被调用的时候可以重用第三种执行引擎是自适应优化器。在这种方法里,内拟机开始的时候解释字节码,但是会监视运行中程序的活动,并且记录下使用最频繁的代码段。程序运行的时候,虚拟机只把那些活动最频繁的代码编译成本地代码,其他的代码由于使用得并不很频繁,继续保留为字节码——由虚拟机继续解释它们。一个自适应的优化器可以使得Java虑拟机在80%~90%的时间里执行被优化过的本地代码,而只需要编译l0%~20%对性能有影响的代码。最后一种虚拟机臼硬件芯片构成,它用本地方法执行Java字节码.这种执行引擎实际上是内嵌在芯片里的
    有时候Java虚拟机被称为Iava解释器。然而,考虑到执行字节码的方式可能是不同的,这个称谓可能造成误导,虽然对于直接解释字节码的Iava虚拟机来说“Java解释器”是一个合理的名字。但是当讨论执行技术时,“解释”是一种我们所知道的易于实现而执行缓慢的特殊技术。因此,"Java解释器”只表示是“Java虚拟机”,但并没有任何执行技术的含义。
    当Java虚拟机是由主机操作系统上的软件实现的时候,Java程序通过调用本地方法( nativemethod )和主机交互。Java中有两种方法:Java方法和本地方法。Java方法是由Java语言编写,编译成字节码,存储在class文件中的;本地方法是由其他语言(比如C,C++。或者汇编语言)编写的,编泽成和处理器相关的机器代码。本地方法保存在动态连接库中,格式是各个平台专有的。Java方法是与平台龙关的,但是本地方法却不是。运行中的Java程序调用本地方法时,虚拟机装载包含这个本地方法的动态库,并调用这个方法。在图1-4中可以看到,本地方法是联系Java程序和底层主机操作系统的连接方法。
    通过本地方法,Java程序可以直接访问底层操作系统的资源。如果你这样用,你的程序就变成了平台相关的、因为包含本地方法的动态库是平台相关的。除此之外,使用本地方法还可能把程序变得和特定的Java平台实现相关。一个本地方法接口…—Java本地接口 (Java NativeInterface,JNI)——使得本地方法可以在特定主机系统的任何–个Java平台实现上运行。然而Java平台供应商并不一定必须支持JNI。除了INI之外,他们还可以提供自己的本地方法接口(或者按照合同要求来取代JNI )。
    Java给人们提供了选择的机会。如果希望使用特定主机上的资源,它们又无法从Java API访问,那么可以写一个平台相关的Java 程序来调用本地方法。如果希望保证程序的平台无关性、那么只能通过Java API来访问底层系统资源

Java虚拟机的生命周期

一个运行时的Java虚拟机实例的犬职就是:负责运行一个Java程序。当启动一个Java程序时,一个虚拟机实例也就诞生了。当该程序关闭退出,这个虚拟机实例也就随之消亡。如果在同一台计算机上同时运行三个Java程序,将得到三个Java虚拟机实例。每个Java程序都运行于它自己的Java虚拟机实例中

Java虚拟机实例通过调用某个初始类的main( )方法来运行一个Java程序。而这个main ( )方法必须是公有的( public)、静态的( static ),返回值为void.并且接受一个字符串数组作为参数。任何拥有这样一个main ( )方法的类都可以作为Java程序运行的起点。

JVM运行时数据区

当Java虚拟机运一个程序时,它需要内存来存储许多东西.例如,字节码,从已装载的class文件中得到的其他信息.程序创建的对象、传递给方法的参数,返回值,局部变量,以及运算的中间结果等等。Java虎拟机把这些东西都组纠到几个“运行时数据区”中.以使于管理。

尽管这些“运行时数据区”都会以某种形式存在于每一个lava虚拟机实现中,但是规范对它们的描述却是相当抽象的。这些运行时数据区结构上的细节,大多数都出具体实现的设计者次定。

不同的虚拟机实现可能具有很不同的内存限制、有的实现可能有大量的内存可用,有的可能只有很少。有的实现可以利用虚拟内存、有的则不能.规范本身对“运行时数据区”只有抽象的描述,这就使得Java虚拟机可以很容易地在各种让算机和设备上实现

某些运行时数据区是由程序中所有线程共享的,还有一些则只能由一个线程拥有.每个Java虚拟机实例都有一个方法区以及一个堆它们是t该虚拟机实例中所有线程共享的。当虚拟机装载一个class文件时,它会从这个class文件包含的二进制数据中解析类型信息。然后,它把这些类型信息放到方法区中。当程序运行时,虚拟机会把所有该程序在运行时创建的对象都放到堆中。

当每一个新线程被创建时,它都将得到它自己的PC寄存器(程序计数器)以及一个Java栈。如果线程正在执行的是一个Java方法(非木地方法),那么PC寄存器的值将总是指示下一条将被执行的指令,而它的Java栈则总是存储该线程中Java方法调用的状态——-包括它的局部变量,被调用时传进来的参数,它的返回值,以及运算的中间结果等等。而本地方法调用的状态,则足以某种依赖寸具体实现的方式存储在本地方法栈中、也可能是在寄存器或者其他某些与特定实现相关的内存区中。

Java栈足由许多栈帧(( stack framc)或者说帧( frame)组成的,一个栈帧包含一个Java方法调用的状态。当线程调用一个Java方法时,虚拟机压入一个新的栈帧到该线程的Java战中;当该方法返回时,这个栈帧被从Java栈中弹出并抛弃

Java虚拟机没有寄存器,其指令集使用Java栈来存储中间数据。这样设计的原因足为了保持Java虚拟机的指令集尽量紧凑﹒同时也便于Java虑拟机在那些只有很少通用寄存器的平台上实现。另外、Java虚拟机的这种基于栈的体系结构,也有助于运行时某些虚拟机实现的动态编译器和即时编译器的代码优化。

请看图5-3,它描绘了Java虑拟机为每一个线程创建的内存区,这些内存区域是私有的。任何线程都不能访问另一个线程的PC寄存器或者Java栈。

图5-3展示了一个虚拟机实例的快照,它有三个线程正在执行。线程1和线程2都正在执行Java方法、而线程3则正在执行一个本地方法。

在图5-3中,和本书其他地方一样,Java栈都是向下生长的,而栈顶都显示在图的底部。当前止在执行的方法的栈帧则以浅色表示﹐对于一个正在运行Java方法的线程面言,它的PC寄存器总是指向下一条将被执行的指令。在图5-3中,像这样的PC寄存器(比如线程1和线程2的)都是以浅色显示的﹐由于线程3当前正在执行一个本地方法.因此,它的PC寄存器——以深色显示的那个,其值是不确定的

下面分别讲这5各部分。

方法区

在Java虚拟机中,关于被装载类型的信息存储在一个逻辑上被称为方法区的内存中。当虚拟机装载某个类型时,它使用类装载器定位相应的class文件,然后读入这个class文件——一个线性二进制数据流—―-然后将它传输到虚拟机中.紧接着虚拟机提取其中的类型信息,并将这些信息存储到方法区。该类型中的类〔静态)变量同样也是存储在方法区中

Java虚拟机在内部如何存储类型信息.这是出县体实现的设计者来决定的。比如,在class文件中,多字节值总是以高位在前(即代表较大数的字节在前)的顺序存储。但是当这些数据被引入到方法区后,虚拟机可以以任何方式存储它。假设某个实现是运行在低位优先的处理器上,那么它很可能会把多字节值以低位优先的顺序存储到方法区。

由于所有线程都共享方法区,因此它们对方法区数据的访问必须被设计为是线程安全的。比如,假设同时有两个线程都企图访问一个名为Iava的类,而这个类还没有被装入虚拟机,那么,这时只应该有一个线程去装载它.而另一个线程则只能等待。

类信息:对每个装载的类,虚拟机都会在方法区中存储以下类型信息:

  • 这个类型的全限定名。
  • 这个类型的直接超类的全限定名(除非这个类型是java.Jang.Object,它没有超类)。·这个类型是类类型还是接口类型。
  • 这个类型的访问修饰符〔 public、abstract或final的某个子集)·任何直接超接口的全限定名的有序列表。
  • 在Java class文件和虚拟机中,类名总是以全限定名出现。在Java源代码中,全限定名由类所属包的名称加一个“.”,再加上类名组成。例如,类Object的所属包为java.lang,那它的全限定名应该是java.lang.Object,但在class文件里、所有的“”都被斜杠“/”代替.这样就成为java/lang/Object。至于全限定名在方法区中的表示,则因不同的设计者有不同的选择而不同,可以用任何形式和数据结构来代表。
    除了上面列出的基本类型信息外,虚拟机还得为每个被装载的类存储以下信息:
  • 该类型的常量池。
  • 字段信息。
  • 方法信息。
  • 除了常量以外的所有类(静态)变量
  • 一个到类ClassLoader的引用。
  • 一个到Class类的引用。
    在下面的小节中会描述这些数据。
    常量池。虚拟机必须为每个被装载的类维护一个常量池。常量池就是该类所用常量的一个有序集合,包括直接常量( string. integer和floating point常量)和对其他类型、字段和方法的符号引用。池中的数据项就像数组一样是通过索引访问的。因为常量池存储了相应类型所用到的所有类型、字段和方法的符号引用,所以它在Java程序的动态连接中起着核心的作用。常量池在本章后面和第6章中会详细讨论。
    字段信息。对于类型中声明的每一个字段,方法区中必须保存下面的信息。除此之外,这些字段在类或者接口中的声明顺序也必须保存。下面是字段信息的清单:
  • 字段名。
  • 字段的类型。
  • 字段的修饰符( public、private、protected、static、final、volatile、transient的某个子集)。
    方法信息。对于类型中声明的每一个方法,方法区中必须保存下面的信息。和字段一样,这些方法在类或者接口中的声明顺序也必须保存
    下面是方法信息的清单
  • 方法名。
  • 方法的返回类型(或void )。
  • 方法参数的数量和类型(按声明顺序)。
  • 方法的修饰符( public、private、protected、static、final、synchronized、native、abstract的某个子集)。
    除上面的清单中列出的条目之外,如果某个方法不是抽象的和本地的,它还必须保存下列信息:
  • 方法的字节码( bytecodes )。
  • 操作数栈和该方法的栈帧中的局部变量区的大小(这些在本章的后面会详细描述)。
  • 异常表(参见第17章)。
    类(静态)变量。类变量是由所有类实例共享的,但是即使没有任何类实例,它也可以被访问。这些变量只与类有关——而非类的实例,因此它们总是作为类型信息的一部分而存储在方法区除了在类中声明的编译时常量外,虚拟机在使用某个类之前,必须在方法区中为这些类变量分配空间(对应类加载器的准备阶段)
    而**编译时常量(就是那些用final声明以及用编译时已知的值初始化的类变量)**则和一般的类变量的处理方式不同,每个使用编译时常量的类都会复制它的所有常量到自己的常量池中,或嵌人到它的字节码流中。作为常量池或字节码流的一部分,编译时常量保存在方法区中——就和一般的类变量一样。但是当一般的类变量作为声明它们的类的一部分数据面保存的时候,编译时常量作为使用它们的类的一部分而保存。这种特殊处理方式在第6章中更详细地讨论。

Java程序在运行时创建的所有类实例或数组都放在同–个堆中。而一个Java虚拟机实例中只存在-个堆空间,因此所有线程都将共享这个堆。又由于一个Java程序独占一个Java虚拟机实例,因而每个Java程序都有它自己的堆空间-一它们不会彼此干扰。但是同一个Java程序的多个线程却共享着同一个堆空间,在这种情况下,就得考虑多线程访问对象(堆数据)的同步问题了。

以下内容引用自 20张图助你了解JVM运行时数据区,你还觉得枯燥吗?

PC寄存器
PC寄存器的作用就是用来存储指向下一条指令的地址,也就是即将要执行的指令代码,由执行引擎读取该指令并交由cpu执行。它是程序控制流的指示器,分支,循环,跳转,异常处理,线程恢复等基础功能都需要依赖这个计数器来完成。我们可以把PC寄存器理解为一个记录着当前线程所执行的字节码的行号指示器,也可以理解为一个游标,来告诉程序按照我指定的顺序执行。接下来用例子来演示下它所处的位置与作用。

如图所示,PC寄存器中存储着指向“操作指令”的“指令地址”。假如现在PC寄存器中存储的指令地址是“5”,则执行引擎会取出对应的操作指令,然后做两件事:一是操作局部变量表、操作数栈等完成数据的存、取、加减等操作;二是将操作指令翻译成CPU能识别的机器指令,最后由CPU执行;此时字节码解释器就会改变PC寄存器中的值为“6”,以此类推。
面试题分析
(1)为什么要使用PC寄存器记录当前线程的执行地址呢?
JVM的多线程是通过CPU时间片轮转(即线程轮流切换并分配处理器执行时间)算法来实现的。也就是说,某个线程在执行过程中可能会因为时间片耗尽而被挂起,而另一个线程获取到时间片开始执行。当被挂起的线程重新获取到时间片的时候,它要想从被挂起的地方继续执行,就必须知道它上次执行到哪个位置,这时候就需要PC寄存器来记录某个线程的字节码执行位置,如果虚拟机是单线程也就没必要用程序计数器记录每个线程的位置了。
(2)PC寄存器为什么会被设定为线程私有呢?
由于jvm的多线程是通过线程轮流切换并分配处理器执行时间的方式来实现的,在任何一个确定的时刻,一个处理器都只会执行一条线程中的指令。因此为了能够准确的记录各个线程正在执行的当前字节码指令地址,最好的办法自然就是为每一个线程都分配一个PC寄存器。这样各条线程之间计数器互不影响,独立存储。

虚拟机栈

在这里我们要对“栈”和“堆”做一个简单的区分,其中栈是运行时的单位,它解决的是程序运行的问题,即程序如何执行,或者说是如何处理数据;堆是存储的单位,它解决的是数据存储的问题,即数据怎么放、放在哪。我们举个简单的例子:假如你正在修理汽车,我们可以把修车的操作步骤看做是栈操作,而把汽车的零件一个个放到汽车中就可以看做是堆存储。
Java虚拟机栈,早期也叫Java栈。每个线程在创建时都会创建一个虚拟机栈,所以虚拟机栈是线程私有的,当线程结束时虚拟机栈也就结束了。JVM对虚拟机栈的操作只有进栈和出栈,所以它的访问速度仅次于程序计数器,也是一种快速有效的分配存储方式。对于虚拟机栈来说它不存在垃圾回收问题,但是虚拟机栈的大小是动态的或者固定不变的,所以它会存在栈溢出或者内存溢出问题

  • 栈溢出:如果采用固定大小的虚拟机栈,那每一个线程的虚拟机栈容量可以在线程创建的时候独立选定。如果线程请求分配的栈容量超过虚拟机栈允许的最大容量,虚拟机栈会抛出StackOverflowError异常。
  • 内存溢出:如果虚拟机栈可以动态扩展,并且在尝试扩展的时候无法申请到足够的内存,或者在创建新的线程时没有足够的内存去创建对应的虚拟机栈,那虚拟机将会抛出OutOfMemoryError异常。栈的大小直接决定了函数调用的最大可达深度,我们可以通过-Xss参数来配置栈内存,追加字母k或K表示KB,m或M表示MB,g或G表示GB,示例:-Xss1m。

虚拟机栈主管Java程序的运行,保存方法的局部变量(8种基本数据类型、对象的引用地址)、部分结果,并参与方法的调用和返回,那它内部到底是什么构造呢?虚拟机栈内部保存着一个一个的栈帧(Stack Frame),每个栈帧与该线程正在执行的每个方法都是一一对应的。栈帧是一个内存区块,是一个数据集,维系着方法执行过程中的各种数据信息。在一条活动线程中,一个时间点上,只会有一个活动的栈帧。即只有当前正在执行的方法的栈帧(栈顶栈帧)是有效的,这个栈帧被称为当前栈帧 (Current Frame),与当前栈帧相对应的方法就是当前方法(Current Method),定义这个方法的类就是当前类(Current Class)。执行引擎运行的所有字节码指令只针对当前栈帧进行操作。执行过程如下图:

程序开始执行,首先方法1入栈,为栈帧1,此时栈帧1为当前栈帧;随后方法1调用方法2,方法2入栈,为栈帧2,此时栈帧2为当前栈帧,以此类推;当方法4入栈成为栈帧4并且执行代码,在方法4返回之际,栈帧4会传回方法4的执行结果给栈帧3,接着,虚拟机会丢弃栈帧4即栈帧4出栈,使得栈帧3重新成为当前栈帧,以此类推,直到方法1执行完成,栈帧1出栈,虚拟机栈被回收
Java方法有两种返回函数的方式,一种是正常的函数返回,使用return指令(包含void返回类型);一种是抛出异常(指的是未处理的异常,如果是try…catch过了,算第一种)。不管使用哪种方式,都会导致栈帧出栈。不同线程中所包含的栈帧是不允许存在互相引用的,即不可能在一个栈帧之中引用另外一个线程的栈帧

本地方法栈

本地方法其实就是java调用非java代码的接口,该接口由非java语言实现。本地接口的作用是融合不同的编程语言为java所用,它的初衷是融合C/C++程序。
本地方法栈是用来管理本地方法的调用的,也是线程私有的。他也允许被实现成固定或者可动态扩展的内存大小,在内存溢出方面与虚拟机栈类似。本地方法栈的具体做法是Native Method Stack中登记native方法,在Execution Engine执行时加载本地方法库。

JVM合集之开篇点题
20张图助你了解JVM运行时数据区,你还觉得枯燥吗?
静态变量的理解
《深入Java虚拟机第二版》

Java虚拟机(JVM)学习合集相关推荐

  1. Java虚拟机JVM学习06 自定义类加载器 父委托机制和命名空间的再讨论

    Java虚拟机JVM学习06 自定义类加载器 父委托机制和命名空间的再讨论 创建用户自定义的类加载器 要创建用户自定义的类加载器,只需要扩展java.lang.ClassLoader类,然后覆盖它的f ...

  2. Java虚拟机JVM学习05 类加载器的父委托机制

    Java虚拟机JVM学习05 类加载器的父委托机制 类加载器 类加载器用来把类加载到Java虚拟机中. 类加载器的类型 有两种类型的类加载器: 1.JVM自带的加载器: 根类加载器(Bootstrap ...

  3. java虚拟机预先加载哪些类_Java虚拟机JVM学习02 类的加载概述

    Java虚拟机JVM学习02 类的加载概述 类的加载 类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang.Class对 ...

  4. Java虚拟机(JVM)与Java内存模型(JMM)学习笔记

    Java虚拟机[JVM]与Java内存模型[JMM]学习笔记 Java虚拟机(JVM) 三种JVM JVM 位置 JVM的主要组成部分及其作用 类加载器 双亲委派机制 沙箱安全机制 Java本地接口( ...

  5. Java语言基础大合集!让兴趣助你更好的学习,赢得未来江湖

    前言 众所周知,Java是一门面向对象编程语言,不仅吸收了C++语言的各种优点,还摒弃了C++里难以理解的多继承.指针等概念,因此Java语言具有功能强大和简单易用两个特征. Java语言作为静态面向 ...

  6. 深入理解JVM文章合集

    原文地址:http://ddrv.cn/a/88331 Java动态追踪技术探究 在Java虚拟机中,字符串常量到底存放在哪 一次生产 CPU 100% 排查优化实践 聊聊 Java 虚拟机:类的加载 ...

  7. [java] 虚拟机(JVM)底层结构详解[转]

    [java] 虚拟机(JVM)底层结构详解[转] 本文来自:曹胜欢博客专栏.转载请注明出处:http://blog.csdn.net/csh624366188 在以前的博客里面,我们介绍了在java领 ...

  8. 京东java笔试_2017阿里,百度,京东java面试+笔试大合集,2018的你会吗?

    2017阿里,百度,京东java面试+笔试大合集 1.阿里 面试(一二面合集) 1.介绍一下你自己. 2.介绍一下你认为做的最好的一个项目. 3.请用五分钟的时间把你做的项目的流程图画一下. 4.项目 ...

  9. Java虚拟机JVM的内部体系结构

    JVM(Java虚拟机)是一个抽象机器. 它是一个提供可以执行Java字节码的运行时环境的规范.JVM可用于许多硬件和软件平台(即JVM是平台相关的). 什么是JVM? JVM(Java虚拟机)是: ...

  10. 深入理解java虚拟机 - jvm高级特性与最佳实践(第三版)_JVM虚拟机面试指南:年薪30W以上高薪岗位需求的JVM,你必须要懂!...

    JVM的重要性 很多人对于为什么要学JVM这个问题,他们的答案都是:因为面试.无论什么级别的Java从业者,JVM都是进阶时必须迈过的坎.不管是工作还是面试中,JVM都是必考题.如果不懂JVM的话,薪 ...

最新文章

  1. LINUX DNS服务的配置(一)
  2. php课程实验总结报告_PHP课程总结20161125
  3. 整合rabbitmq+redis发送验证码消息
  4. 经济学原理 下载 曼昆_2021南开经济学考研全年规划
  5. zipkin brave mysql_zipkin mysql表结构
  6. 问题分享:最近测试VDI-in-a-Box使用AD做身份验证出现以下错误提示:
  7. python打开浏览器全屏_python 打开浏览器的两种方式
  8. 图标字体化 android,Android 优化 图标文字 iconfont
  9. mock模拟接口测试_Python接口测试之mock(上)
  10. ffempge常用指令_fluent-ffmpeg 常用函数
  11. C语言中 malloc函数介绍
  12. 广州高清卫星地图 用百度卫星地图服务器下载 含标签、道路数据叠加 可商用
  13. Volatile关键字~转载自博客园的“海子”
  14. 网络安全技术及应用--网络安全基础(第一章)
  15. linux怎么建立辅助dns,rhel5 建立辅助DNS
  16. 解决了计算机处理汉字的问题,计算机处理汉字必须解决的三个问题分别是汉字...
  17. 充电枪cp信号控制板_通过充电枪CP信号控制常电输入的电路的制作方法
  18. 下载全免费瑞星升级包的好地方
  19. NVMe SSD 基本功
  20. 目标检测——卷积神经网路基础知识

热门文章

  1. 百战c++(数据库2)
  2. Recovering Realistic Texture in Image Super-resolution by Deep Spatial Feature Transform
  3. vp230引脚功能_CAN收发器—TJA1040与TJA1050区别
  4. 2020.11.30【NOIP提高B组】模拟 总结
  5. GLCC编程夏令营——LMP课题 周会分享
  6. 139邮箱注册免费注册 html5.mail.10086.cn,139邮箱注册(登录139免费邮箱)
  7. 阿里云上部署的SQL Server服务器和SVN服务器不能远程访问
  8. NASA研制3D食物打印机:原料或取自昆虫
  9. 三秒让你学会公私网地址转换(NAT)
  10. 机器学习6:——Pandas——6:高级处理3:数据合并