之前去面试的时候,面试官问我JVM性能调优,因为我之前公司的项目没有接触过JVM性能调优。(感觉这些都是公司架构师考虑过的问题),面试官问的时候都是一脸懵逼,最后的结果当然都是爽。.,所以,为了查漏补缺,我去学习了一下JVM的相关知识,希望对大家有所帮助。在学习任何新的知识之前,我都会先列一个学习大纲,然后按照这个学习大纲一步步去学习和理解,所以学习JVM的新技术,我也是分3个部分来学习:JVM类加载器,JVM内存结构,JVM垃圾回收是3个部分来学习。今天的文章是关于JVM类加载器的。一、什么是JVM既然是学习JVM的相关理论知识,那当然要知道什么是JVM。JVM是Java虚拟机(JavaVirtualMachine)的缩写。说到虚拟机,可能有人会问什么是虚拟机。我把虚拟机的相关概念放在这里:它用于执行一系列计算机指令。虚拟机可以分为系统虚拟机和程序虚拟机。系统虚拟机:如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代码中,我们都知道类(指类本身,如Interface、Enum)的加载、连接、初始化等过程都是在程序运行过程中完成的。先说类的加载、连接和初始化。类加载:*最常见的情况*是将已有类的Class文件(即字节码文件)从磁盘加载到内存中,放在运行时数据区的方法区中。然后在内存中创建一个java.lang.Class对象,封装方法区中类的数据结构类的连接(分为三个阶段):验证:保证加载类的正确性准备:for静态变量类的(也称为类变量)分配内存并初始化为默认值(例如int的默认值为0)解析:将类中的符号引用转换为直接引用类初始化:为类静态变量赋值(从上到下执行)Java程序可以分为两种使用类的方式:主动使用和被动使用所有Java虚拟机实现,当每个类或接口被一个Java程序“第一次主动使用”时,它们在使用“时被初始化,重要的是要记住类在第一次被主动使用时被初始化。如果主动使用它的类或接口导致初始化(initi此时的alization是指loading和connection这三个步骤(connection,注意此时的connection只是完成了类的静态变量分配并初始化为默认值)已经完成)我总结了7个activeuses这里:创建一个类的实例来访问类或接口的静态变量,或者给静态变量赋值调用类的静态方法反射(如class.forName())来初始化一个当Java虚类的子类的机器启动,表示为启动类JDK1类的动态语言支持。不会导致类的初始化。3、类加载连接初始化详解其实我们知道,类加载的最终产物是一个位于内存中的Class对象。Class对象在方法区封装了类的数据结构,为Java程序员提供了访问方法区内数据结构的接口。根据上面的总结,我们知道类的连接其实就是在类被加载的时候,就进入了连接阶段。链接就是将已经读入内存的类的二进制数据合并到虚拟机的运行环境中。那么类验证的内容是什么呢?类文件结构检查语义检查字节码验证二进制兼容性验证4.类加载器类加载实际上是由类加载器完成的,我们可以把类加载器想象成一个帮助JVM工作的小人。那么类加载器的定义是什么,这里根据我个人的理解做一个总结:必须由类加载器加载)。从JDK1.2版本开始,类加载过程采用双亲委托机制。这种机制可以更好的保证Java平台的安全。在这种委托机制中,除了Java虚拟机自带的根类加载器(因为根类加载器本身没有父加载器)外,其他类加载器都有且只有一个父加载器。当Java程序请求加载器loader1加载Sample类时,loader1首先委托其父加载器加载Sample类。如果父加载器能够加载,则父加载器完成加载任务,否则加载器loader1自己加载Sample类。种类。类加载器分为两种:(1)Java虚拟机自带的加载器根类加载器(BootstrapClassLoader),也称为启动类加载器扩展类加载器(ExtensionClassLoader)系统(应用)类加载器(SystemClassLoader)或AppClassLoader)(2)自定义类加载器java.lang.ClassLoader的子类(所有自定义类加载器都应该继承抽象类ClassLoader类)用户可以自定义加载类的方式。类加载器不需要等到一个类“第一次被主动使用”之后再加载它五、类加载器的双亲委派机制详解本节我们来详细了解一下类加载器的双亲委托机制。父委托机制又叫双亲委托机制(我个人不得不理解其实应该叫父委托机制,因为在源码中是parent而不是parents):在父委托机制中,每个loader形式一个熟悉的结构,按照父子关系(逻辑上,如下图),除了启动类加载器,其他类加载器只有一个父加载器。下面这几种loader,表面上看是继承关系,实际上是包含关系。下面我举个例子来看看父委托机制的实际实现:对于上图的执行过程,不得不详细解释一下类加载器的父委托机制是如何实现的。执行:首先,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();具体实现如下:ClassLoadercontextClassLoader=Thread。currentThread().getContextClassLoader();System.out.println(contextClassLoader);第三种:获取系统ClassLoader:ClassLoader.getSystemClassLoader();第四种:获取调用者的ClassLoaderDriverManager.getCallerLoader()我们还需要知道,数组实际上并不是类加载器创建的,而是在需要的时候,由jvm运行时自动创建的。对于一个数组,它的类加载器和它的元素类型的类加载是一样的。如果元素类型是基本类型,则数组没有类加载ClassLoader类本身默认具有并行能力。如果子类要支持并行加载,需要自己注册。自定义加载器如果需要并行加载,需要自己配置,通过调用registerAsParallelCapable()7。applicationframework,可以马上做事,马上可以看到效果,JVM不是这样的,涉及很多理论。很多同学可能觉得不重要,觉得没什么可学的。其实不一样。就像练武一样。内功修好了,练其他招式就容易了,而且会精益求精,而JVM就相当于内功,所以可以想见,对于JVM的学习显然是非常重要的。以上是我对JVM类加载器的总结。下一篇文章应该是结合java源码对类加载器的理解。
