1. JVM简介
- 初识JVM
JVM是Java Virtual Machine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。
Java语言的一个非常重要的特点就是与平台的无关性。而使用Java虚拟机是实现这一特点的关键。一般的高级语言如果要在不同的平台上运行,至少需要编译成不同的目标代码。而引入Java语言虚拟机后,Java语言在不同平台上运行时不需要重新编译。Java语言使用Java虚拟机屏蔽了与具体平台相关的信息,使得Java语言编译程序只需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行。Java虚拟机在执行字节码时,把字节码解释成具体平台上的机器指令执行。这就是Java的能够“一次编译,到处运行”的原因。
从编译原理的角度来看,可以将编译过程按照“前端+后端”的方式进行划分。所谓的前端,就是指依赖源语言而与目标机无关,而后端则指依赖目标机而不依赖源语言。JVM的这种实现机制属于不同机器构造统一语言的编译程序的模式,即“同一前端+不同后端”;同样的道理,还有“不同前端+同一后端”,即同一机器生成不同语言的编译程序的模式,如.NET平台
JVM是Java程序运行的容器,但是他同时也是操作系统的一个进程,因此他也有他自己的运行的生命周期,也有自己的代码和数据空间。一个运行时的JVM实例负责运行一个Java程序。当启动一个Java程序,则一个虚拟机实例也就诞生了,当程序关闭退出,虚拟机实例也就随之消亡,每个Java程序都运行于各自的虚拟机实例中。
每个JVM都有两种机制,一个是装载具有合适名称的类(类或是接口),叫做类装载子系统;另外的一个负责执行包含在已装载的类或接口中的指令,叫做运行引擎。类装载子系统除了要定位和导入二进制class文件外,还必须负责验证被导入类的正确性,为类变量分配并初始化内存,以及帮助解析符号引用。
不容忽视的是运行时数据区,又叫虚拟机内存或者Java内存,虚拟机运行时需要从整个计算机内存划分一块内存区域存储许多东西。例如:字节码、从已装载的class文件中得到的其他信息、程序创建的对象、传递给方法的参数,返回值、局部变量等等。Java虚拟机定义了若干种程序运行时所使用的运行时数据区,有些是随着Java虚拟机的启动而创建的,同时,随着虚拟机的关闭而销毁,而有些则是与线程一一对应。
- 认识Java内存
上面已经说到,运行时数据区即Java内存,根据存储数据的不同,Java内存通常被划分为5个区域:程序计数器(Program Count Register)、本地方法栈(Native Stack)、方法区(Methon Area)、栈(Stack)、堆(Heap)。下面针对每部分的功能进行简单介绍:
程序计数器/PC寄存器:又叫程序寄存器。JVM支持多个线程同时运行,当每一个新线程被创建时,它都将得到它自己的PC寄存器(程序计数器)。如果线程正在执行的是一个Java方法(非native),那么PC寄存器的值将总是指向下一条将被执行的指令,如果方法是 native的,程序计数器寄存器的值不会被定义。简单来说,程序寄存器可以看做当前线程所执行的字节码的信号指示器。
栈:又叫堆栈。JVM为每个新创建的线程都分配一个栈。
本地方法栈:存储本地方方法的调用状态。
方法区:当虚拟机装载一个class文件时,它会从这个class文件包含的二进制数据中解析类型信息,然后把这些类型信息(包括类信息、常量、静态变量等)放到方法区中。常量池存放在方法区中。
堆:堆是Java虚拟机所管理的内存中最大的一块,是被所有线程共享的一块内存区域。在此区域的唯一目的就是存放对象实例,几乎所有的对象实例都是在这里分配内存,但是这个对象的引用却是在栈中分配。因此,执行String s = new String(“s”)时,需要从两个地方分配内存:在堆中为String对象分配内存,在栈中为引用(这个堆对象的内存地址,即指针)分配内存。