以前去面试的时候面试官问了我关于关于JVM性能调优的问题,因为本身以前公司的项目里本身没有接触到JVM性能调优的相关问题(感受这些都是公司架构师考虑的问题),全部面试官问的时候本身一脸懵逼,全部最后的结果固然是凉凉。。,因而,为了查漏补缺,就去学习了一下JVM的相关知识,但愿能帮助到你们。java
在学习任何一项新的知识以前,我都会先列出一份学习大纲,而后按照这个学习大纲一步一步的来学习了解,因此学习JVM这个新的技术,我也分为了3个板块来学习:JVM类加载器,JVM内存结构,JVM垃圾回收这三个板块来学习,今天这篇文章讲的是JVM类加载器。程序员
既然是学习关于JVM的相关理论知识,咱们固然得知道什么是JVM。JVM是Java Virtual Machine(Java虚拟机)的缩写。既然说到虚拟机,可能又会有人问什么是虚拟机了,我这里把虚拟机得相关概念放在这里:面试
虚拟机:就是一台虚拟的计算机,他是一款软件;用来执行一系列计算机指令。虚拟机能够分为系统虚拟机和程序虚拟机。spring
系统虚拟机:好比VMware,他们彻底是对物理计算机的仿真,提供了一个可运行完整操做系统的软件平台。数组
程序虚拟机:好比Java虚拟机,它专门为执行单个计算机程序而设计。在Java虚拟机中执行的 指令咱们称为Java字节码指令。(JVM是运行在操做系统之上的,它与硬件没有直接的交互)安全
因此根据定义,咱们能够得知JVM是程序虚拟机。那么JVM在哪里呢,其实,咱们在最开始学习Java得时候,都必须按照Java得运行环境,从网上下载JDK安装包,安装完成以后,在安装路径下会有两个文件夹,一个叫Jdk,一个叫jre,而java虚拟机就在jre的文件夹里面。数据结构
存在即有他存在的道理,那么JVM的存在有什么用呢?他是用来干吗的呢?学过JAVA的都知道,java程序要想运行,Java源程序(.java)要先编译成与平台无关的字节码文件(.class),而后字节码文件再解释成机器码运行。而解释得这个过程就是经过Java虚拟机来执行的(能够参考下面这张图理解)。java虚拟机是来解释字节码文件的,而解释得这个过程实际上是一个很复杂得过程,因此这就到了咱们今天要讲得主题了。架构
咱们先来了解一下类加载得整个过程。从下图能够看到类的生命周期一共分为5个阶段,加载、链接(包括验证、准备和解析)、初始化、使用(类得实例化)、卸载(垃圾回收)。框架
在Java代码中,咱们都知道类(指的是类自己Class,好比,Interface,Enum)的加载、链接、初始化过程都是在程序运行期间完成的。下面咱们就先讲一下类得加载、链接和初始化。jvm
类的加载:*最多见的一种状况*是将已存在的类的Class文件(也就是字节码文件)从磁盘上面加载到内存里面,将其放在运行时数据区的方法区中,而后在内存中建立一个java.lang.Class对象用来封装类在方法区中的数据结构
类的链接(又细分了三个阶段):
一、验证:确保被加载类的正确性
二、准备:为类的静态变量(也能够称为类变量)分配内存,并将其初始化为默认值(好比int 的默认值就是0)
三、解析:将类中的符号引用转换为直接引用
类的初始化:为类的静态变量进行赋值(从代码从上到下执行)
Java程序对类的使用方式可分为两种:
主动使用
被动使用
全部的Java虚拟机实现,在每一个类或接口被Java程序"首次主动使用"时才初始化他们,必定要记住,是首次而且仍是主动使用得时候才会初始化类。
若是对其类或者接口主动使用致使初始化了(此时的初始化就说明加载、链接(链接的三个步骤,注意,此时的链接只完成类的静态变量分配内存,并将其初始化为默认值)已经完成了)
我这里总结了7种主动使用:
——建立类的实例
——访问某个类或接口的静态变量,或者对该静态变量赋值
——调用类的静态方法
——反射(如class.forName())
——初始化一个类的子类
——Java虚拟机启动时被代表为启动类的类
——JDK1.7开始提升的动态语言支持;
除了以上7种状况,其余使用Java类的方式都被看作是对类的被动使用,都不会致使类的初始化。
其实咱们知道类的加载的最终产品是位于内存中的Class对象,Class对象封装了类在方法区内的数据结构,而且向Java程序员提供了访问方法区内的数据结构的接口。
根据以上的总结,咱们知道类的链接其实就是当类被加载后,就进入链接阶段。链接就是将已经读入到内存的类的二进制数据合并到虚拟机的运行环境中去。那么类的验证的内容有哪些呢?
类的加载实际上是类加载器去完成的,咱们能够把类加载器想象成一个小人,帮助JVM干活的。那么类加载器的定义是什么呢,这里按照我我的的理解总结了一下:
类加载器(classLoader):类加载器是用来把类加载到Java虚拟机的内存空间中(加载类的工具,类必定是由类加载器去加载)。从JDK1.2版本开始,类的加载过程采用双亲委托机制。这种机制能更好的保证Java平台的安全。在此委托机制中,除了Java虚拟机自带的根类加载器以外(由于根类加载器自己是没有父加载器的),其他的类加载器都有且只有一个父加载器。当Java程序请求加载器loader1加载Sample类时,loader1首先委托本身的父加载器去加载Sample类,若父加载器能加载,则由父加载器完成加载任务,不然才有加载器loader1自己加载Sample类。
类加载器分为两种类型:
类加载器并不须要等到某个类被”首次主动使用“时再加载它
这一小节咱们来详细了解一下类加载器的双亲委托机制。父亲委托机制也称为双亲委托机制(我我的得理解实际上应该叫作父亲委托机制,由于在源码里面是parent而不是parents):在父亲委托机制中,各个加载器按照父子关系造成了熟悉结构(逻辑上的,好比下图),除了启动类加载器以外,其他的类加载器都有且只有一个父加载器。
如下几种加载器从表面看是继承关系,其实是包含关系哦
我举例来看看父亲委托机制的实际执行:
对上图执行流程我详细得解释一下类加载器父亲委托机制具体是怎么执行得:首先loader1和loader2是咱们自定义的加载器,loader1尝试去加载Sample类,根据父亲委托机制,其实并非由loader1去直接加载Sample类到虚拟机当中,相反,它是把这个加载任务转交给系统类加载器去完成,系统类加载器再把这个加载任务转交给扩展类加载器,而后扩展类加载器再转交给根类加载器去完成,因为根类加载器已是类加载器体系层次的最顶层,因此根类加载器会尝试去Sample类到虚拟机当中(而后根类加载器不能加载,由于他是从特定的几个目录去加载),既然根类加载器没法完成加载,他就会把这个任务返回给扩展类加载器(同理,原则上也不能加载),再让系统类加载器去加载(通常是能够加载成功)。最终再把这个流程返回给loader1,就宣告类加载过程结束。
既然咱们了解了类加载器的种类,那咱们也须要了解经过什么方式能够获取到类加载器,获取类加载器的方式我这里总结了4种方式:
第一种:得到当前类的ClassLoader:
clazz.getClassLoder()
具体实现以下所示:
Class<?> clazz1 = Class.forName("java.lang.String");
System.out.println(clazz1.getClassLoader());复制代码
第二种:得到当前线程上下文的ClassLoader:
Thread.currentThread().getContextClassLoader();
具体实现以下所示:
ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
System.out.println(contextClassLoader);复制代码
第三种:得到系统ClassLoader:
ClassLoader.getSystemClassLoader();
第四种:得到调用者的ClassLoader
DriverManager.getCallerLoader()
咱们还须要知道其实数组并非由类加载器加载建立的的,而是当被须要时,被jvm运行时自动建立的,对于数组来讲,他的类加载器是和他元素的类型的类加载同样的,若是元素类型是基本类型,则数组没有类加载器
ClassLoader类自己默认是并行加载的的(parallel capable),若是子类想支持并行加载,是须要本身注册的,用户自定义加载器若须要并行加载,须要自行配置,经过调用registerAsParallelCapable()
经过以上得相关总结,咱们其实能够发现,JVM学习并非像spring,springcloud都是应用框架,是能够立刻作东西的,立竿见影,能够立刻看到效果,JVM不是这样的,涉及到了不少理论。不少同窗可能以为不重要,感受学了也没有,其实否则,就像练武同样,只有你的内功修炼好了,再去练其余的招式就会很容易,才会精益求精,而JVM就至关于内功,因此可想而知,对于JVM的学习,显然是很重要的。以上就是我对JVM类加载器相关总结,下一篇文章应该是推出关于结合java源码理解类加载器得相关内容,固然后续也会推出JVM其余板块相关知识得相关总结。
最后,最近不少小伙伴找我要Linux学习路线图,因而我根据本身的经验,利用业余时间熬夜肝了一个月,整理了一份电子书。不管你是面试仍是自我提高,相信都会对你有帮助!目录以下:
免费送给你们,只求你们金指给我点个赞!
也但愿有小伙伴能加入我,把这份电子书作得更完美!
推荐阅读: