一、JVM的概念
JVM是Java Virtual Machine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。
Java语言的一个非常重要的特点就是与平台的无关性。而使用Java虚拟机是实现这一特点的关键。一般的高级语言如果要在不同的平台上运行,至少需要编译成不同的目标代码。而引入Java语言虚拟机后,Java语言在不同平台上运行时不需要重新编译。Java语言使用Java虚拟机屏蔽了与具体平台相关的信息,使得Java语言编译程序只需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行。Java虚拟机在执行字节码时,把字节码解释成具体平台上的机器指令执行。这就是Java的能够“一次编译,到处运行(write once,run anywhere)”的原因。
二、JDK、JRE和JVM的区别
JDK:编译、调试java程序用的开发工具包。包含JRE、JVM以及常用的开发工具,如javac java jconsole等。
JRE:Java的运行环境。
JVM:JRE的一部分,把编译好的class文件解析执行,调用操作系统的本地方法,通过执行引擎翻译成不同平台机器码,在特定平台运行。
三、JVM的运行流程
四、JVM的生命周期
1.启动:main方法是jvm实例运行的起点
2.运行:main()作为程序初始线程的起点,任何其他线程都是由该线程负责启动,main线程是非守护线程
3.消亡:所有的非守护线程都终止时;调用System.exit();
五、JVM的结构
JVM组成:
类加载器(class loader):只负责class文件的加载,至于它是否可以运行,则由执行引擎决定。
执行引擎(Execution Engine):class文件被加载后,会把指令和数据信息放入内存中,执行引擎则负责把这些命令解释给操作系统。
运行时数据区(Runtime Data Area):包含堆、JVM栈、本地方法栈、方法区、PC寄存器(程序计数器)。
本地库接口(Native Interface):调用不同语言的接口给JAVA用,会在本地方法栈中记录对应的本地方法,然后调用该方法时就通过执行引擎加载对应的本地lib。
六、类加载器(classLoader)
1.概念:负责加载字节码到jvm中,根据类中定义的内容分配到不同的运行时数据区。
2.分类:
Bootstrap ClassLoader:classLoader的根,rt.jar
Extension ClassLoader
Application ClassLoader
用户自定义类加载器
3.加载过程
1).加载
负责找到class文件,并加载到jvm中
2).连接
校验:确保类的格式正确
准备:为类中的变量分配内存空间,并初始化默认值
解析:符号引用转换为直接引用
3).初始化: 执行静态代码块,构造器,静态属性。在调用new(), 反射调用类中的方法,子类调用初始化函数来初始化。
七、JVM运行时数据区
1.堆(Heap)
它是JVM用来存储对象实例以及数组值的区域,可以认为Java中所有通过new创建的对象的内存都在此分配,Heap中的对象的内存需要等待GC进行回收。jvm只有一个堆区(heap)被所有线程共享,堆内存用来存储Java中的对象。无论是成员变量,局部变量,还是类变量,它们指向的对象都存储在堆内存中。
2.JVM栈(Stack)
参考:http://www.cnblogs.com/yyyyy5101/archive/2011/03/23/1992296.html
开发中,每当我们在程序中使用new生成一个对象,对象的引用存放在栈里,而对象是存放在堆里。
VM栈是线程私有的,每个线程创建的同时都会创建JVM栈,JVM栈中存放的为当前线程中局部基本类型的变量(java中定义的八种基本类型:boolean、char、byte、short、int、long、float、double)、部分的返回结果以及Stack Frame,非基本类型的对象在JVM栈上仅存放一个指向堆上的地址。
Java栈上的所有数据都是私有的。任何线程都不能访问另一个线程的栈数据。所以我们不用考虑多线程情况下栈数据访问同步的情况。
栈帧:一个栈帧随着一个方法的调用开始而创建,这个方法调用完成而销毁。栈帧内存放者方法中的局部变量,操作数栈等数据。
栈帧由三部分组成:局部变量区、操作数栈、帧数据区。局部变量区和操作数栈的大小要视对应的方法而定,他们是按字长计算的。但调用一个方法时,它从类型信息中得到此方法局部变量区和操作数栈大小,并据此分配栈内存,然后压入Java栈。
局部变量区被组织为以一个字长为单位、从0开始计数的数组,类型为short、byte和char的值在存入数组前要被转换成int值,而long和 double在数组中占据连续的两项,在访问局部变量中的long或double时,只需取出连续两项的第一项的索引值即可,如某个long值在局部变量 区中占据的索引时3、4项,取值时,指令只需取索引为3的long值即可。
操作数栈和局部变量区一样,操作数栈也被组织成一个以字长为单位的数组。但和前者不同的是,它不是通过索引来访问的,而是通过入栈和出栈来访问的。可把操作数栈理解为存储计算时,临时数据的存储区域。
3.本地方法栈
JVM采用本地方法堆栈来支持native方法的执行,此区域用于存储每个native方法调用的状态。
4.方法区
方法区域存放了所加载的类的信息(名称、修饰符等)、类中的静态变量、类中定义为final类型的常量、类中的Field信息、类中的方法信息,当开发人员在程序中通过Class对象中的getName、isInterface等方法来获取信息时,这些数据都来源于方法区域,同时方法区域也是全局共享的,在一定的条件下它也会被GC,当方法区域需要使用的内存超过其允许的大小时,会抛出OutOfMemory的错误信息。
1) 类信息:类的全路径名,父类的全路径名,类型是接口还是类,类的修饰符(public,private,abstract,final)
2) 字段信息:字段名,字段类型,字段修饰符(public,private,protected,static,final,volatile)
3) 方法信息:方法名,方法的返回类型(String,void等),方法参数的类型、数目以及顺序,方法修饰符(public,private,protected,static,final,synchronized,native,abstract)
4) 类变量信息(静态变量)
5) ClassLoader引用:通过类加载器加载的对象类型,JVM必须存储对类的引用,而这些针对类加载器的引用是作为了方法区里面的类型数据部分进行存储的。
5.PC寄存器
PC寄存器是用于存储每个线程下一步将执行的JVM指令,若该方法为native的,则PC寄存器中不存储任何信息。
八、参考