谈谈“会”的三个层次

在《说透分布式事务》中,我举例里说明了会与会的差别。对一门语言的学习,这里谈谈我理解的“会”的三个层次:

第一层:了解这门语言的语法、写法,我把它叫做 hello world 级别;

第二层:了解这门语言的优劣势以及它的生态,了解这门语言的能力范围,我把它叫做 应用 级别;

第三层:了解这门语言的底层运行机制,这有利于对程序进行调优,以及当程序遇到了比较罕见的问题时能够从根上分析解决它。我把它叫做 掌握 级别。

在简历上写掌握某种语言的,一般面试官也会问一些很深入原理的问题,个人认为比较合理。自己作为一个15年一线Java开发,自认为有资格把掌握Java写在简历上。今天,我就来聊聊我对双亲委派机制一些理解。

插个题外话,在《高并发下秒杀商品,你必须知道的9个细节》中,有朋友问配图是用什么画的。这里介绍一下自己的经验:

1)思维导图还是processon更加方便:

https://www.processon.com/i/594d313ae4b08b003f2ec84a

2)流程图还是draw.io,这个不推荐在线编辑,慢到怀疑人生。安装版本也是免费的,官网可轻松下载。开头图的框图效果是draw.io的框图有个 Sketch 样式。这个样式很好看,但是不建议用于文献等正式场合。正式场合的图最好方方正正,不要太圆润,粗细均匀。

3)生成曲线图、柱状图这些,还是习惯用excel。

Java类加载机制

首先我们需要思考一件事,我们编写的Java代码,是如何在各种各样的操作系统上运行起来的。

Java文件通过Javac编译成class文件,这种中间码被称为字节码。然后由JVM加载字节码。这个过程就称为类加载。

运行时,由解释器将字节码解释为一行行的机器码来执行。在程序运行期间,即时编译器会针对热点代码,将该部分字节码编译成机器码以获取更高的执行效率。在整个运行时,解释器和即时编译器相互配合,使程序几乎能达到和编译型语言几乎一样的执行速度。这个部分交给专业的编译器开发人员来做,咱们本篇不做深入讲解。

到此上面那张图就讲完了,不要问我右上角那两个表情是怎么回事。就是发现编辑的时候竟然可以添加表情,觉得好玩就试试看。

类的生命周期

在详细讲解之前,我们明确一下类加载流程的目的。站在高处去看,就是把一份被javac编译过的文件通过加载,生成某种形式的class文件的数据结构送进内存。程序可以调用这个数据结构来构造出Java对象。这个过程是在运行时进行的,也是Java动态拓展性的根基。

上面这张图表现了类的整个生命周期。而类加载呢,只包含了加载、链接和初始化三个阶段。加载只是类加载的第一个环节,两者要注意区分。解析部分是灵活的,它可以在初始化环节之前或者之后进行,实现后期绑定。类加载的其他环节的顺序是不可改变的。

加载

加载是一个读取class文件,将其转化为某种静态数据结构而存储在方法区内,并在堆中生成一个便于用户调用的Java对象的过程。

这里值得注意的是,这个Java文件不一定是本地文件,泛指各种来源的二进制流,比如网络、数据库或者比如动态代理技术这样的即时生成的class文件。

验证

验证的步骤很多,上面的图画得不完全准确。对文件格式的校验其实是发生在加载阶段的。通过才能顺利加载。顺利加载并不代表JVM完全认可了这个类,还要进行语法和语义上的分析,保证这个类不会危害JVM,这是对元数据和字节码上的验证。在解析阶段,还会进行符号引用的验证。随着JVM版本的升高,验证过程也在被不断丰富。

准备

准备就是为静态变量赋初始值,注意这里的初始值是JVM默认初始值,是固定的,不是咱们写代码时的那个初始值。这里有个比较容易混淆的概念。

Java内存规范定义了方法区这种抽象概念。主流的JVM实现如HotSpot在JDK8之前使用永久代这种在堆中开辟专门空间的实现方式,JDK8之后使用元空间这种直接内存取代。

HotSpot的实现:类的元信息、常量池、静态变量等都存在在JDK8之前都存在在永久代这种方法区的具体实现中。JDK8之后,常量池、静态变量被从方法区移除,转移到了堆中。元信息这些依然保留在方法区,具体的存储方式改成了元空间。

解析

解析是将符号引用替换为直接引用。

静态解析

符号引用就是假如类A引用了类B,加载阶段是静态解析,这时候B还没有被放到JVM内存中,这时候A引用的只是代表B的符号,这是符号引用。

直接引用就是类A在解析阶段发现自己引用了B,如果这个时候B还没被加载。就是直接触发B的类加载,之后B的符号引用会被替换成实际地址。这被称为直接引用。

动态解析

本文类的生命周期部分引出了后期绑定这个概念。后期绑定其实就是动态解析。如果代码使用了多态。B是一个抽象类或者接口,A就不能知道究竟要用哪个来替换,只能等到实际发生调动时在进行实际地址的替换。这就是为什么有的解析发生在初始化之后。

总结

类加载的过程今天就讲这些。咱们来回顾一下类加载的五个阶段。

从JVM的角度看,加载的读取二进制流和初始化阶段,是开放了主导权给用户的。用户可以使用动态代理等手段选择是否这个阶段进行加载。还可以使用多态的手段选择是否在这个阶段进行初始化。而剩下的所有部分都是JVM内部完成的。

此时你可以闭上眼睛回顾一下类加载的五个阶段,是不是不用死记硬背也能了然于胸了。

深入理解Java类加载机制,再也不用死记硬背了的更多相关文章

  1. 理解Java类加载机制(译文)

    理解java类加载机制 你想写类加载器?或者你遇到了ClassCastException异常,或者你遇到了奇怪的LinkageError状态约束异常.应该仔细看看java类的加载处理了. 什么是类加载 ...

  2. 阿里面试题,深入理解Java类加载机制

    类的生命周期 包括以下 7 个阶段: 加载(Loading) 验证(Verification) 准备(Preparation) 解析(Resolution) 初始化(Initialization) 使 ...

  3. 深入理解Java类加载

    本文目的: 深入理解Java类加载机制; 理解各个类加载器特别是线程上下文加载器; Java虚拟机类加载机制 虚拟机把描述类的数据从 Class 文件加载到内存,并对数据进行校验.转换解析和初始化,最 ...

  4. Java类加载机制的理解

    算上大学,尽管接触Java已经有4年时间并对基本的API算得上熟练应用,但是依旧觉得自己对于Java的特性依然是一知半解.要成为优秀的Java开发人员,需要深入了解Java平台的工作方式,其中类加载机 ...

  5. 深入理解和探究Java类加载机制

    深入理解和探究Java类加载机制---- 1.java.lang.ClassLoader类介绍 java.lang.ClassLoader类的基本职责就是根据一个指定的类的名称,找到或者生成其对应的字 ...

  6. 深入理解Java类加载器(ClassLoader) (转)

    转自: http://blog.csdn.net/javazejian/article/details/73413292 关联文章: 深入理解Java类型信息(Class对象)与反射机制 深入理解Ja ...

  7. Java类加载机制与Tomcat类加载器架构

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

  8. 深入理解JVM虚拟机6:深入理解JVM类加载机制

    深入理解JVM类加载机制 简述:虚拟机把描述类的数据从class文件加载到内存,并对数据进行校验.转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制. 下面我们具体 ...

  9. Java类加载机制()

    Java类加载机制(转载) 概述 在开始正文之前,我们先看两张图 .Java平台的理解?Java最显著的特性?Java是解释执行? 先看一下java程序的执行流程图 再看一下jvm的大致物理结构图 本 ...

随机推荐

  1. 【刷题-LeetCode】209. Minimum Size Subarray Sum

    Minimum Size Subarray Sum Given an array of n positive integers and a positive integer s, find the m ...

  2. Markdown最新使用说明

    Markdown基本语法说明 markdown 和 typora关系:typora是一款实时预览markdown的文本编辑器 本文推荐使用的Markdown编辑器为:Typora (用了多款编辑器,觉 ...

  3. 深入了解promise

    1. Promise基础 什么是回调地狱? 当使用回调函数来进行事件处理的时候,如果嵌套多层回调函数的时候,就会出现回调地狱,例如: method1(function(err, result) { i ...

  4. K8s 资源配额管理对象 ResourcesQuota

    Kubernetes 是一个多租户平台,更是一个镜像集群管理工具.一个 Kubernetes 集群中的资源一般是由多个团队共享的,这时候经常要考虑的是如何对这个整体资源进行分配.在 kubernete ...

  5. python使用range()函数创建数字列表list

    #!/usr/bin/python #coding=utf-8 #好好学习,天天向上 numbers=list(range(1,6)) print(numbers) range指定步长: #!/usr ...

  6. ideal 创建maven 项目

    一 准备工作,已经配置好了maven 环境 .没有的话,参考我的上一篇笔记. 二,ideal相关配置 打开ideal  找到设置. file  ------->setting  .     点击 ...

  7. Spring源码-IOC部分-Xml Bean解析注册过程【3】

    实验环境:spring-framework-5.0.2.jdk8.gradle4.3.1 Spring源码-IOC部分-容器简介[1] Spring源码-IOC部分-容器初始化过程[2] Spring ...

  8. 后缀自动机 (SAM)

    后缀自动机 定义 定义 SAM 为一个有限状态自动机,接受且仅接受 \(S\) 的一个后缀. 同时,SAM 是这样的自动机中最小的那个,其中状态数至多为 \(2n - 1\),转移数至多为 \(3n ...

  9. JAVA中获取不同系统的换行符和获取系统路径路径分割符

    简介 JAVA具有多平台特征,一次开发,多平台运行,主要依据JVM,但是不同的操作系统中换行符和分割符不同,故需要根据不同的操作系统去获取不同的符号. JAVA代码 @Test public void ...

  10. Swift逻辑分支

    一. 分支的介绍 分支即if/switch/三目运算符等判断语句 通过分支语句可以控制程序的执行流程 二. if分支语句 和OC中if语句有一定的区别 判断句可以不加() 在Swift的判断句中必须有 ...