原文:故事:走进JVM的世界(图文并茂)注意!本文较长,建议阅读前收藏。更多文章请关注作者公众号:实战代码也可以star。这篇文章在我的GitHub上的仓库:https://github.com/flyhero/Ma...说明:本文基于Java8Hotspot64位操作系统思路下,小强是一名开发工程师,拥有工作3年多。从他的发量就可以看出,小强的资历还比较年轻。最近公司没什么动静,他开始有些烦了。今天下午,同事们都在热烈讨论业务,他却没有参与,于是决定学点东西,百无聊赖的翻着各种网页,发现JVM是高手们推荐的知识,于是决定好好学习一下看。.5分钟后……小强觉得这知识有点枯燥,难怪高手们都能看懂!又看了几分钟,小强累了,揉了揉睡眼。然而就在这时,他忽然发现,周围同事们议论纷纷的声音已经听不见了,安静的不得了。1.进入世界小强努力睁开眼睛,发现自己在一片空白中,吓了一跳,心想我这是怎么了,我穿越了吗?却要穿越到人间风景如画的地方,美人如云……,这情况……突然一个白胡子老者走到了他的面前。小强刚要说话,老爷子抢先一步:你好,我是这个JVM世界的Creator,你可以叫我“HotSpot”,不过没关系,因为我创造的世界是按照“JVM规范”。休息的时候,发现来了客人,原来是你。小强:我想问……老头:不用问,我知道,你想知道我创造的世界,对吧?跟我来。这老头,我还没说话,这下完了!好吧,让我们看看并告诉你。老人边走边说:JVM的世界空间是有限的,我们坚持一个原则:各司其职,不留无用之人!小强:啊!好残酷。老人:不,这不残忍。我们的世界,为客户服务而生,为客户闪耀。每个人都奉献自己的能力圆满完成任务。退出舞台是唯一正确的,这是他们最好的。目的地。小强:对,这样世界就不会那么拥挤,我们可以有条不紊地工作。为什么我这么无知……2、我们来看看我们这个世界的整体构成,中心区域是怎么布局的。下面来看看我们日常的主要工作区(运行时数据区)。为了使我们的工作更有效率,我们将世界空间划分为这些部分。居住区-Heap这里是人们工作之余居住的居住区。居住区又根据人的年龄划分为伊甸区、幸存者区、老年区。Workspace-stack当每一个任务到来时,都会在工作空间开辟出一个单独的地方来完成这个任务。Recorder-ProgramCounter由于我们同时可以做的任务是有限的,所以我们需要为不同的任务划分不同的时间片。当我们切换任务时,我们需要一个记录器,它可以记录我们在哪里完成了这个任务。Next下次回来还可以继续做。仓库管理区-方法区存放工人模板和常用常量工具。3.生与死。在这里工作的人都会经历生死。大多数人不会活到老年,但这并不重要。重要的是他为我们做出了贡献。3.1天生老人:这里每个人都有一个模板(类),看到那个在小区里休息的高个子了吗?他叫张三,是根据外部客户给的模板“用户类”创建的。他是客户最喜欢的工人。你知道这些客户模板(类别)是如何进入我们的世界的吗?小强:这个我知道一点,以前看过一点。这个过程还是有点复杂。客户的模板(类)通过翻译工厂(编译器)翻译成类字节码,因为你的世界只知道字节码,然后你的加载系统会把它们加载到这里。加载过程有以下几个阶段:加载阶段由加载器完成。老头:对,我们提供三个工厂,启动类加载器,扩展类加载器,应用类加载器。当然,客户也可以定制装载机。小强:他们走的是家长委托的模式,但我一直不太明白这个词!老人:这是你语言翻译的问题。这种模式叫做“家长委托”,你懂的!这意味着让你的父母为你做这件事。小强:家长委托模式有什么好处?老头:优先级的关系可以避免重复加载模板(类)。安全考虑可能会阻止Java核心API被替换。老人继续说:你知道连接过程的三个步骤是什么吗?小强:具体我就不知道了……老头笑道:我们不是一直对谁来客户定义的模板(类)开放的。为了我们世界的安全和提供更好的服务,我们会在模板上做一些验证和后续操作。校验包括格式校验、元数据校验、字节码校验、符号校验。当验证通过后,我们为模板依赖的东西(类变量)分配空间,最后用直接引用代替符号引用。老爷子看着小强,皱了皱眉,继续补充道:你可能不明白什么是符号引用,什么是直接引用!符号引用是指在编译时,我们不知道模板(类)所依赖的其他东西会在我们空间的什么地方,只能用符号来表示。直接引用是指所有东西都加载到这里后,就会有自己的真实空间地址,然后取代符号引用。这样运行时就可以找到它们所依赖的东西。最后是初始化。这个阶段主要是初始化类变量,也就是执行类构造函数的过程。小强:我怎么没看到这些模板?老头:我把这些模板藏在世界的后面,一般人看不到,统称为Klass。小强:不行!你错了吗?不应该叫Class吗?老头:哈哈!刚才说了,一般人是看不出来的,你就是其中之一!平时看到的Class只是对Klass的封装,真正记录模板中具体元信息的是Klass。记住这一次,年轻人。3.2工人小强:为什么你们的工人一样高?老头:你观察的真仔细!是的,他们确实是平等和贫穷的。要想知道为什么,首先要了解这些工人是由哪些部分组成的。他们头的大小是固定的,身体的大小由自己的属性数据决定,最后的脚由我决定。如果前两个数据的大小没有达到8的倍数,那我就补上,所以这里的padding让它们有相同的高差(内存对齐)。我创建它们基于两个原因:平台原因:并非所有硬件平台都可以访问任何地址的任何数据;有些硬件平台只能在特定地址获取特定类型的数据,否则会抛出硬件异常。性能原因:中央大脑(CPU)以内存访问粒度访问内存,即每次内存访问的长度是固定的。如果不这样做,中央大脑需要访问内存两次,但在对齐后只需要访问一次。小强:嗯,我明白了!那你能告诉我为什么这些工人一直在居民区搬迁吗?3.3长寿老人:经过长时间的观察,我发现每个工人的寿命长短是不一样的。所以我把居民区分成新生代和老年代,然后让他们合理移动,这样可以有效利用空间,让垃圾小分队的工作更有效率。工人出生后,会被分配到伊甸区。当伊甸区快满的时候,垃圾小分队就会过来清理。如果清洁后工人还活着,他们将转移到其中一个幸存者区域。当幸存者区快满时,垃圾小分队会将幸存的工人转移到另一个幸存者区,如此循环,每次垃圾小分队清扫,存活的工人都会成长一年,直到工人达到15岁,达到之后,他们将搬迁到老一辈居住的地方。但也有例外。如果一个worker吃多了,newgeneration容不下他,他就直接到oldgeneration去住。当老年代快满的时候,就会进行一次垃圾清理(fullgc)。小强:原来如此!从此,我不再是只知道栈区的菜鸟了!哈哈哈哈……老头:年轻人,别高兴得太早!到目前为止,您所知道的仍然是九牛一毛。3.4死亡证明小强:如何判断工人是否到了生命尽头?第一种方法:引用计数法为每个worker添加一个引用计数器,即只要有人需要worker的帮助,那么这个worker的计数就加1,否则,如果其他人不再需要这个worker的帮助,那么这个计数会减1,直到计数为0,表示worker的生命走到了尽头。但是这种做法有一个问题:如果workerA和workerB需要彼此的帮助,但是没有其他worker或task同时需要他们,那么他们两个都会永远活着!所以我们不会采用这种方法。第二种方法:可达性分析法。我们以找到称为“GCroots”的worker为起点,然后依次寻找他们在工作中依赖的worker,这样我们就可以知道哪些worker是不必要存在的。小强:我怎么知道哪些worker是“GCroots”?老头:工作区(栈)仓库(方法区)需要worker模板(类)本身需要worker(static,constant)后台需要worker(nativemethod)小强:明白了!4.回收老人:带你认识一下垃圾小队的角色!但在了解它们之前,您最好了解清除垃圾的基本方法。4.1基本方法垃圾回收遵循的基本方法如下:mark-sweep先标记所有需要回收的worker(对象),标记完成后统一回收所有标记的worker。但这有两个缺点:1、效率不高。2.碎片会很多。空间复制将可用空间一分为二,并且一次只使用其中一个。工人们搬迁到另一个街区。虽然这解决了标记-清除效率问题,但这种方法将空间减少了一半。标记排序首先标记所有需要回收的工人(对象),然后将存活的工人移动到空间的一端,然后清理边界外的工人。小强笑道:原来是这三个算法!我知道!老头:知道了,就让我认识一下垃圾清扫队的人吧!4.2主要成员垃圾清扫队由若干小组组成。客户可以指定他们喜欢的团队让他们工作。每个团队的清洁方法都不同,各有优缺点。让我向您介绍两个主要成员,CMS和G1。CMS:是的,我们是CMS团队。全称是“ConcurrentMarkSweep”。顾名思义,我们是一个使用mark-and-sweep算法的并发团队。我们的目标是获得最短的恢复暂停时间。小强:那你说说你是怎么工作的?CMS:我们主要分四个步骤,1.初始标记2.并发标记3.再标记4.并发清除小强:算了,这么多步骤太花时间去了解了,我现在知道你的优势了,那有什么你的弱点?CMS:这怎么还暴露人的伤疤……老头这时候重重的咳了两声,意思是CMS立马抓住了,委屈的说:我有3个缺点:资源不是很充足的时候,占用太多资源,导致任务变慢,无法处理浮动垃圾。我们打扫的时候,工人们也在同时工作。我们标记之后,一些工人就不再需要了。我们团队遵循的是“mark-clear”算法,所以会产生大量的碎片空间。导致worldcleaning(fullgc)早到的直言不讳的小强说:原来你的问题这么严重,老夫没有退出你的团队……CMS:你……我觉得我们的团队红极一时当年……所以我猜G1可以弥补CMS的不足?G1:说实话,我们组的目标是取代CMS组...(JDK14CMS正式告一段落)小强坏笑,哈哈...,CMS翻了个白眼躲在角落里偷偷难过。小强:说说你在G1的能力吧!G1:我们团队是基于标记排序的算法,所以不会有很多碎片空间。我们还引入了分区的思想,弱化了生成的概念。我们的停顿时间是可控的,我们也可以充分利用它来避免雪崩。客户给我们的资源,停顿时间的减少,是我们团队的优势。下面我就给大家详细介绍一下我们团队的情况……小强:好的!你继续……就在小强听得津津有味的时候,一只巨大的手突然出现在天空中朝他袭来。小强躲闪不及。看,靠,技术总监……你怎么来了?导演:我不在,我在哪?你在家睡觉吗!小强这才反应过来。原来他还在办公室,出事了!导演:小强,回家好爽,明天别用了!小强慌了,灵机一动:导演,你知道我刚才在做什么吗?那不是睡觉,我有一个故事让你在做决定之前听听。走吧……最后把本文的知识总结成思维导图:说明:[作者]:flyhero[公众号]:代码实战。【简介】:我想做一个有理论有实践的学校。【福利】:关注微信公众号,回复:“JVM”获取思维导图【转载】:转载请注明出处,谢谢合作!
