每一个Java 开发人员都知道字节码由JRE (Java运行时环境)执行。但许多人不知道JRE是Java虚拟机(JVM)的实现, 它负责分析字节码、解析并执行代码。作为一个开发人员了解JVM架构是非常重要的,因为它使我们能更高效的编写代码。在这篇文章中我们将更深入了解Java中的JVM架构以及JVM的各个组件。
JVM是什么?
虚拟机 是物理机器的一个软件实现。Java运行在VM上,实现WORA (一处编写,处处运行)。 编译器将Java文件编译成Java .class 文件,然后这个.class文件被输入到JVM中进行类文件的加载和执行。下面是一个JVM的架构图。
JVM是如何工作的呢?
正如上面的架构图所示,JVM被分为三个主要的子系统:
-
类加载器子系统
-
运行时数据区
-
执行引擎
1. 类加载器子系统
Java的动态类加载功能是由类加载器子系统处理。当它在运行时(不是编译时)首次引用一个类时,它加载、链接并初始化该类文件。
1.1 加载
类由此组件加载。启动类加载器 (Boot Strap class Loader)、扩展类加载器(Extension class Loader)和应用程序类加载器(Application class Loader) 这三种类加载器帮助完成类的加载。
-
启动类加载器 – 负责从启动类路径中加载类,无非就是rt.jar。这个加载器会被赋予最高优先级。
-
扩展类加载器 – 负责加载ext 目录(jre\lib)内的类.
-
应用程序类加载器 – 负责加载应用程序级别类路径,涉及到路径的环境变量等etc.
上述的类加载器会遵循委托层次算法(Delegation Hierarchy Algorithm)加载类文件。
1.2 链接
-
校验 – 字节码校验器会校验生成的字节码是否正确,如果校验失败,我们会得到校验错误。
-
准备 – 分配内存并初始化默认值给所有的静态变量。
-
解析 – 所有符号内存引用被方法区(Method Area)的原始引用所替代。
1.3 初始化
这是类加载的最后阶段,这里所有的静态变量会被赋初始值, 并且静态块将被执行。
2. 运行时数据区(Runtime Data Area)
运行时数据区域被划分为5个主要组件:
-
方法区(Method Area) – 所有类级别数据将被存储在这里,包括静态变量。每个JVM只有一个方法区,它是一个共享的资源。
-
堆区(Heap Area) – 所有的对象和它们相应的实例变量以及数组将被存储在这里。每个JVM同样只有一个堆区。由于方法区和堆区的内存由多个线程共享,所以存储的数据不是线程安全的。
-
栈区(Stack Area) – 对每个线程会单独创建一个运行时栈。对每个函数呼叫会在栈内存生成一个栈帧(Stack Frame)。所有的局部变量将在栈内存中创建。栈区是线程安全的,因为它不是一个共享资源。栈帧被分为三个子实体:
1.局部变量数组 – 包含多少个与方法相关的局部变量并且相应的值将被存储在这里。
2.操作数栈 – 如果需要执行任何中间操作,操作数栈作为运行时工作区去执行指令。
3.帧数据 – 方法的所有符号都保存在这里。在任意异常的情况下,catch块的信息将会被保存在帧数据里面。
4.PC寄存器 – 每个线程都有一个单独的PC寄存器来保存当前执行指令的地址,一旦该指令被执行,pc寄存器会被更新至下条指令的地址。
5. 本地方法栈 – 本地方法栈保存本地方法信息。对每一个线程,将创建一个单独的本地方法栈。
3. 执行引擎
分配给运行时数据区的字节码将由执行引擎执行。执行引擎读取字节码并逐段执行。
-
解释器 – 解释器能快速的解释字节码,但执行却很慢。 解释器的缺点就是,当一个方法被调用多次,每次都需要重新解释。
-
JIT 编译器 – JIT编译器消除了解释器的缺点。执行引擎利用解释器转换字节码,但如果是重复的代码则使用JIT编译器将全部字节码编译成本机代码。本机代码将直接用于重复的方法调用,这提高了系统的性能。
1.中间代码生成器 – 生成中间代码
2.代码优化器 – 负责优化上面生成的中间代码
3.目标代码生成器 – 负责生成机器代码或本机代码
4.探测器(Profiler) – 一个特殊的组件,负责寻找被多次调用的方法。
3.垃圾回收器: 收集并删除未引用的对象。可以通过调用"System.gc()"来触发垃圾回收,但并不保证会确实进行垃圾回收。JVM的垃圾回收只收集哪些由new关键字创建的对象。所以,如果不是用new创建的对象,你可以使用finalize函数来执行清理。
如何才能成为一个公司的顶梁柱般架构师呢?
基本知识
1.学会分析源码
程序员每天都和代码打交道。经过数年的基础教育和职业培训,大部分程序员都会「写」代码,或者至少会抄代码和改代码。但是,会读代码的并不在多数,会读代码又真正读懂一些大项目的源码的,少之又少。这种怪状,真要追究起来,怪不得程序员这个群体本身 —— 它是两个原因造成的:
-
我们所有的教育和培训都在强调怎么写代码,并没有教大家如何读代码
-
大多数工作场景都是一个萝卜一个坑,我们只需要了解一个系统的局部便能开展工作,读不相干的代码,似乎没用
读源码三问:“为什么要有这样的架构”,“他是什么样子的”,“他是怎么工作的”。
那么阿里程序员是如何去读代码的呢?
2.分布式架构特点及设计理念
首先需要说明的是,分布式系统是一个复杂且宽泛的研究领域,学习一两门在线课程,看一两本书可能都是不能完全覆盖其所有内容的。介于这篇文章是引导初学者入门,所以我个人觉得为初学者介绍一下当前分布式系统领域的全貌,也许比直接推荐论文和课程更有帮助。当初学者对这个领域建立起一个大的 Picture 之后,可以根据自己的兴趣,有选择性的深入不同领域进行进一步的学习。
3.为什么微服务会这么火?
要学习微服务,首先,我们要了解为什么使用微服务。
-
代码难以理解?
-
构建和部署耗时长,难以定位问题,开发效率低?
-
单体只能按整体横向扩展,无法分模块垂直扩展?
-
一个bug有可能引起整个应用的崩溃?
-
受技术栈限制,团队成员使用同一框架和语言?
那么如何解决单体的不足呢,通过迁移到微服务架构来解决,我们看一下什么是微服务。
微服务架构:将单体应用拆分为多个高内聚低耦合的小型服务,每个小服务运行在独立进程,由不同的团队开发和维护,服务间采用轻量级通信机制,独立自动部署,可以采用不同的语言及存储。
单体架构整个团队维护开发一个大工程及一个单库,到了微服务架构,用户请求经过API Gateway被路由到下游服务,服务之间以轻量级通信协议进行通信,服务通过注册中心发现彼此,每个服务都有专门的开发维护团队,每个服务对应独立的数据库,服务独立开发,独立部署和上线。
接下来我们总结下微服务的优点。
-
易于开发与维护
-
微服务相对小,易于理解
-
启动时间短,开发效率高
-
独立部署
-
一个微服务的修改不需要协调其它服务
-
伸缩性强
-
每个服务都可以在横向和纵向上扩展
-
每个服务都可按硬件资源的需求进行独立扩容
-
与组织结构相匹配
-
微服务架构可以更好将架构和组织相匹配
-
每个团队独立负责某些服务,获得更高的生产力
-
技术异构性
-
使用最适合该服务的技术
-
降低尝试新技术的成本
下面就送上学习架构图吧
如果你觉得想提升下自己,学习文章中的知识,在此推荐一个免费公开课的地方,可以加群: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++程序员需要学体系结构与编译原理?”
话不多说,附上学习体系图
5.被我们忽略掉的工程化专题
IT产业行业细分化已经不是一天两天的事了。集成技术这件事并不可耻可笑,反而是另一种可贵的能力。并不是像一些人形容的那样,好像批发几个CPU,拿到华强北就能把自己的电脑改装成超级计算机了。
那么,为什么我们常常会忽略掉工程化这件事的价值呢?主要的原因,或许是因为工程化这件事本身就离我们太远。一个产业工程化的普遍性越高,说明这个产业发展的越成熟:产业链细分、分工细化、全球化的研发和生产这些高效的工作方式开始出现。而产业成熟也往往代表着寡头化情况显著。
在IT产业中,寡头化出现代表着创业公司减少——没人再去用声势浩大的发布会讲故事、没人再去宣传自己拿了多少融资。
这一代中国人自小的教育不比欧美的STEAM,而是重学术、轻手艺。我们往往会为工科和产能过剩画上等号。强大的资本和技术门槛为这些产业蒙上了一层神秘的面纱,让普通人很难真正了解到其中技术和工艺的复杂程度,也就更难明白其中的价值。可正是因为中国的工程化能力,才让我们有机会走到AI时代的第一梯队,而不仅仅是靠学术研究能力。
另外一个原因,或许在于我们天生“叛逆心”。超级计算机、手机芯片等等技术门槛较高的产业,其背后往往是大企业和国资科研机构。当评判的对象是他们时,我们似乎更愿意相信狗血的商业故事和阴谋论:比如科研经费都被教授们吃吃喝喝啦;搞超级计算机就是放卫星其实美日根本不care啦;XX企业的技术都是从创业公司买来的除了会赚用户的钱啥技术都没有……
产生这种“叛逆心”的原因太深刻,我们能做到的,只有在这种“惯性思维”出现时先按住自己奔向键盘的手,转表达欲为好奇心,完成自己了解的义务,再去行使自己批判的权利。
附上思维脑图
6.没有高并发经验,想进大公司该怎么办?
假如没有靠谱的公司,接触不到高并发的业务场景怎么办?你永远解决的是小问题,工作10年技术也未必提升多少。
很多程序员也经常找我说,没有经验就没有靠谱的公司收,没有靠谱的公司也就没有经验,我看了无数的书,自己做了无数的实验拼命想找个靠谱公司去深入,但是感觉好难,简直是个死循环
读者群的朋友大家都比较关注高并发,原因很简单,想去BAT这样的大公司,你必须要有高并发的经验。今天普及下高并发的知识,希望大家对高并发有一个正确的认识。
7.学习千遍,不如项目实战成功一次
我们在学习过程中最容易犯的一个错误就是:看的多,动手的少。特别是对一些项目的整体开发,我们接触的机会就更少了。
一次完整的开发,是最好的学习。它能让你对整个开发流程有完整的认识,对知识也会有极大的巩固。更重要的是,你将学会将理论知识用到实际开发中的方法。
所以无论项目大小,一定要动手去进行开发学习。
项目实战相信很多程序员都多少会有的,可是我们这个还要学习什么呢?
那就要看你想不想成为一个架构师了,为什么98%的程序员工作10年,一辈子还只是一个开发者。程序员们都要想一想这个问题,我是不是需要提升了。
我认为,学习项目实战最重要的还是学习项目管理,作为程序员,都应该学点项目管理。
-
凡事皆为“项目”
-
项目的两类属性(复杂的逻辑,庞大的信息量)
-
人脑擅长的是思考,而不是记忆
-
成为一个“独当一面”的人
独当一面是一个很性感的词。是否拥有它,对应的职场价值,有着天壤之别的。
所有老板都喜欢“独当一面”的员工,因为这是最省心力、最好算账的模式:给你一块资源,给你一个 title,给你一个目标,然后你给我打出一片天地来。
当你能独立对一摊子事情负责,并把它们一一搞定,你会拥有大幅度的职场溢价——相应的,其收入回报,也远非“技术螺丝”可比了。
如果你很进取,你会逐渐地:主导一个小组,一个部门,一个家庭,甚至还是城市……而这所有的一切起点,正是独立完整地做好一个项目:你没有谁可以依靠,你要对其中大大小小的事务负责,你要对最后的结果。
换句话说,“项目管理”是“独当一面”的元能力。在这个过程中,你的意识越发清晰,你的方法论越发成熟,你的信心更加沛,项目越做越大。直到某天,你真的有了掌控一方的封疆大吏。
这就是我们学习“项目实战”的终极意义。
或许作为程序员的你想提升自己,却找不到突破口,公司没人带。又或许你已经工作6年了,却还是很迷茫,很多知识都还是不懂,也没有达到自己期望的一个职位,薪资。在此推荐一个免费公开课的地方,上面所提到的架构师基本知识点都有资料,可以加群:433540541,找群主获取上课资格,这是免费的课程,找群主要的时候可以客气一点。
到这里,你可能认为文章已经完了,学完这些就可以去BAT大公司做一个架构师,年薪50W+吗?
不,你错了,这些都知识最基本的知识,想要成为一个架构师必须是一个累积的过程,也是这么多程序员终其一生也只是一个开发,到年龄就会被公司辞退。