当前位置:   article > 正文

JAVA虚拟机(JVM)底层原理_jvm虚拟机原理

jvm虚拟机原理

目录

1.0.什么是JVM

2.0 JVM的组成

2.1类加载器子系统

 2.2运行时数据区

2.3.程序计数器

2.4.Java虚拟机栈

2.5.本地方法栈

2.6.方法区

2.7.堆内存

2.8元空间

2.9.执行引擎

3.0 JVM内存溢出

3.1.1堆内存溢出

3.2.2 虚拟机栈/本地方法栈溢出

3.3. 方法区溢出-元空间

  3.4.本机直接内存溢出

4.0 JVM垃圾回收 

4.1 判断对象是否已死

4.2 常用垃圾回收算法

4.2.常见垃圾收集器

5.0 JVM的优化

5.1JIT编译器优化

5.2JVM内存分区优化

5.3.jvisualvm分析内存快照

5.4.内存溢出问题定位

5.5常用JVM参数参考

5.6.设置jvm参数的几种方式



1.0.什么是JVM

与JVM的初次见面,是在我们Java开始的时候就已经相见了.时隔多日,我们先来回顾一下.

Java的广告语是,”编写一次,到处运行,而它凭借的就是JVM(Java Virtual Machine).而对于不同的平台,Windows,Linux,Mac OS等,有具体不同的JVM版本.这些JVM屏蔽了平台的不同,提供了统一的运行环境,让Java代码无需考虑平台的差异,运行在不同的环境中.

而至于JRE和JDK,就不再赘述了,包含关系应该很清楚的,而今天我们的重点就在于对JVM的进一步认识以及对它进行优化调整.

1.1为什么要优化JVM

正如前面我们所回顾的,我们的Java代码都是运行在JVM中的,而部署的硬件及应用场景有所不同时,仍然采用默认的配置不见得能起到最好的效果,甚至可能会导致运行效率更差,又或者面临高并发情况下,想让程序平稳顺畅的运行,所以我们需要针对实际的需要来进行优化.

所谓优化就是配置一些参数,让jvm运行时使用这些参数,让jvm运行的程序更优。

1.2分析工具jvisualvm

我们只知道有JVM的存在,但它的运行对于我们来说感觉像是摸不着看不见的,所以我们需要借助工具来监控它的一个实时状态,就像Windows的性能监视器一样,JDK也有自己的可视化工具.Java提供了2个监视工具:

  1. D:\opensource\jdk1.8\bin\jconsole.exe
  2. D:\opensource\jdk1.8\bin\jvisualvm.exe

我们以管理员身份运行DOS ,输入jvisualvm,将Java VisualVM启动,输入jvisualvm,将Java VisualVM启动

 

 在这里我们可以看到

本地列表中有多个条目,而一眼也可以看到我们SpringBoot项目的main方法,直接双击,经过短时间的加载后,得到这样一个界面

这个是概述页面,可以得到很多信息,但对于我们分析JVM的运行还是没有什么帮助,所以我们切换到监视页

 监视页展示的就是实时的JVM信息

应该还是很直观的 , 现在安装插件,插件的安装属于VisualVM的一个重要功能,凭借插件我们可以将这个工具的功能变得更强大。

打开工具 -> 插件 -> 选择“可用插件”页 : 我们在这里安装一个Visual GC,方便我们看到内存回收以及各个分代的情况 . 打上勾之后点击安装,就是常规的next以及同意协议等 ,网络不是很稳定,有时候可能需要多尝试几次。可以在设置中修改插件中心地址

根据如下步骤修改地址:找到插件中心

VisualVM: Plugins Centers

 

找到对应的JDK版本:

Update Center documentation

复制插件地址:

安装插件:

然后再 可用插件中 找到 Visual GC

安装完成后我们将当前监控页关掉,再次打开,就可以看到Profiler后面多了一个Visual GC页。

 在这里我们可以看到JIT活动时间,类加载活动时间,GC活动时间以及各个分代的情况。

需要注意的是,当前课件使用的JDK版本为1.8,仍然自带了VisualVM,从1.9开始的版本是没有自带的,需要额外下载,下载的github地址:

VisualVM: Download

另外,如果开发工具使用的是Intellij IDEA的话,可以下载一个插件,VisualVM Launcher,通过插件启动可以直接到上述页面,不用在左边的条目中寻找自己的项目.

当然也有其他的工具,但这个在可预见的未来都会是主力发展的多合一故障处理工具.所以我们后面将会使用这个工具来分析我们的JVM运行情况,进而优化.而需要优化我们还需要对JVM的组成有进一步的了解.接下来我们来看一下JVM的组成

2.0 JVM的组成

从图上可以看到,大致分为以下组件:

  • 类加载器子系统
  • 运行时数据区 : 方法区 堆  虚拟机栈 本地方法栈 程序计数器
  • 执行引擎
  • 本地方法库

而本地库接口也就是用于调用本地方法的接口,在此我们不细说,主要关注的是上述的4个组件

2.1类加载器子系统

顾名思义,这是用于类加载的一个子系统.

2.1.1类加载的过程

类加载的过程包括了加载,验证,准备,解析和初始化这5个步骤

  • 加载:找到字节码文件,读取到内存中.类的加载方式分为隐式加载和显示加载两种隐式加载指的是程序在使用new关键词创建对象时,会隐式的调用类的加载器把对应的类加载到jvm中。显示加载指的是通过直接调用class.forName()方法来把所需的类加载到jvm中。
  • 验证:验证此字节码文件是不是真的是一个字节码文件,毕竟后缀名可以随便改,而内在的身份标识是不会变的.在确认是一个字节码文件后,还会检查一系列的是否可运行验证,元数据验证,字节码验证,符号引用验证等.Java虚拟机规范对此要求很严格,在Java 7的规范中,已经有130页的描述验证过程的内容.
  • 准备:为类中static修饰的变量分配内存空间并设置其初始值为0或null.可能会有人感觉奇怪,在类中定义一个static修饰的int,并赋值了123,为什么这里还是赋值0.因为这个int的123是在初始化阶段的时候才赋值的,这里只是先把内存分配好.但如果你的static修饰还加上了final,那么就会在准备阶段就会赋值.
  • 解析:解析阶段会将java代码中的符号引用替换为直接引用.比如引用的是一个类,我们在代码中只有全限定名来标识它,在这个阶段会找到这个类加载到内存中的地址.
  • 初始化:如刚才准备阶段所说的,这个阶段就是对变量的赋值的阶段.

如上过程都是在JVM执行的过程中自己完成的,我们无需干涉。

2.2.2类与类加载器

每一个类,都需要和它的类加载器一起确定其在JVM中的唯一性.换句话来说,不同类加载器加载的同一个字节码文件,得到的类都不相等.我们可以通过默认加载器去加载一个类,然后new一个对象,再通过自己定义的一个类加载器,去加载同一个字节码文件,拿前面得到的对象去instanceof,会得到的结果是false.

2.2.3双亲委派机制

JVM中内置的类加载器:

 类加载时使用了双亲委派模式:

载规则,优先使用爷爷加载,如果没有加载到再使用它爹加载,如果他爹也没有加载到,才到自己加载,如果自己也没有加载到才报ClassNotFountException。在这过程中只要上一级加载到了,下一级就不会

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Gausst松鼠会/article/detail/352939
推荐阅读
相关标签
  

闽ICP备14008679号