Java程序运行在JVM上,可以吧JVM理解成Java程序和操作系统之间的桥梁,JVM实现了Java的平台无关性,由此可见JVM的重要性。 一个完整的Java程序运行过程会涉及到以下内存区域: (1)寄存器:JVM内部虚拟寄存器,存取速度非常快,程序不可控制。 (2)栈:保存局部变量的值,包括:a.用来保存基本数据类型的值;b.保存类的实例,即堆区对象的引用(指针)。也可以用来保存加载方法时的帧。 (3)堆:用来存放动态产生的数据,比如new出来的对象。注意创建出来的对象只包含属于各自的成员变量,并不包括成员方法。因为同一个类的对象拥有各自的成员变量,存储在各自的堆中,但是他们共享该类的方法,并不是每创建一个对象就把成员方法复制一次。 (4)常量池:JVM为每个已加载的类型维护一个常量池,常量池就是这个类型用到的常量的一个有序集合。包括直接常量(基本类型,String)和对其他类型,方法,字段的符号引用。池中的数据和数组一样通过索引访问。由于常量池包含了一个类型所有的对其他类型,方法,字段的符号引用,所以常量池在Java动态链接中起了核心作用。常量池存在于堆中。 (5)代码段:用来存放从硬盘上读取的源程序代码。 (6)数据段:用来存放static定义的静态成员。 下图表示内存分配图: 代码段: JVM体系总体分四大块: (1)类的加载机制 (2)JVM内存结构 (3)GC算法 垃圾回收 (4)GC分析 命令调优 一、类的加载机制 1、什么是类的加载? 类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang.Class对象,用来封装类在方法区内的数据结构。类的加载的最终产品是位于堆区中的Class对象,Class对象封装了类在方法区内的数据结构,并且向Java程序员提供了访问方法区内的数据结构的接口。 2、类的生命周期 类的生命周期包括这几个部分:加载、连接、初始化、使用和卸载,其中前三部是类的加载的过程,如下图: (1)加载 将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区中的运行时数据结构,在堆中生成一个代表这个类的java.lang.Class对象,作为方法区类数据的访问入口,这个过程需要类加载器参与。 (2)将java类的二进制代码合并到JVM的运行状态之中的过程 验证:确保加载的类信息符合JVM规范,没有安全方面的问题 准备:正式为类变量(static变量)分配内存并设置类变量初始值得阶段,这些内存都将在方法中进行分配 解析:虚拟机常量池的符号引用替换为字节引用过程 (3)初始化:为类的静态变量赋予正确的初始值 (4)使用:new出对象程序中使用 (5)卸载:执行垃圾回收 3、类加载机制 (1)全盘负责:当一个类加载负责加载某个Class时,该Class所依赖和引用的其他Class也将由该类加载器负责载入,除非显示使用另外一个类加载器来载入 (2)父类委托:先让父类加载器试图加载该类,只有在父类加载器无法加载该类时才常识从自己的类路径中加载该类 (3)缓存机制:缓存机制将会保证所有加载过的Class都会被缓存,当程序中需要使用某个Class时,类加载器先从缓存区寻找该Class,只有缓存区不存在,系统才会读取该类对应的二进制数据,并将其转换成Class对象,存入缓存区。这就是为什么修改了Class后必须重启JVM,程序的修改才会有效。 二、JVM内存结构 方法区和堆是所有线程共享的内存区域;而java栈,本地方法栈和程序员计数器是运行线程私有的内存区域。 (1)Java堆(Heap):(存放所有new出来的东西)是Java虚拟机所管理的内存中最大的一块。Java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。 (2)方法区(Method Area),方法区与Java堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息,常量,静态变量,即时编译器编译后的代码等数据。 (3)JVM栈(JVM Stacks),与程序计数器一样,Java虚拟机栈也是线程私有的,它的生命周期与线程相同。虚拟机栈描述的是Java方法执行的内存模型:每个方法被执行的时候都会同时创建一个栈帧,用于存储局部变量表,操作栈,动态链接,方法出口等信息。每一个方法被调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。 (4)程序计数器(Program Counter Register),程序计数器是一块较小的内存空间,它的作用可以看做是当前线程所执行的字节码的行号指示器。 (5)本地方法栈(Native Method Stacks),本地方法栈与虚拟机栈所发挥的作用是非常相似的,其区别不过是虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,而本地方法栈则是为虚拟机使用到的Native方法服务。