前言

我们很多小伙伴平时都是做JAVA开发的,那么作为一名合格的工程师,你是否有仔细的思考过JVM的运行原理呢。

如果懂得了JVM的运行原理和内存模型,像是一些JVM调优、垃圾回收机制等等的问题我们才能有一个更清晰的概念。

为了走进JVM,深入了解底层,王子打算写一个JVM的专题,留下自己对JVM探索的足迹,同时也希望能帮到小伙伴们更好的理解JVM。

那我们开始吧。

JAVA代码的运行流程

首先我们就来聊一聊JAVA代码是怎么运行起来的,这部分比较基础相信大家都知道,就当成是个复习吧。

我们编写的代码都是在java文件中编写的,然后会编译成class字节码文件。

当我们使用到哪个类的时候就会通过类加载器把class字节码文件中的类加载到jvm内存中,然后就是在jvm内存中运行我们的代码了。

整体的运行流程就是这样,相信小伙伴们都很清楚这些,但是有关类加载器是如何把类加载到jvm内存中的,小伙伴们有考虑过吗?

今天我们主要就是聊这一部分。

JVM什么时候加载类

其实说到类加载的底层机制,这是一个很复杂的过程,但是对于我们平时的工作来讲,只要懂得它的核心原理就可以了。

一个类的加载过程会经历如下的几个过程:

加载、验证、准备、解析、初始化、使用、卸载

首先我们就先弄明白一个问题,jvm是什么时候去加载类的呢?

其实答案很简单,就是我们什么时候使用到了这个类,它就去class字节码文件中去加载这个类。

而作为程序的入口,具有main方法的类,肯定是最开始的时候就加载到jvm中了。

对于加载类的时间点问题,其实就是这么简单。

类加载器和双亲委派机制

既然我们知道了类加载的时间点,那么jvm是通过什么方式对类进行加载的呢?就是类加载器。

那接下来我们就来聊聊jvm的类加载器。

jvm的类加载器总体上可以分成4层,我们一起看一下。

1.启动类加载器

首先就是jvm启动的第一道关口,启动类加载器Bootstrap ClassLoader,它主要是加载java的核心类。

相信大家都知道,无论是什么环节下运行java程序,都是要安装jvm虚拟机环境的,而在这个环境的目录中是有一个lib文件夹的,这个文件下就是java最核心的类库,支撑着java系统的运行。

所以一旦jvm启动,那么首先就会通过启动类加载器去加载lib文件夹下的核心类库。

2.扩展类加载器

然后我们就到了第二层,扩展类加载器Extension ClassLoader,这个类加载器其实与启动类加载器是类似的。

在我们的jvm虚拟机环境目录下,是有一个lib/ext的文件夹的,这里面的类就是java运行环境的一些扩展类,这些扩展类就是在jvm启动后,通过扩展类加载器进行加载的。

3.应用程序类加载器

加载完核心类库和扩展类,这时候就到了第三层,应用程序类加载器Application ClassLoader,这个类加载器你就可以理解成是加载我们写好的java代码的就可以了。

4.自定义类加载器

前面的三层就是基本的类加载器了,然后第四层是自定义类加载器,根据一些特殊的需求来自己定义类加载器加载我们的类。

整体上类加载器就是这么的4层结构。很多小伙伴可能都听说过双亲委派机制,那么什么是双亲委派机制呢,王子就和大家用最接地气的语言描述一下。

其实很好理解,就是当我们的类加载器要加载一个类的时候,它首先会委派给它的父亲去加载,但是如果它的父亲没找到就会把这个事交给他的孩子自己去完成了。

这就是双亲委派机制。

举个例子,假如我们的应用程序类加载器要加载一个类A,那么首先它会先回家找它老爸扩展类加载器,问问“老爸,你那有这个类A吗?”

然后扩展类加载器接到这个请求之后,同样也懒得处理,再去找它爷爷启动类加载器。

它爷爷找了一圈没找到类A,很生气,就对扩展类加载器说,“我这没有,你自己找去!”

然后扩展类加载器就灰溜溜的自己找了一圈,同样也没找到,这时候就找到应用类加载器了,说:“我这哪有你这个类A,这明明是你自己应该干的活,爱上哪找上哪找去,我不管了”。

这时候应用类加载器就只能自己去处理了,找了一圈发现找到了类A,就把它加载到jvm内存中了。

相信大家看了这个例子应该很容易理解了吧。

所以假设我们自己创建了一个类java.lang.String,它是不会被应用类加载器加载到内存中的,因为父类中可以找到这个类,就直接给加载到内存中了。

聊聊验证、准备、解析、初始化阶段

聊完了加载,我们再来看看验证、准备、解析、初始化这几个阶段jvm都做了什么。

1.验证阶段

这一步其实很容易理解,就是jvm根据java规范,来校验你加载进来的class文件中的内容是否符合规范,如果不符合规范jvm是无法正常运行的。

所以在加载后,首先就是验证阶段。

2.准备阶段

假设我们有一个类A,刚刚加载并通过了验证,那么就会进行准备工作。

这个准备工作其实就是给类A分配一定的内存空间,然后给里面的静态变量(static修饰的变量)也分配内存空间,并赋初始值。

3.解析阶段

这个阶段干的事实际上是把符号引用替换为直接引用,这一过程网上有很多资料,还是比较复杂的,如果感兴趣小伙伴们可以自己查阅一下资料。

实际工作中也很少会接触这部分的内容,所以我们知道有这么个阶段就可以了。

4.初始化阶段

在准备阶段,我们把类A的内存已经分配完了,那么初始化阶段要做些什么事呢?我们先看一下类A的代码

public class A {
private static String i=System.getProperty("i");
}

准备阶段我们只是给变量i分配了内存空间,并赋值了初始值,但是后边的System.getProperty("i")是不会执行的。

没错,这部分代码就是在初始化阶段执行的,另外静态代码块也会在这一阶段执行。

举个例子,比如我们新建一个对象new A(),此时就会触发从加载到初始化的全过程,把这个类准备好并创建一个实例对象。

此外这里有一个规则,如果类A继承了类B,那么在初始化类A的时候,如果发现类B还没有初始化,会先初始化类B。

扩展

到这里关于JVM的类加载机制其实就已经说完了,王子再给大家扩展一个小知识点。

小伙伴们想过没有,Tomcat也是用java开发的,那么它的类加载机制是什么样的呢,为什么就能支持jsp呢?

其实它就是利用了自定义类加载器这一机制,自己自定义了很多类加载器,整体的结构如下:

Tomcat自定义了这么多的类加载器,用来加载它自己的核心类库,并且Tomcat是打破了双亲委派机制的,感兴趣的小伙伴可以自己去查资料了解一下,王子就不在本篇文章长篇大论来聊Tomcat了。

总结

今天我们聊的内容还是jvm中比较基础的部分,以后的文章我们再慢慢深入,去探索jvm的底层原理,如果对JVM感兴趣的小伙伴可以关注王子的后续文章哦,我们可以一步一个脚印的逐步分解JVM,去了解JVM的垃圾回收机制、性能调优等等实用性问题,让你面对JVM的面试或者生产实践也可以游刃有余。

那我们下次见。

往期文章推荐:

Windows下使用Nginx+Tomcat做负载均衡

聊聊分布式下的WebSocket解决方案

从SpringBoot源码看资源映射原理

大白话谈JVM的类加载机制的更多相关文章

  1. JVM内存结构 JVM的类加载机制

    JVM内存结构: 1.java虚拟机栈:存放的是对象的引用(指针)和局部变量 2.程序计数器:每个线程都有一个程序计数器,跟踪代码运行到哪个位置了 3.堆:对象.数组 4.方法区:字节流(字节码文件) ...

  2. JVM之类加载机制

    JVM之类加载机制 JVM类加载机制分为五个部分:加载,验证,准备,解析,初始化,下面我们就分别来看一下这五个过程. 类加载五部分 加载 加载是类加载过程中的一个阶段,这个阶段会在内存中生成一个代表这 ...

  3. JVM的类加载机制全面解析

    什么是类加载机制 JVM把描述类的数据从Class文件加载到内存,并对数据进行校验.转换解析和初始化,最终形成可以被JVM直接使用的Java类型,这就是JVM的类加载机制. 如果你对Class文件的结 ...

  4. 一文教你读懂JVM的类加载机制

    Java运行程序又被称为WORA(Write Once Run Anywhere,在任何地方运行只需写入一次),意味着我们程序员小哥哥可以在任何一个系统上开发Java程序,但是却可以在所有系统上畅通运 ...

  5. 漫谈JVM之类加载机制(篇一)

    前言 最近在看一本书,发现代码里用到了Thread.currentThread().getContextClassLoader(),为什么类加载器还与线程有关系呢,为什么不直接使用ClassLoade ...

  6. JVM的类加载机制

    虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验.转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制. 类加载的过程: 包括加载.链接(含验证.准备 ...

  7. 【JVM】类加载机制

    原文:[深入Java虚拟机]之四:类加载机制 类从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期包括:加载.验证.准备.解析.初始化.使用和卸载七个阶段.它们开始的顺序如下图所示: 类加 ...

  8. 深入理解JVM(3)——类加载机制

    1.类加载时机 类的整个生命周期包括了:加载( Loading ).验证( Verification ).准备( Preparation ).解析( Resolution ).初始化( Initial ...

  9. (转) JVM——Java类加载机制总结

    背景:对java类的加载机制,一直都是模糊的理解,这篇文章看下来清晰易懂. 转载:http://blog.csdn.net/seu_calvin/article/details/52301541 1. ...

随机推荐

  1. java基本数据类型总结 类型转换 final关键字的用法

    java基本数据类型总结 Java数据类型总结 数据类型在计算机语言里面,是对内存位置的一个抽象表达方式,可以理解为针对内存的一种抽象的表达方式.接触每种语言的时候,都会存在数据类型的认识,有复杂的. ...

  2. webpack打包初始入门教程

    Webpack 是一个前端资源加载/打包工具.它将根据模块的依赖关系进行静态分析,然后将这些模块按照指定的规则生成对应的静态资源. 从图中我们可以看出,Webpack 可以将多种静态资源 js.css ...

  3. 记录使用vs code两天的心得

    一个字 就是骚~感觉以后写博客都省了

  4. 编程体系结构(02):Java异常体系

    本文源码:GitHub·点这里 || GitEE·点这里 一.异常简介 优秀的程序代码,都在追求高效,安全,和低错误率,但是程序中的异常是无法避免的,降低异常出现的频率是关键,异常出现如何处理是另一个 ...

  5. Activiti7 绑定业务主键以及流程定义 流程实例的挂起和激活

    绑定业务主键businessKey /** * 绑定业务主键 */ @Test public void bindingBusinessKey() { // 获取RuntimeService Runti ...

  6. python3之range()

    python range() 函数可创建一个整数列表,一般用在 for 循环中. 函数语法(左闭右开) Python3 range() 函数返回的是一个可迭代对象(类型是对象),而不是列表类型, 所以 ...

  7. Java的String.matches不区分大小写正则表达式匹配示例

    需求:输入的cmd符合create,listall,delete三种形式,不用区分大小写 写成函数: public static boolean isAllowed3Cmd(String cmd) { ...

  8. 接口自动化---简单的数据驱动框架ATP(基于excel)

    数据驱动测试:根据数据进行测试.将用例写入excel文件,用代码读取文件中的数据,从而实现自动化测试. 自动化框架实现步骤: 1.获取用例 2.调用接口 3.校验结果 4.发送测试报告 5.异常处理 ...

  9. python 模块安装导入

    一.定义 python模块就是一个.py文件,一个模块中可以有多个函数,在使用模块时,只需要import下,就可以使用模块中的函数功能.import模块的过程相当于把这个py文件中的所有内容都执行一遍 ...

  10. spring5源码 -- IOC容器设计理念和核心注解的作用

    一. spring源码整体脉络介绍及源码编译 二. Spring IOC的源码深入学习 2.1 ioc容器的加载过程(重要) 2.2 bean的生命周期源码深度剖析 2.3 循环依赖总结讲解 2.4 ...