JVM内存划分
为了便于管理,JVM在执行java程序的时候,会把它所管理的内存划分为多个不同区域。
1,class文件
class文件是java程序编译后生成的中间代码,这些中间代码将会被JVM解释执行。
2,类装载器子系统
类装载器子系统负责把class文件装载到内存中,供虚拟机执行。JVM有两种类装载器,分别是启动类装载器和用户自定义类装载器。其中,启动类装载器是JVM实现的一部分;用户自定义类装载器则是java程序,必须是ClassLoader类的子类。常见的类加载器主要有以下几种:
1) Bootstrap ClassLoader,这是JVM的根ClassLoader,它是用C++语言实现的,当JVM启动时,初始化此ClassLoader,并由此ClassLoader完成$JAVA_HOME中jre/lib/rt.jar(sun jdk的实现)中所有class文件的加载,这个jar中包含了java规范定义的所有接口及实现。
2) Extension ClassLoader。JVM用此ClassLoader来加载扩展功能的一些jar包。
3) System ClassLoader。JVM用此ClassLoader来加载启动参数中指定的ClassPath中的jar包及所在目录,在SunJDK中,ClassLoader对应的类名为AppClassLoader。
4) User-Defined ClassLoader。是Java开发人员继承ClassLoader抽象类自行实现的ClassLoadre,基于自定义ClassLoader可用于加载非Classpath中的jar及目录。
3,方法区
方法区用来存储被虚拟机加载的类信息、常量、静态常量、编译器编译后的代码等数据。在类加载器加载class文件的时候,这些信息将会被提取出来,并存储到方法区中,由于这个区域是所有线程共享的区域,因此,它被设计成为线程安全的。方法区可以被看成JVM的一个规范,在HotSpot中,方法区是用Perm区来实现的方法区。
在JDK1.6以及以下版本中,方法区中还存放了运行时的常量池,最典型的应用就是字符串常量,从JDK1.7开始,字符串常量池已经被移到堆区了。
4,堆
堆是虚拟机启动的时候创建的被所有线程共享的区域。这块区域主要用来存放对象的实例,通过new操作创建出来的对象的实例都存储在堆空间中,因此,堆就成为垃圾回收器管理的重点区域。
5,虚拟机栈
栈是线程私有的区域,每当有新的线程创建时,就会给它分配一个栈空间,当线程结束后,栈空间就被回收,因此,栈与线程拥有相同的生命周期。栈主要用来实现java语言中方法的调用与执行,没个方法在被执行的时候,都会创建一个栈帧用来存储这个方法的局部变量、操作栈、动态链接和方法出口等信息。当进行方法调用时,通过压栈与弹栈操作进行栈空间的分配与释放。当一个方法被调用的时候,会压入一个新的栈帧到这个线程的栈中,当方法调用结束后,就会弹出这个栈帧,从而回收掉调用这个方法用的栈空间。
6,程序计数器
程序计数器也是线程私有的资源,JVM会给每个线程创建单独的程序计数器。它可以被看作是当前线程执行的字节码的行号指示器,解释器的工作原理就是通过改变这个计数器额值来确定下一条需要被执行的字节码指令,程序控制的流程(循环、分支、异常处理、线程恢复)都是通过这个计数器来完成的。
7,本地方法栈
本地方法栈与虚拟机栈的作用是相似的,唯一不同的是虚拟机栈为虚拟机执行java方法(也就是字节码)服务,而本地方法栈则是为虚拟机使用到的Native方法服务,Native方法接口都会使用某种本地方法栈,当线程调用java方法时,JVM会创建一个新的栈帧并压入虚拟机栈。然而当它调用的是本地方法时,虚拟机栈保持不变,不会在线程的虚拟机栈中压入新的帧,而是简单地动态链接并直接调用指定的本地方法。如果某个虚拟机实现的本地方法接口使用的是C++链接模型,那么它的本地方法栈就是C++栈。
8,执行引擎
执行引擎主要负责执行字节码。方法的字节码是由java虚拟机的指令序列构成的,每一条指令都包含一个单字节的操作码,如果这个操作码有操作数,那么会接着取得它的操作数。然后执行这个操作,执行完成后会继续取得下一个操作码执行。
在操作方法时,JVM提供了四种指令来执行:
invokestatic,调用类的static方法
invokevirtual,调用对象实例的方法
invokeinterface,将属性定义为接口来进行调用
invokespecial,调用一个初始化方法,私有方法或者父类的方法
9,垃圾回收器
主要作用是回收程序中不再使用的内存。