原 架构师视角:对JVM架构进行解析

每一个Java 开发人员都知道字节码由JRE (Java运行时环境)执行。但许多人不知道JRE是Java虚拟机(JVM)的实现, 它负责分析字节码、解析并执行代码。作为一个开发人员了解JVM架构是非常重要的,因为它使我们能更高效的编写代码。在这篇文章中我们将更深入了解Java中的JVM架构以及JVM的各个组件。

JVM是什么?

虚拟机 是物理机器的一个软件实现。Java运行在VM上,实现WORA (一处编写,处处运行)。 编译器将Java文件编译成Java .class 文件,然后这个.class文件被输入到JVM中进行类文件的加载和执行。下面是一个JVM的架构图。

原 架构师视角:对JVM架构进行解析

JVM是如何工作的呢?

正如上面的架构图所示,JVM被分为三个主要的子系统:

  1. 类加载器子系统

  2. 运行时数据区

  3. 执行引擎

1. 类加载器子系统

Java的动态类加载功能是由类加载器子系统处理。当它在运行时(不是编译时)首次引用一个类时,它加载、链接并初始化该类文件。

1.1 加载

类由此组件加载。启动类加载器 (Boot Strap class Loader)、扩展类加载器(Extension class Loader)和应用程序类加载器(Application class Loader) 这三种类加载器帮助完成类的加载。

  1. 启动类加载器 – 负责从启动类路径中加载类,无非就是rt.jar。这个加载器会被赋予最高优先级。

  2. 扩展类加载器 – 负责加载ext 目录(jre\lib)内的类.

  3. 应用程序类加载器 – 负责加载应用程序级别类路径,涉及到路径的环境变量等etc.

上述的类加载器会遵循委托层次算法(Delegation Hierarchy Algorithm)加载类文件。

1.2 链接

  1. 校验 – 字节码校验器会校验生成的字节码是否正确,如果校验失败,我们会得到校验错误。

  2. 准备 – 分配内存并初始化默认值给所有的静态变量。

  3. 解析 – 所有符号内存引用被方法区(Method Area)的原始引用所替代。

1.3 初始化

这是类加载的最后阶段,这里所有的静态变量会被赋初始值, 并且静态块将被执行。

2. 运行时数据区(Runtime Data Area)

运行时数据区域被划分为5个主要组件:

  1. 方法区(Method Area) – 所有类级别数据将被存储在这里,包括静态变量。每个JVM只有一个方法区,它是一个共享的资源。

  2. 堆区(Heap Area) – 所有的对象和它们相应的实例变量以及数组将被存储在这里。每个JVM同样只有一个堆区。由于方法区和堆区的内存由多个线程共享,所以存储的数据不是线程安全的。

  3. 栈区(Stack Area) – 对每个线程会单独创建一个运行时栈。对每个函数呼叫会在栈内存生成一个栈帧(Stack Frame)。所有的局部变量将在栈内存中创建。栈区是线程安全的,因为它不是一个共享资源。栈帧被分为三个子实体:

    1.局部变量数组 – 包含多少个与方法相关的局部变量并且相应的值将被存储在这里。

    2.操作数栈 – 如果需要执行任何中间操作,操作数栈作为运行时工作区去执行指令。

    3.帧数据 – 方法的所有符号都保存在这里。在任意异常的情况下,catch块的信息将会被保存在帧数据里面。

    4.PC寄存器 – 每个线程都有一个单独的PC寄存器来保存当前执行指令的地址,一旦该指令被执行,pc寄存器会被更新至下条指令的地址。

    5. 本地方法栈 – 本地方法栈保存本地方法信息。对每一个线程,将创建一个单独的本地方法栈。

3. 执行引擎

分配给运行时数据区的字节码将由执行引擎执行。执行引擎读取字节码并逐段执行。

  1. 解释器 – 解释器能快速的解释字节码,但执行却很慢。 解释器的缺点就是,当一个方法被调用多次,每次都需要重新解释。

  2. JIT 编译器 – JIT编译器消除了解释器的缺点。执行引擎利用解释器转换字节码,但如果是重复的代码则使用JIT编译器将全部字节码编译成本机代码。本机代码将直接用于重复的方法调用,这提高了系统的性能。

1.中间代码生成器 – 生成中间代码

2.代码优化器 – 负责优化上面生成的中间代码

3.目标代码生成器 – 负责生成机器代码或本机代码

4.探测器(Profiler) – 一个特殊的组件,负责寻找被多次调用的方法。

3.垃圾回收器: 收集并删除未引用的对象。可以通过调用"System.gc()"来触发垃圾回收,但并不保证会确实进行垃圾回收。JVM的垃圾回收只收集哪些由new关键字创建的对象。所以,如果不是用new创建的对象,你可以使用finalize函数来执行清理。

如何才能成为一个公司的顶梁柱般架构师呢?

基本知识

1.学会分析源码

程序员每天都和代码打交道。经过数年的基础教育和职业培训,大部分程序员都会「写」代码,或者至少会抄代码和改代码。但是,会读代码的并不在多数,会读代码又真正读懂一些大项目的源码的,少之又少。这种怪状,真要追究起来,怪不得程序员这个群体本身 —— 它是两个原因造成的:

  • 我们所有的教育和培训都在强调怎么写代码,并没有教大家如何读代码

  • 大多数工作场景都是一个萝卜一个坑,我们只需要了解一个系统的局部便能开展工作,读不相干的代码,似乎没用

读源码三问:“为什么要有这样的架构”,“他是什么样子的”,“他是怎么工作的”。

那么阿里程序员是如何去读代码的呢?

原 架构师视角:对JVM架构进行解析

2.分布式架构特点及设计理念

首先需要说明的是,分布式系统是一个复杂且宽泛的研究领域,学习一两门在线课程,看一两本书可能都是不能完全覆盖其所有内容的。介于这篇文章是引导初学者入门,所以我个人觉得为初学者介绍一下当前分布式系统领域的全貌,也许比直接推荐论文和课程更有帮助。当初学者对这个领域建立起一个大的 Picture 之后,可以根据自己的兴趣,有选择性的深入不同领域进行进一步的学习。

原 架构师视角:对JVM架构进行解析

3.为什么微服务会这么火?

要学习微服务,首先,我们要了解为什么使用微服务。

  • 代码难以理解?

  • 构建和部署耗时长,难以定位问题,开发效率低?

  • 单体只能按整体横向扩展,无法分模块垂直扩展?

  • 一个bug有可能引起整个应用的崩溃?

  • 受技术栈限制,团队成员使用同一框架和语言?

那么如何解决单体的不足呢,通过迁移到微服务架构来解决,我们看一下什么是微服务。

微服务架构:将单体应用拆分为多个高内聚低耦合的小型服务,每个小服务运行在独立进程,由不同的团队开发和维护,服务间采用轻量级通信机制,独立自动部署,可以采用不同的语言及存储。

单体架构整个团队维护开发一个大工程及一个单库,到了微服务架构,用户请求经过API Gateway被路由到下游服务,服务之间以轻量级通信协议进行通信,服务通过注册中心发现彼此,每个服务都有专门的开发维护团队,每个服务对应独立的数据库,服务独立开发,独立部署和上线。

接下来我们总结下微服务的优点。

  • 易于开发与维护

  • 微服务相对小,易于理解

  • 启动时间短,开发效率高

  • 独立部署

  • 一个微服务的修改不需要协调其它服务

  • 伸缩性强

  • 每个服务都可以在横向和纵向上扩展

  • 每个服务都可按硬件资源的需求进行独立扩容

  • 与组织结构相匹配

  • 微服务架构可以更好将架构和组织相匹配

  • 每个团队独立负责某些服务,获得更高的生产力

  • 技术异构性

  • 使用最适合该服务的技术

  • 降低尝试新技术的成本

下面就送上学习架构图吧

原 架构师视角:对JVM架构进行解析

如果你觉得想提升下自己,学习文章中的知识,在此推荐一个免费公开课的地方,可以加群:433540541,找群主获取上课资格,这是免费的课程,找群主要的时候可以客气一点。

4.程序员到底要不要学习JVM

总有人问这个东西好像用不上,于是要不要学这样的问题。

然后又总有人担心一直搬砖成天做些重复没提升的东西。

如果你这辈子只甘心做一个平庸的Java码农,那么你完全没有必要去学习JVM相关的知识,学习JVM对于一个Java程序员的好处大概可以概括为下几点:

  • 1.你能够明白为什么Java最早期被称为解释型语言,而后来为什么又被大家叫做解释与编译并存的语言(了解JVM中解释器以及即时编译器就可以回答这个问题);

  • 2.你能够理解动态编译与静态编译的区别,以及动态编译相对于静态编译到底有什么好处(JVM JIT);

  • 3.你能够利用一些工具,jmap, jvisualvm, jstat, jconsole等工具可以辅助你观察Java应用在运行时堆的布局情况,由此你可以通过调整JVM相关参数提高Java应用的性能;

  • 4.可以清楚知道Java程序是如何执行的;

  • 5.可以明白为什么Java等高级语言具有可移植性强的特性。

其实这个问题相当于“为什么C/C++程序员需要学体系结构与编译原理?”

话不多说,附上学习体系图

原 架构师视角:对JVM架构进行解析

5.被我们忽略掉的工程化专题

IT产业行业细分化已经不是一天两天的事了。集成技术这件事并不可耻可笑,反而是另一种可贵的能力。并不是像一些人形容的那样,好像批发几个CPU,拿到华强北就能把自己的电脑改装成超级计算机了。

那么,为什么我们常常会忽略掉工程化这件事的价值呢?主要的原因,或许是因为工程化这件事本身就离我们太远。一个产业工程化的普遍性越高,说明这个产业发展的越成熟:产业链细分、分工细化、全球化的研发和生产这些高效的工作方式开始出现。而产业成熟也往往代表着寡头化情况显著。

在IT产业中,寡头化出现代表着创业公司减少——没人再去用声势浩大的发布会讲故事、没人再去宣传自己拿了多少融资。

这一代中国人自小的教育不比欧美的STEAM,而是重学术、轻手艺。我们往往会为工科和产能过剩画上等号。强大的资本和技术门槛为这些产业蒙上了一层神秘的面纱,让普通人很难真正了解到其中技术和工艺的复杂程度,也就更难明白其中的价值。可正是因为中国的工程化能力,才让我们有机会走到AI时代的第一梯队,而不仅仅是靠学术研究能力。

另外一个原因,或许在于我们天生“叛逆心”。超级计算机、手机芯片等等技术门槛较高的产业,其背后往往是大企业和国资科研机构。当评判的对象是他们时,我们似乎更愿意相信狗血的商业故事和阴谋论:比如科研经费都被教授们吃吃喝喝啦;搞超级计算机就是放卫星其实美日根本不care啦;XX企业的技术都是从创业公司买来的除了会赚用户的钱啥技术都没有……

产生这种“叛逆心”的原因太深刻,我们能做到的,只有在这种“惯性思维”出现时先按住自己奔向键盘的手,转表达欲为好奇心,完成自己了解的义务,再去行使自己批判的权利。

附上思维脑图

原 架构师视角:对JVM架构进行解析

6.没有高并发经验,想进大公司该怎么办?

假如没有靠谱的公司,接触不到高并发的业务场景怎么办?你永远解决的是小问题,工作10年技术也未必提升多少。

很多程序员也经常找我说,没有经验就没有靠谱的公司收,没有靠谱的公司也就没有经验,我看了无数的书,自己做了无数的实验拼命想找个靠谱公司去深入,但是感觉好难,简直是个死循环

读者群的朋友大家都比较关注高并发,原因很简单,想去BAT这样的大公司,你必须要有高并发的经验。今天普及下高并发的知识,希望大家对高并发有一个正确的认识。

原 架构师视角:对JVM架构进行解析

7.学习千遍,不如项目实战成功一次

我们在学习过程中最容易犯的一个错误就是:看的多,动手的少。特别是对一些项目的整体开发,我们接触的机会就更少了。

一次完整的开发,是最好的学习。它能让你对整个开发流程有完整的认识,对知识也会有极大的巩固。更重要的是,你将学会将理论知识用到实际开发中的方法。

所以无论项目大小,一定要动手去进行开发学习。

项目实战相信很多程序员都多少会有的,可是我们这个还要学习什么呢?

那就要看你想不想成为一个架构师了,为什么98%的程序员工作10年,一辈子还只是一个开发者。程序员们都要想一想这个问题,我是不是需要提升了。

我认为,学习项目实战最重要的还是学习项目管理,作为程序员,都应该学点项目管理。

  1. 凡事皆为“项目”

  2. 项目的两类属性(复杂的逻辑,庞大的信息量)

  3. 人脑擅长的是思考,而不是记忆

  4. 成为一个“独当一面”的人

独当一面是一个很性感的词。是否拥有它,对应的职场价值,有着天壤之别的。

所有老板都喜欢“独当一面”的员工,因为这是最省心力、最好算账的模式:给你一块资源,给你一个 title,给你一个目标,然后你给我打出一片天地来。

当你能独立对一摊子事情负责,并把它们一一搞定,你会拥有大幅度的职场溢价——相应的,其收入回报,也远非“技术螺丝”可比了。

如果你很进取,你会逐渐地:主导一个小组,一个部门,一个家庭,甚至还是城市……而这所有的一切起点,正是独立完整地做好一个项目:你没有谁可以依靠,你要对其中大大小小的事务负责,你要对最后的结果。

换句话说,“项目管理”是“独当一面”的元能力。在这个过程中,你的意识越发清晰,你的方法论越发成熟,你的信心更加沛,项目越做越大。直到某天,你真的有了掌控一方的封疆大吏。

这就是我们学习“项目实战”的终极意义。

原 架构师视角:对JVM架构进行解析

或许作为程序员的你想提升自己,却找不到突破口,公司没人带。又或许你已经工作6年了,却还是很迷茫,很多知识都还是不懂,也没有达到自己期望的一个职位,薪资。在此推荐一个免费公开课的地方,上面所提到的架构师基本知识点都有资料,可以加群:433540541,找群主获取上课资格,这是免费的课程,找群主要的时候可以客气一点。

到这里,你可能认为文章已经完了,学完这些就可以去BAT大公司做一个架构师,年薪50W+吗?

不,你错了,这些都知识最基本的知识,想要成为一个架构师必须是一个累积的过程,也是这么多程序员终其一生也只是一个开发,到年龄就会被公司辞退。