前言

​ 之前去面试的时候面试官问了我关于关于JVM性能调优的问题,由于自己之前公司的项目里自己没有接触到JVM性能调优的相关问题(感觉这些都是公司架构师考虑的问题),所有面试官问的时候自己一脸懵逼,所有最后的结果当然是凉凉。。,于是,为了查漏补缺,就去学习了一下JVM的相关知识,希望能帮助到大家。

正文

​ 在学习任何一项新的知识之前,我都会先列出一份学习大纲,然后按照这个学习大纲一步一步的来学习了解,所以学习JVM这个新的技术,我也分为了3个板块来学习:JVM类加载器,JVM内存结构,JVM垃圾回收这三个板块来学习,今天这篇文章讲的是JVM类加载器。

1、什么是JVM

​ 既然是学习关于JVM的相关理论知识,我们当然得知道什么是JVM。JVM是Java Virtual Machine(Java虚拟机)的缩写。既然说到虚拟机,可能又会有人问什么是虚拟机了,我这里把虚拟机得相关概念放在这里:

虚拟机:就是一台虚拟的计算机,他是一款软件;用来执行一系列计算机指令。虚拟机可以分为系统虚拟机和程序虚拟机。

  • 系统虚拟机:比如VMware,他们完全是对物理计算机的仿真,提供了一个可运行完整操作系统的软件平台。

  • 程序虚拟机:比如Java虚拟机,它专门为执行单个计算机程序而设计。在Java虚拟机中执行的 指令我们称为Java字节码指令。(JVM是运行在操作系统之上的,它与硬件没有直接的交互)

所以根据定义,我们可以得知JVM是程序虚拟机。那么JVM在哪里呢,其实,我们在最开始学习Java得时候,都必须按照Java得运行环境,从网上下载JDK安装包,安装完成之后,在安装路径下会有两个文件夹,一个叫Jdk,一个叫jre,而java虚拟机就在jre的文件夹里面。

​ 存在即有他存在的道理,那么JVM的存在有什么用呢?他是用来干嘛的呢?学过JAVA的都知道,java程序要想运行,Java源程序(.java)要先编译成与平台无关的字节码文件(.class),然后字节码文件再解释成机器码运行。而解释得这个过程就是通过Java虚拟机来执行的(可以参考下面这张图理解)。java虚拟机是来解释字节码文件的,而解释得这个过程其实是一个很复杂得过程,所以这就到了我们今天要讲得主题了。

2、类加载(classLoading)

​ 我们先来了解一下类加载得整个过程。从下图可以看到类的生命周期一共分为5个阶段,加载、连接(包括验证、准备和解析)、初始化、使用(类得实例化)、卸载(垃圾回收)。

​ 在Java代码中,我们都知道类(指的是类本身Class,比如,Interface,Enum)的加载、连接、初始化过程都是在程序运行期间完成的。下面我们就先讲一下类得加载、连接和初始化。

类的加载:最常见的一种情况是将已存在的类的Class文件(也就是字节码文件)从磁盘上面加载到内存里面,将其放在运行时数据区的方法区中,然后在内存中创建一个java.lang.Class对象用来封装类在方法区中的数据结构

类的连接(又细分了三个阶段):

​ 1、验证:确保被加载类的正确性

​ 2、准备:为类的静态变量(也可以称为类变量)分配内存,并将其初始化为默认值(比如int 的默认值就是0)

​ 3、解析:将类中的符号引用转换为直接引用

类的初始化:为类的静态变量进行赋值(从代码从上到下执行)

Java程序对类的使用方式可分为两种:

  • 主动使用

  • 被动使用

所有的Java虚拟机实现,在每个类或接口被Java程序"首次主动使用"时才初始化他们,一定要记住,是首次并且还是主动使用得时候才会初始化类。

如果对其类或者接口主动使用导致初始化了(此时的初始化就说明加载、连接(连接的三个步骤,注意,此时的连接只完成类的静态变量分配内存,并将其初始化为默认值)已经完成了)

我这里总结了7种主动使用:

——创建类的实例

——访问某个类或接口的静态变量,或者对该静态变量赋值

——调用类的静态方法

——反射(如class.forName())

——初始化一个类的子类

——Java虚拟机启动时被表明为启动类的类

——JDK1.7开始提高的动态语言支持;

除了以上7种情况,其他使用Java类的方式都被看做是对类的被动使用,都不会导致类的初始化。

3、类的加载连接初始化详细讲解

​ 其实我们知道类的加载的最终产品是位于内存中的Class对象,Class对象封装了类在方法区内的数据结构,并且向Java程序员提供了访问方法区内的数据结构的接口。

根据以上的总结,我们知道类的连接其实就是当类被加载后,就进入连接阶段。连接就是将已经读入到内存的类的二进制数据合并到虚拟机的运行环境中去。那么类的验证的内容有哪些呢?

  • 类文件的结构检查

  • 语义检查

  • 字节码验证

  • 二进制兼容性的验证

4、类加载器

​ 类的加载其实是类加载器去完成的,我们可以把类加载器想象成一个小人,帮助JVM干活的。那么类加载器的定义是什么呢,这里按照我个人的理解总结了一下:

类加载器(classLoader):类加载器是用来把类加载到Java虚拟机的内存空间中(加载类的工具,类一定是由类加载器去加载)。从JDK1.2版本开始,类的加载过程采用双亲委托机制。这种机制能更好的保证Java平台的安全。在此委托机制中,除了Java虚拟机自带的根类加载器之外(因为根类加载器本身是没有父加载器的),其余的类加载器都有且只有一个父加载器。当Java程序请求加载器loader1加载Sample类时,loader1首先委托自己的父加载器去加载Sample类,若父加载器能加载,则由父加载器完成加载任务,否则才有加载器loader1本身加载Sample类。

类加载器分为两种类型:

1、Java虚拟机自带的加载器

  • 根类加载器(BootstrapClassLoader),也称启动类加载器
  • 扩展类加载器(ExtensionClassLoader)
  • 系统(应用)类加载器(SystemClassLoader或者AppClassLoader)

2、用户自定义的类加载器

  • java.lang.ClassLoader的子类(所有用户自定义的类加载器都应该继承抽象类ClassLoader类)
  • 用户可以定制类的加载方式

类加载器并不需要等到某个类被”首次主动使用“时再加载它

5、类加载器双亲委托机制详解

​ 这一小节我们来详细了解一下类加载器的双亲委托机制。父亲委托机制也称为双亲委托机制(我个人得理解实际上应该叫做父亲委托机制,因为在源码里面是parent而不是parents):在父亲委托机制中,各个加载器按照父子关系形成了熟悉结构(逻辑上的,比如下图),除了启动类加载器之外,其余的类加载器都有且只有一个父加载器。

以下几种加载器从表面看是继承关系,实际上是包含关系哦

我举例来看看父亲委托机制的实际执行:

​ 对上图执行流程我详细得解释一下类加载器父亲委托机制具体是怎么执行得:首先loader1和loader2是我们自定义的加载器,loader1尝试去加载Sample类,根据父亲委托机制,其实并不是由loader1去直接加载Sample类到虚拟机当中,相反,它是把这个加载任务转交给系统类加载器去完成,系统类加载器再把这个加载任务转交给扩展类加载器,然后扩展类加载器再转交给根类加载器去完成,由于根类加载器已经是类加载器体系层次的最顶层,所以根类加载器会尝试去Sample类到虚拟机当中(然后根类加载器不能加载,因为他是从特定的几个目录去加载),既然根类加载器无法完成加载,他就会把这个任务返回给扩展类加载器(同理,原则上也不能加载),再让系统类加载器去加载(一般是可以加载成功)。最终再把这个流程返回给loader1,就宣告类加载过程结束。

6、获取类加载器的几种途径

​ 既然我们了解了类加载器的种类,那我们也需要了解通过什么方式可以获取到类加载器,获取类加载器的方式我这里总结了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()

7、总结

​ 通过以上得相关总结,我们其实可以发现,JVM学习并不是像spring,springcloud都是应用框架,是可以马上做东西的,立竿见影,可以马上看到效果,JVM不是这样的,涉及到了很多理论。很多同学可能觉得不重要,感觉学了也没有,其实不然,就像练武一样,只有你的内功修炼好了,再去练其他的招式就会很容易,才会精益求精,而JVM就相当于内功,所以可想而知,对于JVM的学习,显然是很重要的。以上就是我对JVM类加载器相关总结,下一篇文章应该是推出关于结合java源码理解类加载器得相关内容,当然后续也会推出JVM其他板块相关知识得相关总结。


公众号:良许Linux

有收获?希望老铁们来个三连击,给更多的人看到这篇文章

我把JVM的类加载器整理了一下的更多相关文章

  1. JVM自定义类加载器加载指定classPath下的所有class及jar

    一.JVM中的类加载器类型 从Java虚拟机的角度讲,只有两种不同的类加载器:启动类加载器和其他类加载器. 1.启动类加载器(Boostrap ClassLoader):这个是由c++实现的,主要负责 ...

  2. 【深入理解JVM】类加载器与双亲委派模型 (转)

    出处: [深入理解JVM]类加载器与双亲委派模型 加载类的开放性 类加载器(ClassLoader)是Java语言的一项创新,也是Java流行的一个重要原因.在类加载的第一阶段“加载”过程中,需要通过 ...

  3. 1.1 jvm核心类加载器--jdk源码剖析

    目录 前提: 运行环境 1. 类加载的过程 1.1 类加载器初始化的过程 1.2 类加载的过程 1.3 类的懒加载 2. jvm核心类加载器 3. 双亲委派机制 4. 自定义类加载器 5. tomca ...

  4. 深入理解JVM,类加载器

    虚拟机设计团队把类加载阶段中的“通过一个类的全限定名来获取描述此类的二进制字节流(即字节码)”这个动作放到Java虚拟机外部去实现,以便让应用程序自己决定如何去获取所需要的类.实现这个动作的代码模块称 ...

  5. 【JVM】JVM之类加载器

    一.前言 首先,小小测试,看是否已经掌握了JVM类加载的过程 1.1.测试一 class Singleton { private static Singleton sin = new Singleto ...

  6. JVM之类加载器下篇

    除了自定义的类加载之外,jvm存在三种类加载器,并以一种父委托的加载机制进行加载. --启动类加载器,又称根加载器,是一个native的方法,使用c++实现.在java中我们用null标识,用于加载j ...

  7. 【深入理解JVM】类加载器与双亲委派模型

    原文链接:http://blog.csdn.net/u011080472/article/details/51332866,http://www.cnblogs.com/lanxuezaipiao/p ...

  8. JVM学习一:JVM之类加载器概况

    18年转眼就3月份都快结束了,也就是说一个季度就结束了:而我也因为年前笔记本坏了,今天刚修好了,那么也应该继续学习和博客之旅了.今年的博客之旅,从JVM开始学起,下面我们就言归正传,进入正题. 一.J ...

  9. (转)JVM——自定义类加载器

    背景:为什么要自定义,如何自定义,实现过程 转载:http://blog.csdn.net/SEU_Calvin/article/details/52315125 0. 为什么需要自定义类加载器 网上 ...

随机推荐

  1. java之FTP上传下载

    import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import ...

  2. Java WebService学习笔记 - Axis进阶(二)

    上一篇  Java WebService学习笔记 - Axis(一) 前一篇博文中简单介绍了Axis的使用方法,这篇将介绍一些Axis的一些高级特性 Axis中Handler的使用 Handler ...

  3. 删库吧,Bug浪——我们在同一家摸鱼的公司

    那些口口声声, Bug越来越难写人的,应该盯着你们: 像我一样,我盯着你们,满眼恨意. IT积攒了几十年的漏洞, 所有的死机.溢出.404和超时, 像是专门为你们准备的礼物. 圈复杂度.魔鬼变量.内存 ...

  4. 【转载】提高访问 github 的速度

    原文地址:https://www.cnblogs.com/liuchao888/p/11733996.html 工具地址:http://tool.chinaz.com/dns?type=1&h ...

  5. cv2 exposureFusion (曝光融合)

    import cv2 import numpy as np import sys filenames = ['./images/memorial0061.jpg', './images/memoria ...

  6. C# 接口(interface) 抽象类(abstract)

    类代码: interface Employee { void ShowEmp(); } abstract class EmployeeInPostion: Employee { public abst ...

  7. 一个Window/Linux(Fedora测试平台)的CPU,磁盘,内存,PC,进程相关信息采集功能

    说明:采用的是Multi-Byte Character Set,不支持Unicode. Peer2PeerData.h #ifndef _PEER_2_PEER_DATA_H #define _PEE ...

  8. WSL配置高翔vslam环境配置流水账

    1 安装参考博文链接:https://www.cnblogs.com/dalaska/p/12802384.html 2 Ubuntu 16.04地址:https://www.microsoft.co ...

  9. flask的小错误

    这几天刚学flask,根据录屏学代码的时候,遇到一个问题 基本能看懂错误,role_id是类的一个字段,应该是一个对象,最后发现是单词写错了,应该是大写的Column, db.Column(db.In ...

  10. BZOJ3573 米特运输 题解

    题目 米特是D星球上一种非常神秘的物质,蕴含着巨大的能量.在以米特为主要能源的D星上,这种米特能源的运输和储存一直是一个大问题.D星上有N个城市,我们将其顺序编号为1到N,1号城市为首都.这N个城市由 ...