JVM原理

·  JVM是Java Virtual Machine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。
  JAVA之所以是跨平台的语言,就是因为JVM。在不同的平台上有相应的JVM,这样我们写的java语言是相同的,在不同的操作系统中就可以游刃有余。实质上,JVM是JAVA跨平台的重要核心。

一、JVM的概念

·  JVM是Java Virtual Machine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。Java虚拟机包括一套字节码指令集、一组寄存器、一个栈、一个垃圾回收堆和一个存储方法域。 JVM屏蔽了与具体操作系统平台相关的信息,使Java程序只需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行。JVM在执行字节码时,实际上最终还是把字节码解释成具体平台上的机器指令执行。

二、JVM的原理

·  JVM是java的核心和基础,在java编译器和os平台之间的虚拟处理器。它是一种利用软件方法实现的抽象的计算机基于下层的操作系统和硬件平台,可以在上面执行java的字节码程序。java编译器只要面向JVM,生成JVM能理解的代码或字节码文件。Java源文件经编译成字节码程序,通过JVM将每一条指令翻译成不同平台机器码,通过特定平台运行。

1.JVM生命周期

·  JVM伴随Java程序的开始而开始,程序的结束而停止。一个Java程序会开启一个JVM进程,一台计算机可以运行多个程序们也就可以运行多个JVM。
JVM将线程分为两种,守护线程和普通线程。守护线程是JVM自己使用的线程。普通线程一般是java程序的线程,只要JVM中有普通线程在执行,那么JVM就不会停止。

2.内存模型的组成
·  JVM内存模型主要有堆内存,方法区,程序计数器,虚拟机栈和本地方法栈组成。具体在这里插入图片描述
  
·  其中,堆和方法区是所有线程共有的,而虚拟机栈,本地方法栈和程序计数器则是线程私有的。
  下面,我们具体介绍这几个模块
  
  堆内存 
  被所有线程共享,在虚拟机启动时创建,用来存放对象实例,几乎所有的对象实例都在这里分配内存。对于大多数应用来说,Java堆(Java Heap)是Java虚拟机所管理的内存中最大的一块。
  Java堆是垃圾收集器管理的主要区域,因此很多时候也被称做“GC堆”。如果从内存回收的角度看,由于现在收集器基本都是采用的分代收集算法,所以Java堆中还可以细分为:新生代和老年代;新生代又有Eden空间、From Survivor空间、To Survivor空间三部分。
  Java 堆不需要连续内存,并且可以通过动态增加其内存,增加失败会抛出 OutOfMemoryError 异常。
  在这里插入图片描述
  方法区
  方法区和java堆一样,是各个线程共享的区域,他用于存储已被虚拟机加载的类信息,常量,静态常量,及时编译后的代码等数据。
  由于程序中所有的线程都共享一个方法区,所以访问方法区的信息必须确保线程是安全的。如果有两个线程同时去加载一个类,那么只有一个线程被允许去加载这个类,另一个必须等待。
  程序运行时,方法区的大小是可以改变的,程序在运行时可以扩展。同时方法去里面的对象可以被垃圾回收,但条件非常苛刻,必须在该类没有任何引用的情况下才能被GC回收。
  
  程序计数器
  程序计数器是可以当做java执行(一个线程)的指示器,执行下一条指令,选择哪一条路径,是否循环操作等都是依赖这个指示器来执行的。java虚拟机的多线程是通过线程。

·  轮流切换并分配处理器执行时间来实现的,在任何一个确定的时刻,一个处理器或内核都只会执行一条线程的指令,因此为了线程能够恢复到正确的执行位置,每条线程都要有一个独立的程序计数器,并且不同线程中的程序计数器互不影响,也就是说 程序计数器是线程私有的。

·  如果线程正在执行一个java方法 则这个程序计数器记录的是正在执行的虚拟机字节码指令的地址,如果是本地方法(native)则计数器值为空。

·  程序计数器是唯一一个在虚拟机中没有内存溢出的区域。
  虚拟机栈
  每个java方法在执行时,会创建一个“栈帧(stack frame)”,栈帧的结构分为“局部变量表、操作数栈、动态链接、方法出口”几个部分(具体的作用会在字节码执行引擎章节中讲到,这里只需要了解栈帧是一个方法执行时所需要数据的结构)。我们常说的“堆内存、栈内存”中的“栈内存”指的便是虚拟机栈,确切地说,指的是虚拟机栈的栈帧中的局部变量表,因为这里存放了一个方法的所有局部变量。
  当Java虚拟机运行程序时。每当一个新的线程被创建时。Java 虚拟机都会分配一个虚拟机栈,Java虚拟机栈是以帧为单位来保存线程的运行状态。Java栈只会有两种操作:以帧为单位进行压栈跟出栈。 某个线程正在执行的方法称为当前方法,以此类推出当前类,当前常量池(每一个方法都有自己唯一的常量池)
  每当线程调用当前方法时,都会将,新栈压入,成为当前帧。jvm会使用它来存储我们的形参,局部变量,中间运行结果等。
执行结束返回的方式会有两种,一种是 retuen正常返回,另外一种是通过异常来返回。无论哪种方式虚拟机都会释放弹出当前帧。这样上一个方法就成为了当前帧 。之前我们提过Java栈中的数据都是线程私有的,因此线程都不能访问另一个线程的数据。因此我们在此无需考虑多线程。
   
   本地方法栈
   JVM采用本地方法堆栈来支持native方法的执行,此区域用于存储每个native方法调用的状态。本地方法栈与Java栈的作用和原理非常相似。区别只不过是Java栈是为执行Java方法服务的,而本地方法栈则是为执行本地方法(Native Method)服务的。在JVM规范中,并没有对本地方法栈的具体实现方法以及数据结构作强制规定,虚拟机可以自由实现它。在HotSopt虚拟机中直接就把本地方法栈和Java栈合二为一。
 
 3.类加载器
 JVM类加载机制分为五个部分:加载,验证,准备,解析,初始化,下面我们就分别来看一下这五个过程。
  
  加载是类加载过程中的一个阶段,这个阶段会在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的入口。注意这里不一定非得要从一个Class文件获取,这里既可以从ZIP包中读取(比如从jar包和war包中读取),也可以在运行时计算生成(动态代理),也可以由其它文件生成(比如将JSP文件转换成对应的Class类)。
  这一阶段的主要目的是为了确保Class文件的字节流中包含的信息是否符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。
  
  验证:确保被加载类的正确性,即确保被加载的类符合javac编译的规范
  
  准备:为类的静态变量分配内存,并初始化为默认值
  
  解析:将类中的符号引用转化为直接引用
  
  注:符号引用即一个Java源文件在被编译时,在不清楚被引用类实际内存地址的情况下,会使用能唯一识别并定位到目标的符号来代替。如A类引用了B类,编译时A并不知道B类实际的内存地址,故可以使用能唯一识别B的符号来代替。而当类加载时,编译后的.class文件实际已被调入内存,可知道A,B类的实际内存地址,当引用的目标已被加载入内存,则此时的引用为直接引用。

·  初始化阶段是类加载最后一个阶段,前面的类加载阶段之后,除了在加载阶段可以自定义类加载器以外,其它操作都由JVM主导。到了初始阶段,才开始真正执行类中定义的Java程序代码。
  初始化阶段是执行类构造器方法的过程。方法是由编译器自动收集类中的类变量的赋值操作和静态语句块中的语句合并而成的。虚拟机会保证方法执行之前,父类的方法已经执行完毕。p.s: 如果一个类中没有对静态变量赋值也没有静态语句块,那么编译器可以不为这个类生成()方法。

  • 3
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值