JVM(Java虚拟机),为java实现了跨平台性,不需要多次进行编写不同平台的代码,因为JVM会将不同字节码转换为特定的机器代码。
一、JVM的组成部分:JVM解释器、指令系统、寄存器、栈、存储区和碎片回收区。
◆JVM解释器:即虚拟机处理字段码的CPU。
◆JVM指令系统:该系统与计算机很相似,一条指令由操作码和操作数两部分组成。操作码为8位二进制数,主要是为了说明一条指令的功能,操作数可以根据需要而定,JVM最多有256种不同的操作指令。目前已使用了160多种操作码。
◆寄存器:JVM有自己的虚拟寄存器,这样就可以快速地与JVM的解释器进行数据交换。为了功能的需要,JVM设置了4个常用的32位寄存器:pc(程序计数器)、optop(操作数栈顶指针)、frame(当前执行环境指针)和vars(指向当前执行环境中第一个局部变量的指针)。
◆JVM栈:指令执行时数据和信息存储的场所和控制中心,它提供给JVM解释器运算所需要的信息。当JVM得到一个Java字节码应用程序后,便为该代码中一个类的每一个方法创建一个栈框架,以保存该方法的状态信息。每个栈框架包括以下三类信息:局部变量、执行环境、操作数栈。
局部变量用于存储一个类的方法中所用到的局部变量。vars寄存器指向该变量表中的第一个局部变量。
执行环境用于保存解释器对Java字节码进行解释过程中所需的信息。它们是:上次调用的方法、局部变量指针和操作数栈的栈顶和栈底指针。执行环境是一个执行一个方法的控制中心。例如:如果解释器要执行iadd(整数加法),首先要从frame寄存器中找到当前执行环境,而后便从执行环境中找到操作数栈,从栈顶弹出两个整数进行加法运算,最后将结果压入栈顶。
操作数栈用于存储运算所需操作数及运算的结果。
◆存储区:JVM有两类存储区:常量缓冲池和方法区。常量缓冲池用于存储类名称、方法和字段名称以及串常量。方法区则用于存储Java方法的字节码。
◆碎片回收区:JVM碎片回收是指将使用过的Java类的具体实例从内存进行回收,这就使得开发人员免去了自己编程控制内存的麻烦和危险。随着JVM的不断升级,其碎片回收的技术和算法也更加合理。JVM 1.4.1版后产生了一种叫分代收集技术,简单来说就是利用对象在程序中生存的时间划分成代,以此为标准进行碎片回收。
二、JVM的内存模型:方法区、堆、(虚拟机)栈、本地方法栈、程序计数器
1、方法区:主要存储类级别数据,也称作运行时常量池。使用””直接声明的内容存储在这里
2、堆:存放的是new的实例化对象,实例变量以及数组。是GC最频繁的部分,在JVM启动时就会创建。因为所有线程共享方法区和堆,因此二者都是非线程安全的。
3、栈:执行java方法的地方。对每个线程单独创建一个运行时栈,所有局部变量在一个栈内存创建。
4、本地方法栈:执行native方法的地方。
5、程序计数器:存储在CPU中。
比如:String s1=new String("a");
String s2="a";
首先通过main方法运行进栈
栈中定义一个对象s1,因为是new所以去堆中开辟一个内存空间,将内存空间的引用赋值给s1,然后去字符串常量池中查找是否有a这个变量存在,若有直接使用,没有则在常量池中分配一个空间进行存放,并且将地址存入new出来的内存空间中。
栈中定义一个对象s2,去字符串常量池中进行匹配查找是否存在a对象,有的话直接把a的地址赋给s2
s1中存的是堆中分配的空间,堆中分配的空间中存的是字符串常量池中分配空间存放”a”的空间的地址值。而s2中之间存的是字符串常量池中分配空间存放”a”的空间的地址值。
三、JVM的类加载机制:加载,验证,准备,解析,初始化
类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang.Class对象,用来封装类在方法区内的数据结构。在这五个阶段中,加载、验证、准备和初始化这四个阶段发生的顺序是确定的,而解析阶段则不一定,它在某些情况下可以在初始化阶段之后开始,这是为了支持Java语言的运行时绑定(也成为动态绑定或晚期绑定)。
双亲委派模型::如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把请求委托给父加载器去完成,依次向上,因此,所有的类加载请求最终都应该被传递到顶层的启动类加载器中,只有当父加载器在它的搜索范围中没有找到所需的类时,即无法完成该加载,子加载器才会尝试自己去加载该类。
四、JVM中的垃圾回收机制: GC通过确定对象是否被活动对象引用来确定是否收集该对象。
1、System.gc()方法
使用System.gc()可以不管JVM使用的是哪一种垃圾回收的算法,都可以请求Java的垃圾回收,但是不一定保证确实会进行回收,并且只收集由new关键字创建的对象。
2、finalize()方法
在JVM垃圾回收器收集一个对象之前,一般要求程序调用适当的方法释放资源,但在没有明确释放资源的情况下,Java提供了缺省机制来终止该对象心释放资源,这个方法就是finalize()。它的原型为:protected void finalize() throws Throwable 在finalize()方法返回之后,对象消失,垃圾收集开始执行。原型中的throws Throwable表示它可以抛出任何类型的异常。之所以要使用finalize(),是存在着垃圾回收器不能处理的特殊情况。例如:1)由于在分配内存的时候可能采用了类似 C语言的做法,而非JAVA的通常new做法。这种情况主要发生在native method中,比如native method调用了C/C++方法malloc()函数系列来分配存储空间,但是除非调用free()函数,否则这些内存空间将不会得到释放,那么这个时候就可能造成内存泄漏。但是由于free()方法是在C/C++中的函数,所以finalize()中可以用本地方法来调用它。以释放这些“特殊”的内存空间。2)又或者打开的文件资源,这些资源不属于垃圾回收器的回收范围。