jvm结构

优化是一个体系,不是一个点,特别是JVM的优化比SQL的优化还要复杂,它的颗粒度更细,比如你对垃圾回收器的

选择,比如对存储结构区域的划分.而且不同的情况所优化的方式不一样,包括像我们数据库优化,数据库优化可以

更换引擎,根据对表操作的不同,如果表查询特别多,在设计表的时候这个表就是用来做查询的,换成MYISAM的引擎

查询效率要比InnoDB要高的.

其实虚拟机是分为两个结构的,一个是client结构,一个是server结构,这是我们从来都不知道的.

1. JVM结构

2. JVM垃圾回收算法及收集器

3. JVM优化

4. Eclipse运行调优

前面两个是JVM的原理,第三个是JVM调优的工具,第四个是一个小实战.

还记得我们装完JDK以后配置环境变量,我们会在命令行窗口执行 java 或者 java -version

这个步骤就犹如我们打开电脑连接网络以后想判断它是否连接成功,打开浏览器输入www.baidu.com一样

jvm结构

以往我们只关注下面,只要不报既不是内部也不是外部命令可执行文件,或者看到这样的结果就完事了,立马把

窗口关掉,但是我们有没有认真的观察这三行代码里面到底涵盖了什么,我们还是要从这里分析.

它会给你输出三行结果:

第一行: java version 1.8.0_151 说明你当前JDK的版本是1.8的 这副版本是151

然后再往下 Java<TM> SE Runtime Environment <build 1.8.0_151> 这是显示开发环境的一些信息.

Java HotSpot<TM> 64-Bit Server VM <build 25.151-b12, mixed mode> 这里我会看到两个比较关键的

词语,一个是HotSpot,一个是Server, HotSpot是JVM的一个核心组件,或者叫JVM的名称,JDK每次版本再更新的

时候,都会侧重的去优化一个问题,进一步的去优化JVM的运行效率,运行效率指的是什么呢?对class文件的解析

或者是编译,在JDK1.3之前的版本,这是在JDK1.3以后理论提出来的,JDK1.3之前的版本的核心不是HotSpot,那

它会采取一个什么样的策略来读取字节码文件呢,当你运行一个class的时候,classloader将类加载到内存虚拟机

当中,但它加载进来的是class文件,class文件是一个中间层文件,它并不是一个CPU能够直接解释的这样一个文件

虚拟机需要把class文件编译成本地代码,本地代码就是CPU能够直接处理的文件,所以每次我们加载进来的class,

JVM都会做一个本地代码的转换,每次来都转换,转换的越多,就表示消耗的性能就越大,那么在JDK1.3这个版本

之后,其实SUN公司已经发现了这个问题,后来就引入HotSpot核心技术的VM,它也是一个虚拟机,但是HotSpot的VM

并不是SUN公司去写的,而是SUN公司收购的另一家公司的,因为在这个VM里面,它有很多比较先进的理念,热点

探测, HotSpot就是一个热点探测,你每次虚拟机要加载class文件,我会对你加载的class文件做标记,标记的

过程当中如果达到一定的阀值,它会对频繁使用的class文件直接编译成本地代码缓存起来了,不再让你频繁的

编译了,有点像缓存,跟它的原理上差不多,也就是说它会把它的结果集放在缓存里,以便你下次再来的时候

不需要再查数据库了,它也是,会把本地代码会缓存起来,下次再来的时候可以去掉编译的环节,HotSpot的特点

当然还有其他的特点,那些可能对于我们来说就不太关注,HotSpot在JDK1.3的时候就准备要往里放了,任何

一个技术在商业化之前它都要经过严格的测试,所以在JDK1.3并没有把HotSpot放在里面,而是在JDK1.5的那个

版本才真正的把HotSpot的技术嵌入到或者说彻底商业化了.

从JDK1.5以后的都是HotSpot的核心, Client是客户端, JVM虚拟机在启动时它给我们准备了两个版本,一个是

client,一个是server,但是其实他们不是两套虚拟机,是一个虚拟机,是采用了两套不同的机制来初始化,

client更多的是做桌面型的应用而提供的一个虚拟机,也就是说应用于桌面级应用的虚拟机,对内存空间分配

做的优化,我们都知道JAVA语言都可以写C/S结构的东西,client的最大特点是什么呢?桌面型的操作系统, JVM

处理的代码量并不会很大,一个桌面系统打开以后,你的按钮一次只能触发一个,你能同时触发两个按钮吗?

程序和JVM交互的频率相对于B/S结构来讲,它的频率会低很多,双十一瞬间会有好大的请求量,我们的代码要嵌入

虚拟机里面运行,虚拟机要处理的线程数是不是要多一些,但是桌面级操作系统,一次只能点一个按钮,相比于B/S

结构,让JVM去处理的线程数,肯定要少很多,所以为了更合理的使用内存,在client版本当中,它把内存分配的

空间,相比server端要少了,比如说,它把堆里的内存就分了10几M,或者100多M,他觉得十几兆一百多兆足够用

了,在十几兆和一百多兆的区域内,我所耗的时间就是0.00几秒,完全够用,但是如果是server端,那分配的

空间就可能更大,那么JVM启用的都是客户端,那我们能不能把它改成server端,可以的,怎么改呢?

jvm结构

-client和-server谁在上就以谁启动,由上至下来看,我们也可以手动将它的位置来做一些改变,

它修改需要管理员的权限

jvm结构

jvm结构

虚拟机默认的,不管是client端还是server端,默认的已经可以很好的适应了常见的JAVA的运行了.

除非你的项目的体系比较强大,你才需要对它进行进一步的优化,把client改成server版本,server端这么好,

为什么不默认的使用server端,因为server版本的比client版本申请的空间要大,比较浪费空间,所以在当时那个

年代内存比较紧张的时候,不是占内存越大越好,所以给你一个选择性,现在这两个问题已经解决了.

这是JVM的一个开始,接下来我们来看JVM的结构:

我们要向理解虚拟机,首先还是得理解结构.

JVM结构第一部分JVM的总体结构:

jvm结构

这个其实划分的颗粒度也不是很细,但是这个颗粒度已经足够我们认识了,JVM在运行过程当中,会分配这么几个

区域,第一个类加载子系统,第二个方法区,第三个JAVA堆,第四块直接内存,第五块垃圾回收器,第六块执行引擎,

这每一块都代表什么意思呢?

1. 类加载子系统和方法区: 类加载子系统负责从文件或者网络中加载class文件,也就是说classloader也就是

一个组件,其实不是加载进来直接就运行了,它会经过好多模块的处理,文件的校验,还有HotSpot的处理等等

加载的信息存放于一块称为方法区的一块内存空间,也就是说classloader通过IO把我们的class文件里面的字节

加载到虚拟机当中的时候它要保存起来,它不能加载完就扔掉了,那class文件的字节会保存到哪呢?会放在一个

叫方法区的空间里面. 除了类的信息以外,方法区中可能还会存放运行时的常量池信息,常量池就是存放我们的

常量,都是唯一一份放着,包括字符串字面量和数字常量,这部分常量信息是class文件中常量池部分的内存映射.

所以说这两块的能力通过类加载系统将我们class文件加载进来放到方法区里,放在方法区的都是唯一的东西.


2. java堆: 这个java堆就是我们在讲SE的时候的堆栈. java堆在虚拟机启动时就建立了,它是java程序主要的

内存工作区域,几乎所有的java对象实例都存放在java堆中,堆空间是所有线程共享的,这是一块与java应用密切

相关的内存空间. 这里是我们要了解的重点,我们都是以他作为主讲解,我们所有创建的对象,都会在堆里出现,

那么所指的堆就是这个堆.


3. 直接内存: java的NIO库允许java程序使用直接内存,直接内存是java堆外的,直接向系统申请内存空间,

通常访问直接内存的速度优于java堆. 因此出于性能的考虑,读写频繁的场合可能会考虑使用直接内存,由于

直接内存在java堆外,因此它的大小不会直接受限于Xmx指定的最大堆大小,但是系统内存是有限的,java堆和

直接内存的的总和依赖依然受限于操作系统能给出的最大内存. 其实直接内存并不隶属于java堆的范畴内,

也就是说它并不是java堆所包含的, java堆的大小是由JVM来控制的,它说多大就多大,而直接内存是直接映射

到系统内存上的,它理论上的空间是无限的,不受JVM堆的控制,但是实际上也不可能是无限的,它的大小取决于你

物理内存的大小,你说你物理内存是1G的,剩余的可以给他分配多少多少,但是它的空间大小要比这大一堆的.

这就是直接内存,NIO库下的一个东西,提升效率的.


4. 垃圾回收系统: 这个也是一个重点内存,是java虚拟机的重要组成部分,垃圾回收器可以对方法区,java堆和

直接内存进行回收,也就是说垃圾回收系统并不局限于回收java堆,对于方法区和直接内存都是可以回收的,

其中java堆是垃圾回收器的重点,和C/C++不同,java中所有的对象空间释放都是隐式的,也就是对于程序员来讲

对于内存的开辟和释放完全是不透明的,他不像C语言或者C++,也就是说,java中没有类似free()或者delete()

这样的函数释放指定的内存区域,对于不再使用的垃圾对象,垃圾回收系统会在后台默默的工作,默默的查找,

标记并释放对象,完成包括 java堆、方法区和直接内存中的全自动管理. 我们要了解垃圾回收,以便与以后

我们真的要做优化,跟别人学习优化的时候,你知道优化的点,其中垃圾回收就是一个优化的点.


5. java栈: 每一个java虚拟机线程都有一个私有的java栈,一个线程的java栈在线程创建的时候就创建了,

java栈中保存着帧信息,其实所谓的java栈,比如我们讲递归的时候,要想把递归讲的透彻一些,我们肯定要画

栈图,因为方法都是在栈运行的,或者说是跟栈有关的,这个时候我机会发现,方法自己调用自己的时候,这个方法

压栈压栈,但是当一个新的方法压栈的时候,由于原来的方法还没有运行完,有些数据需要保存,这个保存点就叫

栈帧,java栈中保存着局部变量,方法参数,也就是说所有的局部变量和参数也叫做栈变量,原因就在这里,

它是独立的,栈和栈中间是完全隔离的,同时和java方法的调用和返回密切相关.


6. 本部方法栈: 本地方法栈和java栈非常类似,也是一个栈空间,最大的不同在于java栈用于方法的调用,

而本地方法栈则用于本地方法的调用,本地方法就是native method, 也就是说虚拟机在运行一些内容的时候,

其实脱离不了操作系统的内容,也就是要调用操作系统的API,这个API在JVM里就叫做本地方法,为什么不同的

操作系统提供了不同版本的JVM,就是因为在这一块的位置上不通用,因为不同的操作系统的结构或者方法是不一

样的,所以为了让虚拟机能够适应各个操作系统,那么就必须写多个虚拟机调用多个操作系统里的不同的方法

来完成它想要做的事,如果所有的操作系统本地方法都相同,那虚拟机只要有一份就可以了,java虚拟机允许java

直接调用本地方法(通常使用C编写)


7. PC寄存器(Program Counter): PC寄存器是每一个线程私有的空间,java虚拟机会为每一个java线程

创建PC寄存器,在任意时刻, 一个java线程总是在执行一个方法, 这个正在执行的方法称为当前方法,

如果当前方法不是本地方法, PC寄存器就会指向当前正在被执行的指令,如果当前方法是本地方法,那么PC寄存

器的值就是undefined, PC寄存器是区别线程里执行的方法是本地方法还是java自己的方法的作用.


8. 执行引擎: 执行引擎是java虚拟机的最核心组件之一,它负责执行虚拟机的字节码,现在虚拟机为了提升效率

会使用即时编译(just in time)技术将方法编译成机器码后再执行.