1、JVM简析:
java不单单是一门编程语言,它是一门技术,由四方面组成:Java编程语言、Java类文件格式、Java虚拟机和Java应用程序接口(Java API)。JVM(Java虚拟机)是一种用于计算设备的规范,他是一个虚拟出来的计算机,是通过在实际的计算机上仿真模拟各种计算功能来实现的。
Java语言的一个非常重要的特点就是与平台的无关性。而是使用Java虚拟机来实现这一特点的关键。一般的高级语言如果在不同的平台上运行,至少需要编译成不同的目标代码,而引入Java虚拟机后,Java语言子啊不同平台上运行时就不需要重新编译。Java语言使用Java虚拟机屏蔽了与具体平台相关的信息,是的Java语言编译程序只要生成Java虚拟机上运行的目标码(字节码),就可以在多种平台上不加修改的运行。Java虚拟机在执行字节码时,把字节码编译成具体平台上的机械指令执行,这就是Java能够“一次编译,执行到底”的原因。
Java平台的逻辑结构上来看,我们可以重下图了解JVM:
从上图能过清晰的看到Java平台包含各个逻辑模块,也能了解JDK是Java的开发工具,它不仅提供了Java程序运行所需的JRE,还提供了一系列的编译,运行等工具,如javac,java,javaw等。JRE只是Java程序的运行环境,它最核心的内容就是JVM(Java虚拟机)及核心类库。
对于JVM自身的物理结构,如下图:
二、JAVA代码编译和执行过程
Java代码编译是由Java源码编译器来完成,流程图如下所示:
Java字节码的执行是由JVM执行引擎来完成,流程图如下所示:
Java代码编译和执行的整过过程包含一下三个重要机制:
1、Java源码编译机制
Java源码编译机制由一下三个过程组成:
(1)分析和输入到符号表
(2)注解处理
(3)语义分析和生成class文件
流程图如下所示:
最后生成的class文件由一下部分组成:
(1)结构信息。包含class文件格式版本号及各部分的数量与大小信息
(2)元数据。对应与Java源码中声明与常量信息。包含类/继承的超类/实现的接口的声明信息、域域方法的声明信息和常量池。
(3)方法信息。对应Java源码中语句和表达式对应的信息。包含字节码、异常处理器表、求值栈与局部变量区大小、求值栈的类型记录、调试符号信息
2、类加载机制
JVM的类加载时通过ClassLoader及子类来完成的,类的层次关系和加载顺序可以由下图描述:
1)Bootstrap ClassLoader
负责加载¥JAVA_HOME中jre/lib/rt.jar里的所有的class,由C++实现,不是ClassLoader子类
2)Extension ClassLoader
负责加载java平台中扩张功能的一些jar包,包括¥JAVA_HOME中的jre/lib/*.jar或-Djava.ext.dirs指定目录下的jar包
3)App ClassLoader
负责记载classpath中指定的jar包及目录中class
4)Custom ClassLoader
属于应用程序根据自身需要自定义的ClassLoader,如tomcat、jboss都会根据j2ee规范自行实现ClassLoader加载过程中会先检查类是否被已加载,检查顺序是自底向上,从Custom ClassLoader到BootStrap ClassLoader逐层检查,只要某个classloader已加载就视为已加载此类,保证此类只所有ClassLoader加载一次。而加载的顺序是自顶向下,也就是由上层来逐层尝试加载此类。
3、类执行机制
JVM是基于栈的体系结构来执行class字节码。线程创建后,都产生程序计数器(PC)和栈(Stack)。程序计数器存放吓一跳要执行的指令在方法的偏移量,栈中存放一个个栈帧,每个栈帧对应着每个方法的每次调用,而栈帧有事局部变量区和操作数帧两部分组成,局部变量用于存放方法中的局部变量和参数,操作数帧中用于存放方法执行过程中产生的中间结果。栈的结构图如下:
三、JVM内存管理
JVM内存组成结构
JVM栈由堆、栈、本地方法栈、方法区等部分组成,结构图如下
1)堆
所有通过new常见的对象的内存都分配在堆中,堆的大小可以通过-Xmx和-Xms来控制。堆被划分为新生代和旧生代,新生代又被进一步分为Eden和Survivo区,最后Survivo由From Space和To Space组成,结构如下所示:
a)新生代 新建对象都用新生代分配内存,Eden空间不足的时候,会把存活的对象转移到Survivor中,新生代大小可以由-Xmn来控制,也可以用-XX:SurvivorRatio来控制Eden和Survivor的比例
b)旧生代。用与存放新生代中经过多次垃圾回收仍然存活的对象
c)持久代(Perment Space)实现方法区,主要存放已有加载的类信息,方法信息,常量池等等。可以用过-XX:PermSize和-XX:MaxPermSize来指定持久带初始化值和最大值。Permanent Space并不等于方法去,只不过Hotspot JVM用Permanent Space来实现方法区而已,有些虚拟机没有Permanent Space而用其他机制来实现方法区。
-Xmx:最大堆内存,如:-Xmx512m
-Xms:初始时堆内存,如:-Xms256m
-XX:MaxNewSize:最大年轻区内存
-XX:NewSize:初始时年轻区内存.通常为 Xmx 的 1/3 或 1/4。新生代 = Eden + 2 个 Survivor 空间。实际可用空间为 = Eden + 1 个 Survivor,即 90%
-XX:MaxPermSize:最大持久带内存
-XX:PermSize:初始时持久带内存
-XX:+PrintGCDetails。打印 GC 信息
-XX:NewRatio 新生代与老年代的比例,如 –XX:NewRatio=2,则新生代占整个堆空间的1/3,老年代占2/3
-XX:SurvivorRatio 新生代中 Eden 与 Survivor 的比值。默认值为 8。即 Eden 占新生代空间的 8/10,另外两个 Survivor 各占 1/10
2)栈
每个线程执行每个方法的时候会在栈中申请一个栈帧,每个栈帧包括局部变量区和操作数栈,用于存放此次方法调用过程中的零食变量、参数和中间结果
-xss:设置每个线程的堆栈大小.JDK1.5+ 每个线程堆栈大小问哦1M,一般来说如果栈不是很深的话1M是绝对够用了的。
3)本地方法栈
用于支持native方法的执行,存储了每个native方法调用的状态
4)方法区
存放了要加载类信息、静态变量、final类型的常量、属性和方法信息。JVM用吃就带来存放方法区,可通过-XX:PermSize和-XX:MaxPermSize来指定最小值和最大值
垃圾回收按照基本回收策略分
引用计数(Reference Counting):
引用计数器的实现很简单,对于一个对象A,只要有任何一个对象引用了A,则A的引用计数器就加1,当引用失效时,引用计数器就减1。只要对象A的引用计数器的值为0,则对象A就不可能再被使用。此算法最为致命的时无法处理循环引用的问题。(Java中不使用)
四、GC算法与种类
标记-清除
标记-清除算法时现代垃圾税收算法的思想基础。标记-清楚 算法讲垃圾回收分为两个阶段:标记阶段和清楚阶段。一种可行的实现是,在标记阶段,首先通过跟阶段,标记所有从根阶段开始的可达对象。因此,未被标记的对象就是被引用的对象垃圾。然后通过清楚阶段,清楚所有未被标记的对象
标记-压缩
标记压缩算法适合用于存活对象较多的长河,如老年代。他在标记-清除算法的基础上做了一些优化。和标记-清除算法一样,标记-压缩算法首先也需要从根阶段开始,堆所有可达对象做一次标记。但之后,它并不简单的清理未标记的对象,而是将所有的存活对象压缩到内存的一段,之后,清除边界外所有的空间。
复制算法
与标记-清除算法相比,复制算法是一种相对高效的回收方法
不适用于存活对象较多的场合,如老年代。空间浪费
将原有的内存空间分为两块,每次只使用其中一块,在垃圾回收的时候讲正在使用的内存中的存活对象复制到未使用的内存块中,之后清除正在使用的内存块中的所有对象吗交换两个内存的角色。完成垃圾回收
依据对象的存活周期来分类,短命对象归为新生代,长命对象归为老年代。
根据不同代的特点,选取合适的收集算法
少量对象存活,使用复制算法。如新生代
大量对象存活,合适标记-清除或标记-压缩。如老年代
五、GC参数
1)-XX:+UseSerialGC 串行收集器
特点:最古老,最稳定,效率高,但是可能会产生较长的停顿,新生代、老年代使用串行回收,新生代复制算法、老年代标记-压缩
2)ParNew 并行收集器
-XX:+UseParNewGC ,新生代并行,老年代串行
Serial收集器新生代并行版本
复制算法
多线程,需要多核支持
-XX:ParallelGCThreads 限制线程数量
3)Paralledl收集器
类似ParNew,新生代复制算法,老年代标记-压缩
-XX:+UseParalledGC 使用Parallel收集器+老年代串行
-XX:+UseParallelOldsGC 使用Parallel收集器+老年代并行
-XX:MaxGCPauseMills 最大停顿时间,单位毫秒 ,GC尽量保证回收时间不超过设定值
-XX:GCTimeRatio 0-100的取值范围,默认99,(总时间-垃圾收集事件)占总时间比
4)CMS收集器 -XX:+UseConcMarkSweepGC
CMS运行过程比较复杂,着重实现了标记的过程,可分为
初始标记:根看可以之间关联到对象,速度快
并发标记(和用户线程一起):主要标记过程,标记全部对象
重新标记:由于并发时,用户线程依然运行,因此在正式清理之前,再做修正
并发清除(和用户线程一起):基于标记结果,直接清理对象
特点:尽可能降低停顿,但是会影响性能的整体吞吐量和性能,比如在用户线程运行过程中,分一半CPU去做GC,系统性能在GC阶段,反应速度就下降一般,而且清除不彻底,因为在清理阶段,用户线程还在运行,会产生新的垃圾,无法清理。应为和用户线程一起运行,不能在空间快满时再清理。
-XXCMSInitiatingOccupancyFraction设置出发GC的阀值,如果不幸内存预留空间不够,就会引起concurrent mode failure
-XX:+ UseCMSCompactAtFullCollection Full GC后,进行一次整理整理过程是独占的,会引起停顿时间变长
-XX:+CMSFullGCsBeforeCompaction 设置进行几次Full GC后,进行一次碎片整理
-XX:ParallelCMSThreads 设定CMS的线程数量