赞
踩
一、JVM介绍:
JVM是Java Virtual Machine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。
Java虚拟机包括一套字节码指令集、一组寄存器、一个栈、一个垃圾回收堆和一个存储方法域。 JVM屏蔽了与具体操作系统平台相关的信息,使Java程序只需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行。JVM在执行字节码时,实际上最终还是把字节码解释成具体平台上的机器指令执行。
JVM是JRE的一部分。它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。JVM有自己完善的硬件架构,如处理器、堆栈、寄存器等,还具有相应的指令系统。Java语言最重要的特点就是跨平台运行。使用JVM就是为了支持与操作系统无关,实现跨平台。
二、JVM作用
与平台无关性:JVM屏蔽了与具体操作系统平台相关的信息,使得Java程序只需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行。
Java中的所有类,必须被装载到jvm中才能运行,这个装载工作是由jvm中的类装载器完成的,类装载器所做的工作实质是把类文件从硬盘读取到内存中JVM对中央处理器(CPU)所执行的一种软件操作,用于执行编译过的Java程序码(Applet与应用程序)。
JVM就是我们常说的java虚拟机,它是整个java实现跨平台的最核心的部分,所有的java程序会首先被编译为.class的类文件,这种类文件可以在虚拟机上执行。也就是说class并不直接与机器的操作系统相对应,而是经过虚拟机间接与操作系统交互,由虚拟机将程序解释给本地系统执行。当然只有JVM还不能成class的执行,因为在解释class的时候JVM需要调用解释所需要的类库lib,而jre包含lib类库。
三、JVM重要特征
1.内存管理机制
Java虚拟机内存模型包括程序计数器、虚拟机栈、本地方法栈、方法区、堆,如图所示
Java虚拟机运行时内存模型
(1)程序计数器
程序计数器是一块较小的内存空间,可以看作当前线程所执行的字节码行号指示器。需要注意以下几点内容:
1)程序计数器是线程私有,各线程之间互不影响
2)如果正在执行java方法,计数器记录的是正在执行的虚拟机字节码指令地址
3)如果执行native方法,这个计数器为null
4)程序计数器也是在Java虚拟机规范中唯一没有规定任何OutOfMemoryError异常情况的区域
(2)虚拟机栈
虚拟机栈即我们平时经常说的栈内存,也是线程私有,是Java方法执行时的内存模型,每个方法在执行时都会创建一个栈帧用于储存以下内容:
1)局部变量表:32位变量槽,存放了编译期可知的各种基本数据类型、对象引用、returnAddress类型。
2)操作数栈:基于栈的执行引擎,虚拟机把操作数栈作为它的工作区,大多数指令都要从这里弹出数据、执行运算,然后把结果压回操作数栈。
3)动态连接:每个栈帧都包含一个指向运行时常量池(方法区的一部分)中该栈帧所属方法的引用。持有这个引用是为了支持方法调用过程中的动态连接。Class文件的常量池中有大量的符号引用,字节码中的方法调用指令就以常量池中指向方法的符号引用为参数。这些符号引用一部分会在类加载阶段或第一次使用的时候转化为直接引用,这种转化称为静态解析。另一部分将在每一次的运行期间转化为直接应用,这部分称为动态连接。
4)方法出口:返回方法被调用的位置,恢复上层方法的局部变量和操作数栈,如果无返回值,则把它压入调用者的操作数栈。
(3)本地方法栈
本地方法栈是线程私有,与虚拟机栈类似,为native方法服务。
(4)方法区
线程共享,用于储存已被虚拟机加载的类信息、常量、静态变量,即编译器编译后的代码,方法区也称持久代(Permanent Generation),主要存放java类定义信息,与垃圾回收关系不大,但不是没有垃圾回收,这个区域的内存回收目标主要是针对常量池的回收和对类型的卸载。运行时常量池,方法区的一部分,虚拟机加载Class后把常量池中的数据放入运行时常量池。
(5)堆
堆是JVM中最大的一块区域,线程共享,此区唯一的目的就是存放对象实例,几乎所有对象实例都在这里分配
1)新生代:包括Eden区、From Survivor区、To Survivor区,系统默认大小Eden:Survivor=8:1
2)老年代:在年轻代中经历了N次垃圾回收后仍然存活的对象,就会被放到年老代中。因此,可以认为年老代中存放的都是一些生命周期较长的对象。
2、垃圾回收机制
说起GC,大部分人都会把这项技术当作Java语言的产物,其实GC的历史比Java久远。GC中不外乎两个步骤:1.确定哪些是垃圾,2.进行垃圾回收
(1)对象已死的判定
如何确定一个对象是否“死亡”?目前有两种方式:
1)引用计数算法:给对象添加一个计数器,每当有一个地方引用它时,计数器加1;当引用失效,计数器减1;计数器为0的对象就是不可能再被使用的。目前在微软的COM技术、Python语言都广泛使用该算法进行内存管理,但是至少主流的Java虚拟机没有选择该算法来管理内存对象,其中最主要原因是它无法解决对象之间的相互循环引用问题。
2)可达性分析算法:基本思想就是通过一系列的称为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,所走过的路径称为引用链,当一个对象到GC Roots没有任何引用链时,则证明此对象是不可用的。
(2)垃圾回收算法
1)标记-清除算法:首先标记出所有需要回收的对象,然后进行统一的回收,不足之处有两个:效率低、碎片多。
2)复制算法:将可用内存划分成大小相等的两块,每次只使用一块,当一块用完了,就将还存活的对象复制到另外一块上,然后把已使用的内存空间清理掉。不足之处是将内存缩小到一半,利用率不高。
3)标记-整理算法:与标记-清除类似,但后续步骤不是直接对可回收对象进行清理,而是让所有存活对象都向一端移动,然后直接清理掉端边界以外的区域
4)分代收集算法:分代收集是目前jvm普遍采用的算法,即新生代采用复制算法,因为有大量新生对象死去,只有少量存活;老年代采用标记-整理,因为老年代中对象存活率高,没有额外的空间对它进行担保。
(3)垃圾回收器
如果说垃圾回收算法是内存回收的方法论,那么垃圾收集器就是内存回收的实现。Java虚拟机规范中并没有对垃圾收集器应该如何实现作相应规定,因此不同厂商、不同版本差异很大。在JDK1.7以后开始采用G1。
3 类加载机制
(1)Class文件结构
商业和开源机构已经在Java语言之外发展出一大批在Java虚拟机上运行的语言,如Groovy、JRuby、Scala等。
实现语言无关性的基础仍然是虚拟机和字节码存储格式,Java虚拟机不和任何语言绑定,它只与Class文件的二进制文件格式相关联,理论上讲,任一门功能性语言都可以表示为一个能被Java虚拟机所接受的有效的Class文件。
Class文件结构包括以下内容:
1)魔数:确定这个文件能否被Java虚拟机接受,值为0xCAFFBABE(咖啡宝贝?)
2)版本号:Class文件的版本号
3)常量池:Class文件的资源仓库
4)访问标志:用于识别一些类或者接口层次的访问信息,是类还是接口?是否为public?
5)索引集合:包括类索引、父类索引、接口索引
6)字段表集合:描述接口或类中声明的变量,但不包括方法内部的局部变量
7)方法表集合:代码在方法表中的属性集合“Code”属性
8)属性表集合:字段表、方法表都可以携带自己的属性表,以用于描述某些场景专用信息
(2)类加载过程
加载
加载阶段虚拟机需要完成以下3件事:
验证
目的是为了确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。
准备
正式为类变量分配内存并设置类变量初始值的阶段,这些变量所使用的内存都将在方法区进行分配,注意是类变量(static修饰),不是实例变量。
解析
虚拟机将常量池内的符号引用替换为直接引用的过程,包括类或接口的解析、字段解析、类方法解析、接口方法解析。
初始化
初始化类和其他资源
(3)类加载器
类加载器用于实现类的加载动作,对于任意一个类,都需要由加载它的类加载器和这个类本省一同确立其在Java虚拟机中的唯一性,每个类加载器都有一个独立的类名称空间,比较两个类是否相等,只有在这两个类是同一个类加载器加载的前提下才有意义。例如Class对象的equals()、isInstance()。
从Java开发人员的角度看,类加载器可划分为3种:
1)启动类加载器:负责加载存放在<JAVA_HOME>\lib下面的类库
2)扩展类加载器:负责加载存放在<JAVA_HOME>\lib\ext下面的类库
3)应用程序类加载器:负责加载用户路径上的类库
双亲委派模型:
双亲委派模型的工作过程是:如果一个类加载器收到了类加载请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去加载,每一层次的类加载器都是如此,因此所有的加载请求最终都应传送到顶层的启动类加载器中,只有当父类加载器反馈自己无法完成这个加载请求时,子加载器才会尝试自己去加载。
4 性能监控调优
jvm启动参数
参数名称 | 说明 |
-Xms | 初始堆大小,物理内存的1/64(<1GB) |
-Xmx | 最大堆大小,物理内存的1/4(<1GB) |
-Xmn | 年轻代大小,此处的大小是(eden+ 2 survivor space) |
-XX:PermSize | 设置持久代初始值,物理内存的1/64 |
-XX:MaxPermSize | 设置持久代最大值 物理内存的1/4 |
-Xss | 每个线程的堆栈大小,JDK1.5以后为1M |
-XX:NewRatio | 年轻代(包括Eden和两个Survivor区)与年老代的比值 |
性能优化四个命令:
1)jps:查看java进程
2)jstat:显示本地或者远程虚拟机垃圾回收,例如:jstat -gcutil $pid 1000 5
3)jmap:查看JVM堆中对象详细占用情况,例如:jmap -histo [pid]
4)jstack:用于生成虚拟机当前线程快照,jstack -l [pid]
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。