1.什么是Java虚拟机?为什么Java被称作是“平台无关的编程语 言”?
Java虚拟机是一个可以执行Java字节码的虚拟机进程。Java源文件被编译成能被Java虚拟机执行的字节码文件。 Java被设计成允许应用程序可以运行在任意的平台,而不需要程序员为每一个平台单独重写或者是重新编译。Java虚拟机让这个变为可能,因为它知道底层硬件平台的指令长度和其他特性。
2.Java内存结构
方法区和堆是所有线程共享的内存区域;而java栈、本地方法栈和程序员计数器是运行时线程私有 的内存区域。
- Java堆(Heap),是Java虚拟机所管理的内存中最大的一块。Java堆是被所有线程共享的一块内 存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,几乎所有的对象实 例都在这里分配内存。
- 方法区(Method Area),方法区(Method Area)与Java堆一样,是各个线程共享的内存区 域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数 据。
- 程序计数器(Program Counter Register),程序计数器(Program Counter Register)是一块 较小的内存空间,它的作用可以看做是当前线程所执行的字节码的行号指示器。
- JVM栈(JVM Stacks),与程序计数器一样,Java虚拟机栈(Java Virtual Machine Stacks)也是 线程私有的,它的生命周期与线程相同。虚拟机栈描述的是Java方法执行的内存模型:每个方 法被执行的时候都会同时创建一个栈帧(Stack Frame)用于存储局部变量表、操作栈、动态 链接、方法出口等信息。每一个方法被调用直至执行完成的过程,就对应着一个栈帧在虚拟机 栈中从入栈到出栈的过程。
- 本地方法栈(Native Method Stacks),本地方法栈(Native Method Stacks)与虚拟机栈所发 挥的作用是非常相似的,其区别不过是虚拟机栈为虚拟机执行Java方法(也就是字节码)服 务,而本地方法栈则是为虚拟机使用到的Native方法服务。
3.JVM加载class文件的原理机制
JVM中类的装载是由类加载器(ClassLoader)和它的子类来实现的,Java中的类加载器是一个重要 的Java运行时系统组件,它负责在运行时查找和装入类文件中的类。 由于Java的跨平台性,经过编 译的Java源程序并不是一个可执行程序,而是一个或多个类文件。当Java程序需要使用某个类时, JVM会确保这个类已经被加载、连接(验证、准备和解析)和初始化。类的加载是指把类的.class文 件中的数据读入到内存中,通常是创建一个字节数组读入.class文件,然后产生与所加载类对应的 Class对象。加载完成后,Class对象还不完整,所以此时的类还不可用。当类被加载后就进入连接 阶段,这一阶段包括验证、准备(为静态变量分配内存并设置默认的初始值)和解析(将符号引用 替换为直接引用)三个步骤。最后JVM对类进行初始化,包括:1)如果类存在直接的父类并且这个 类还没有被初始化,那么就先初始化父类;2)如果类中存在初始化语句,就依次执行这些初始化语 句。 类的加载是由类加载器完成的,类加载器包括:根加载器(BootStrap)、扩展加载器 (Extension)、系统加载器(System)和用户自定义类加载器(java.lang.ClassLoader的子 类)。从Java 2(JDK 1.2)开始,类加载过程采取了父亲委托机制(PDM)。PDM更好的保证了 Java平台的安全性,在该机制中,JVM自带的Bootstrap是根加载器,其他的加载器都有且仅有一个 父类加载器。类的加载首先请求父类加载器加载,父类加载器无能为力时才由其子类加载器自行加 载。JVM不会向Java程序提供对Bootstrap的引用。下面是关于几个类加载器的说明:
- Bootstrap:一般用本地代码实现,负责加载JVM基础核心类库(rt.jar);
- Extension:从java.ext.dirs系统属性所指定的目录中加载类库,它的父加载器是Bootstrap;
- System:又叫应用类加载器,其父类是Extension。它是应用最广泛的类加载器。它从环境变 量classpath或者系统属性java.class.path所指定的目录中记载类,是用户自定义加载器的默认 父加载器。
4.Java对象创建过程
- JVM遇到一条新建对象的指令时首先去检查这个指令的参数是否能在常量池中定义到一个类的符 号引用。然后加载这个类(类加载过程在后边讲)
- 为对象分配内存。一种办法“指针碰撞”、一种办法“空闲列表”,最终常用的办法“本地线程缓冲分 配(TLAB)”
- 将除对象头外的对象内存空间初始化为0
- 对对象头进行必要设置
5.类的生命周期
类的生命周期包括这几个部分,加载、连接、初始化、使用和卸载,其中前三部是类的加载的过程, 如下图;
- 加载,查找并加载类的二进制数据,在Java堆中也创建一个java.lang.Class类的对象
- 连接,连接又包含三块内容:验证、准备、初始化。 1)验证,文件格式、元数据、字节码、 符号引用验证; 2)准备,为类的静态变量分配内存,并将其初始化为默认值; 3)解析,把 类中的符号引用转换为直接引用
- 初始化,为类的静态变量赋予正确的初始值
- 使用,new出对象程序中使用
- 卸载,执行垃圾回收
6.垃圾收集算法
GC最基础的算法有三种: 标记 -清除算法、复制算法、标记-压缩算法,我们常用的垃圾回收器一般 都采用分代收集算法。
- 标记 -清除算法,“标记-清除”(Mark-Sweep)算法,如它的名字一样,算法分为“标记”和“清 除”两个阶段:首先标记出所有需要回收的对象,在标记完成后统一回收掉所有被标记的对象。
- 复制算法,“复制”(Copying)的收集算法,它将可用内存按容量划分为大小相等的两块,每次 只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后 再把已使用过的内存空间一次清理掉。
- 标记-压缩算法,标记过程仍然与“标记-清除”算法一样,但后续步骤不是直接对可回收对象进行 清理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存
- 分代收集算法,“分代收集”(Generational Collection)算法,把Java堆分为新生代和老年代, 这样就可以根据各个年代的特点采用最适当的收集算法。
7.调优命令
Sun JDK监控和故障处理命令有jps jstat jmap jhat jstack jinfo
- jps,JVM Process Status Tool,显示指定系统内所有的HotSpot虚拟机进程。
- jstat,JVM statistics Monitoring是用于监视虚拟机运行时状态信息的命令,它可以显示出虚拟 机进程中的类装载、内存、垃圾收集、JIT编译等运行数据。
- jmap,JVM Memory Map命令用于生成heap dump文件
- jhat,JVM Heap Analysis Tool命令是与jmap搭配使用,用来分析jmap生成的dump,jhat内 置了一个微型的HTTP/HTML服务器,生成dump的分析结果后,可以在浏览器中查看 jstack,用于生成java虚拟机当前时刻的线程快照。
- jinfo,JVM Configuration info 这个命令作用是实时查看和调整虚拟机运行参数。
8.常见调优工具
常用调优工具分为两类,jdk自带监控工具:jconsole和jvisualvm,第三方有:MAT(Memory Analyzer Tool)、GChisto。
- jconsole,Java Monitoring and Management Console是从java5开始,在JDK中自带的java监 控和管理控制台,用于对JVM中内存,线程和类等的监控
- jvisualvm,jdk自带全能工具,可以分析内存快照、线程快照;监控内存变化、GC变化等。
- MAT,Memory Analyzer Tool,一个基于Eclipse的内存分析工具,是一个快速、功能丰富的 Java heap分析工具,它可以帮助我们查找内存泄漏和减少内存消耗
- GChisto,一款专业分析gc日志的工具
9.JVM性能调优参数
- 设定堆内存大小
-Xmx:堆内存最大限制。
- 设定新生代大小。 新生代不宜太小,否则会有大量对象涌入老年代
-XX:NewSize:新生代大小
-XX:NewRatio 新生代和老生代占比
-XX:SurvivorRatio:伊甸园空间和幸存者空间的占比
- 设定垃圾回收器
年轻代用 -XX:+UseParNewGC 年老代用-XX:+UseConcMarkSweepGC