今天我们来讲一下阿迪尔这个变异报错的问题,主要是这个 g d k 版本的问题。首先我们来看一下,就是执行的时候,然后报的这个变异错误,就是这个 g d k 版本不对,然后怎么解决呢? 主要是这里要设置一下这个版本,这个版本是有问题的,不对的点到这里来,然后就可以看到这个加瓦这个变音器,这里这里这里这个版本是是七,然后把它改到八就可以了。 点应用我们再来试一下。好,现在可以就是变音通过,而且也执行成功了。最后我们来总结一下,就是除了刚才那个地方,可能是那个地方的那个解决一百分之问题,还有 另外一个地方就是右键项目,然后点这个 oppo 模块设置,然后找到这个模块,这里这里也有这个 gdk 版本,也有可能是这里有问题。 然后还有一个是这个项目,这里这个是这个版本,然后这个是这个版本,如果是出现这个阿迪尔里面,然后出现这个就这个版本的问题,就是这几个地方,看一下是不是你想要的那个版本,基本上就可以解决这个问题了。好,谢谢。
粉丝1772获赞2.8万


进阶加瓦价格师学习打卡第一天!第一天的学习我们先从 gvm 开始,只要是做加瓦开发,一定会安装 gdk, 那我 我们作为加瓦程序员,想要进阶提升,不仅要知道加瓦代码是运行在 gpm 上的,我们还要知道是怎么运行的,是怎么分配内存的,以及是怎么进行垃圾回收的。有了这些理论知识打底的话,我们就能更好的 学习街边某条优来提升系统的性能。那第一天的内容我们从街边某类加载机制开始,包括什么是类的加载, 类加载过程、类加载器以及双亲为派机智等等,这就是今天的学习内容了。那昭君建议大家在学习的过程中要养成记笔记的好习惯,俗话说好记性不如烂笔头,有了笔记也可以方便自己复习。 那完成了今天学习任务的同学啊,可以在评论区写下你的学习感想哦!

j y m 常用的参数解读, xms 是我们的初始最大小, xmx 是我的扩容的最大大小, xss 我们现成站的大小, xm n 以及这些个参数,那么就是我们的新代大小,然后新代的最大大小以及新代和老年代的比例,当然这个你千万不要动他,除非你发现了你这个应用朝生夕死要的比较多呢,你可以把 xm 调大或他比例调小,好吧, ok, 基本上我们调的最多的是这几个参数。好, 那么如果想在日子里面跟踪呢?那么呢跟踪我们的内卸载就可以加这两个跟踪啊参数,当然还有很多其他参数哎,然后大家可以扫一下我们的右上角的二维码啊,可以扫一下这个,拿一下这个资料,里面还有更多这些东西, 大家也可以通过 gvm 给到官方的这个手册,然后看到里面有些很多更多的这些参数,你就要看一下是什么意思啊。接下来我们来看了解了常用参数,了解了我们的须知概念,这个时候我们需要有些调约工具,对不对啊?

所谓调优,首先确定追求什么?吞吐量优先还是响应时间优先?吞吐量、用户代码时间、响应时间都越短,响应时间越少。什么是调优?一、跟 据需求进行 jvm 规划和预调优。二、优化运行 jvm 运行环境。三、解决 jvm 运行过程中出现的各种问题。常用命令分析工具。

本视频由彼岸教育大力支持,彼岸教育让全球优质资源触手可及。大家好,我是韩石。记得以前和几个人一起吃饭,聊到编程语言的话题, 其中一位老哥的技术战士 php, php 呢,确实有点局限,所以一位资深的大叔呢,劝他是否考虑转到加瓦,因为加瓦的生态很一流。然后呢,那位老兄就解释说自己也会加瓦,于是有人问他会哪些,他说,语法这些都会。 我觉得这位老哥对语言的理解有些停留在表面。如果把学习一门编程语言简单的分为三个层次的话,我觉得第一层是了解这门语言的语法写法,第二 层是了解这门语言区别于其他语言的优劣势以及他的生态,知道这门语言的能力范围。第三层是了解这门语言的底层运行机制,这样呢, 有利于对程序进行调优,以及当程序出现了一些比较罕见的问题时呢,能够从跟上去分析解决他。那么这里再多聊几句,无论是还在上学还是已经毕业的观众,如果想要继续学习深造,不妨关注一下彼岸教育的在线海外硕士。这里呢,推荐伊利诺伊理工大学的计算机科学硕士,有 人工智能、数据科学、信息安全、分布式语音计算、网络与通讯等五个方向。了解详情的可以点击下方弹窗或者简介中的链接。 好了,收回到家吧。家法最核心的呢,还是 jbm, jbm 属于上面所说的第三层,基于 jbm 的语言呢,有很多,比如 coty, luby 和斯旮旯等等,只要了解 jbm, 学习这些语言的都会变得很轻松,我觉得呢,学知识还是得学相对核心的 内容,那么这个系列我们就一起来深入了解一下。 jbm 这期视频呢,要聊的部分是类加载机制,这几天呢,在工作中也正好遇到了相关的需求, 简单来说呢,就是我在设计一个监控系统,针对不同的监控维度,需要计算出不同的告警事件,而监控的维度呢,是需要动态可配置的,可能会有成百上千条。相应的计算告警事件的逻辑呢,也是各有差异,也是需要做到动态可配置的。 所以这里就需要程序在运行时呢,动态的去编译和加载外部的加法文件。这一块呢,就涉及到了 jbm 的内加载机制。 于是呢,顺着这个油头,我们来深入理解一下相关的知识。首先我们需要了解一件事,那就是我们自己编写的加把代码是如何在各种各样的操作系统上运行起来的。这个知识点呢,相信大家都知道,加把文件通过加把 c 编译成 文件,这种中间码被称为字解码,然后由 gbm 加载字解码运行时呢,解释器将字解码解释为一行行机器码来执行。 在程序运行期间,及时编译器呢,会针对热点代码将该部分字节码编译成机器码,以获得更高的执行效率。在 整个运行时,解释器和及时变异器的相互配合,使加法程序几乎可以达到和变异型语言一样的执行速度。在上面这一段简短的描述中呢,充满了健身的技术难点,像变异器就交给专业的人去做,大部分普通程序员能够接触到的应该就是 jbm 加载字解码的这个过程, 个过程呢,被称为内加载,这也是面试中的一个高频问题,可以用来考察面试者对于加把的理解深度。在详细讲解之前,我们明确一下内加载流程的目的,站在高处去看呢,就是把一份被加把 c 编译过的克拉斯文 文件,通过加载生成某种形式的克拉斯数据结构进入内存程序呢,可以调用这个数据结构来构造出,哦不价格。对,这个过程呢,是在运行时进行的,这也是加法动态拓展性的根基。我们再来看下面这一张更加详细一点的流程图, 这张图呢,肯定很多人都已经看到过,但是呢,我觉得其实是画的有点问题的,具体是什么问题呢?下面会讲到。关于这张图呢,我觉得有三点是需要严重说明的。第一点,这张图表现了一个类的生命周期,如 如果完整一点的话,我们可以在最开始再加上加把 c 编翼阶段,而类加载呢,只包含了加载、连接、初始化这三个阶段。第二点,需要区分类加载与加载,加载呢只是类加载的第一个环节。第三点,解析步骤是灵活的,他可以在初始化环节的之前或者之后再进行实现所 后期绑定。这点呢,在后面讲到解析环节时会详细的讲。而内加载过程中的其他环节呢,顺序是不可改变的。内加载的过程肯定有很多人都背过,也很有可能是背了忘,忘了背, 其实呢,完全不用背,只要跟着这期视频把其中每个环节都真正理解了,那自然而然就能写出来了。我们首先来看加载这个环节,我尝试用一句话来概括加载环节,加载呢,是一个读取克拉斯文件,将其转化为某种静态数据结构,存储在方法区内, 并在堆中生成一个便于用户钓用的加把浪克拉斯类型的对象的过程。其中有几个注意点。这里的克拉斯文件呢,不一定指的是本地文件,泛指各种来源的二金支流,比如说来自于网络,来自于数据库,甚至是及时生成的卡斯文件。其中像很著名 的动态代理技术呢,就是使用到了及时计算出来的克拉斯,然后实力化代理对象,可以自由选定二进制 理由的来源。这虽然是 jbm 开发者提供的一个小小的接口,但是却给了广大上层开发者施展手脚的舞台。还有上面提到的方法去和堆,这些都是 jbm 内存模型中的相关概念,在这里不熟悉也没事。 本期视频虽然不详细讲,但这个系列之后呢,会讲到内存模型。第二个是验证阶段,验证动作呢,其实是有很多个步骤的,其中第一个步骤, 文件格式的检验其实是发生在加载阶段的,如果通过,那么才能顺利加载,顺利加载后呢?此时方法区内虽然已经存在了该克拉斯的静态结构,最终也存在了该克拉斯类型的对象,但是这并不代表着 jbm 已经完全 认可了这个类。如果程序想要使用这个类,那么就必须进行连接,而连接的第一步就是进一步对这个类进行验证,我们来看看到底对方法区内的克拉斯静态结构进行了哪些方面的验证。 第一个呢,是原数据的验证,第二个是自检码的验证。这两个阶段呢,我觉得是不用记的,简单概括来说,就是对克拉斯静态结构进行语法和语意上的分析,保证其不会产生危害虚拟机的行为。 那更深一层次的细节呢,比较复杂,我们也暂时不用去理解。如果这两个步骤验证通过,那么虚拟机会姑且认为该克拉斯是安全的,但是这并不意味着验证已经完全结束了。还有一 到对符号引用进行验证的步骤,他是在解析阶段内发生的,而解析阶段呢,我们之前也提到过,他可以在初始化阶段之前或者之后进行。 所以呢,验证其实包含了很多个步骤,分散在各个不同的阶段内。这一点呢,是很多博客上没有提到的,也是上面这张图中画的不完全准确的地方。此外,验证的内容是 会不断发展的,除了这里提到的文件格式验证、原数据自检码验证、符号引用的验证四个环节。那么从低版本的虚拟机到现在呢,验证步骤其实已经不断的加入了各种机制,在未来,虚拟机的开发人员可能会引入更多、更完善、更完美的验证策略。在原数据自检码验证通过之后呢, 去你机会姑且认为该克拉斯是安全的,这时候将会进入准备阶段。准备阶段做的处理其实不复杂,就是为该类型中定义的静态变量负零值。注意,这里仅仅是静态变量,而不是成员变量,此时呢,将会出现一个被太多人混淆和误解的概念, 我们这里就好好来捋一捋,希望大家以后都不会再犯错。虚拟机内存规范中的定义的方法去这种抽象概念。那 houst bote 这种主流的虚拟机,在 gdk 八之前呢,使用了永久贷这种具体的实现方式来实现,方法区在 gdk 八以后,在 gdk 八级以后,弃用了永久贷这种实现方式,采用圆空间这种直接内存来取代。所以说我们常常看到有人说 gdk 八级以后采用了原空间来替代方法区, 这种说法呢,是完全错误的,因为方法区是抽象概念,圆空间是实现方式,这种说法就完全是牛头不对马嘴。那应该怎么理解呢?在 jdk 八之前,类的原信息、常量持静态变量等都存储在永久带这种具体实现中,而在 jdk 八及以后,常量持静态变量被移除了,方法区转移到了 堆中,原信息这些呢,依然保留在方法区内,但是具体的存储方式改成了原空间。这个知识点呢,虽然不算多重要,但是我希望大家能够搞明白抽象概念与具体实现之间的区别。 我觉得被人问起来呢,这个问题还是挺能体现出一个工程师的逻辑素养的。在准备阶段完成之后呢,终于来到了上文一直提及的解析阶段,这个阶段呢,主要做的一件事情就是将符号引用替换为直接引用。 那么到底什么是符号引用,什么是直接引用?我来说说我的简单理解。当一个加法类被编译成 plus 之后呢?假如这个类称为 a, 并且 a 中引用了 b, 那么在编译阶段, a 是不知道 b 有没有被变异,而且此时 b 也一定没有被加载,所以 a 肯定不知道 b 的实际地址,那么 a 怎么才能找到 b 呢?此 实在 a 的克拉斯文件中将使用一个字不算 s 来代表 b 的地址, s 就被称为是符号引用。在运行时呢,如果 a 发生了类加载,到解析阶段会发现 b 还未被加载,那么将会触发 b 的类加载,将 b 加载到虚拟集中,此时 a 中 b 的符号引用将会被替换成 b 的实际地址, 这被称为直接应用,这样 aj 能够真正的调用倒闭了。但是事情没有这么简单,了解多肽的同学应该知道,加法通过后期绑定的方式来实现多肽,那么后期绑定这个概念又是如何实现的呢?其实就是这里的动态解析。 接着上面所说,如果 a 调用的 b 是一个具体的实现类,那么就称为静态解析,因为解析的目标类很明确,而假如上层加法代码使用了多肽,这里的 b 可能是一个抽象类或者是接口,那么他可能 有两个具体的实现类, c 和 d, 此时 b 的具体实现并不明确,当然也就不知道使用哪个具体类的直接引用来进行替换。既然不知道,那么就等一等吧,直到运行过程中发生了调用,此时虚拟机调用站中将会得到具体的类型信息,这时候再进行解析,就能 用明确的直接引用来替换符号引用了。这也是为什么解析阶段有时候会发生在初始化阶段之后,这就是动态解析,用它来实现了上层的后期绑定和多态底层的对应了。 emook 的这条字节码指令,我个人认为呢,解析阶段是类加载过程中理解上最有难度的部分, 如果第一遍没听懂,可以先理解一下多肽后期绑定这些概念,然后再把这段看几遍,相信能够加深你的理解。当解析步骤完成,意味着整个连接部分的完成,这也就是说外部加载的加法类, 这时候已经成功的引入到了你的程序当中。初始化阶段的比较易于理解,简单概括就是此时会判断代码中是否存在主动的资源。初始化动作 如果有的话,那么执行这里所说的主动资源。初始化动作不是指的类的构造函数,而是克拉斯层面的,比如说成员变量的复制动作,静态变量的复制动作 以及静态代码快的逻辑。而只有显示的调用六指令才会调用构造函数进行对象的实力化,这是对象层面的,二者不要混淆。好了,关于内加载的过程呢,到这里我们基本就讲完了,我们再来回顾一下这五个阶段。从 jbm 的角度来看, 加载阶段的读取二进支流这个动作,以及初始化阶段,这两个部分是开放了主导权给用户的,用户可以进行自由控制,而剩下的所有部分都是由虚拟级权权包揽,尤其 内部来完成。流程上搞懂了,那虚拟机到底是如何使用代码来实现这些步骤的呢?这就要聊到加法的内加载器了,这些内容呢,我们下期再好好聊聊,此时你可以闭上眼睛再回忆一下内加载的五个阶段, 是不是不用死记硬背也能了然于胸了呢?好了,本期视频就到这里,如果你觉得我的视频不错呢,一定要给我点赞,投币加转发,我是韩石,我们下期再见!


各位大家好,我们本节课呢来继续学习啊关于 jbm 的呃这个课程内容。那么 上节课呢,和前面的几节课我们一同呢去学习了。关于呃这个 jbm 当中比较重要的一个环节,就是垃圾回收相关的一些理论的内容。 那本节课呢,我们将把最后一部分理论内容呢给大家介绍完毕。那最后一部分理论内容介绍完毕之后,我们将会通过呃一些具体的代码视力,并且呢辅以相应的一些 执行工具来去啊,帮助大家共同的去消化和理解我在之前的课程当中所介绍的关于这位 m 垃圾回收的一些理论内容。就是说通过理论来去结合实际,然后通过实践的这种方式来去反衬 理论的这样的一个呃形式啊。通过这样的一种形式呢,相信大家呢应该对于这边 m 的垃圾回收呢,应该有一个更加深刻的理解。嗯,我们先快速的回顾一下之前所讲的一些这个重要的内容。然后呢,开始本节课的内容的一个讲解。 呃,首先呢,这个图大家一定要掌握啊。这个图大家一定要掌握。就是这位 m 运行时内存的数据区域 啊,分为方法区啊,分为方法区,分为虚拟基站,本地方法站。那么在二口的 cosplat 虚拟基当中呢,他是将加瓦虚拟基站和本地方法站呢啊给统一起来了。那么还有堆堆呢,是我们啊进行呃管理的最大的。这边 进行内存管理的最大的一块区域,那么也是垃圾回收呢最主要的一个区域。在堆上的这个回收呢,他的性价比是非常高的啊。当然了,在虚拟基站呀,在这个方法区域啊等等也是可以进行垃圾回收的。但是呢,性价比是比较低的。 还有一个程序计数器,那么程序计数器呢,是唯一一个在 jbm 的规范当中,没有明确的指令,他里面会出现内存溢出错误的这样的一个区域。 那么其中呢,灰色的区域为县城共享,就是方法区域和堆呢,是共享的。而虚加虚拟机站,程序计数器以及呢本地方法站这三个内容呢,他们是县城隔离的啊,每个县城呢是独有自己的一份的 啊。这一块希望大家能够理解。这个图特别特别的重要,帮助你能了解这位 m 所管理的内存 区域一共分为几块啊。这块就不说了, 这是这边垃圾回收的模型,主要分为这么三点。垃圾判断的算法就是什么对象是垃圾,什么对象不是垃圾。鸡西的算法。垃圾回收器的实现和选择。那么呃,这里面常用的有两种方式,第一种呢是引用技术啊,第二种是跟搜索算法。那么在呃 泡泡的虚拟剂当中呢,他没有采用的是饮用激素,而是采用的根收所算法。那么饮用激素算法呢?呃,可能会出现循环饮用这样的一种情况。 那么跟搜索算法呢,就是从一个起点作为一个入的,那么从他开始往下去便利啊,只要没有被链接的这个节点呢,都被认为是垃圾。那么在下一次垃圾回收的时候呢, 就会被清除掉。那么在加法,在加法语言当中呢, gcrut 啊, gc 的垃圾回收的根呢是包括呃在 vm 占中就占中的局部变量中的饮用啊,占中的局部变量中饮用,方法区中的静态饮用,然后 zni 当中的饮用 啊。这是介绍的关于方法区的内容啊,大家可以迅速的看一下,我就不再做过多的讲解了,因为之前已经被讲解过了。 那么这位 m 呢?常见的一些呃垃圾回收的算法啊。这位 m 常见的一些 gc 的算法有标记清除算法, max 为有标记整理算法,或者标记压缩算法,叫 max max, 还有复制算法,靠 啊。最后呢是有啊,分代算法,在现代的这个在现代的这个虚拟机当中,不仅是说加瓦虚拟机,那么其他语言也会有相应的虚拟机,比如说像那个当代的啊, c 萨他们 运行至上的啊,也是运行在虚拟机之上的。也有相应的分带算法,这个基本上称为现代的虚拟机必不可少的一个重要的组成部分。 那么标记清除算法呢?就是分为两个阶段,第一个阶段呢,称之为叫标记,也叫做 max。 那么第二个阶段呢,称之为叫清除,也叫做 suv 啊。这两个阶段呢,是一前一后的先去标记,然后呢再去进行清除。那么他有自己的一个缺点,就是效率和空间 这样两个问题。那么弓箭问题呢,就是导致呢可能会产生太多的碎片,使得内存呢,总的剩余空间的容量呢,是大于带分配的对象的容量。但是呢,由于连续的空间的这个都不存在,连 足够的连续的空间能够容纳下所分配的这个对象的大小,所以导致对象呢是无法分配的啊,这是标记清除算法产生所固有的一个问题。那这个图呢,大家 应该能好好理解一下啊,从这个运行期站当中呢的引用呢去出发,然后呢标记哪些对象呢,是可以保留的,哪些对象呢,是需要被回收的,那么红色的我们看到都是可以被回收的, 这样的一些所谓的垃圾啊,所谓的垃圾。好,再往下看。那这个 标记清除算法的一些缺点,效率不高,需要扫描呢,所有的对象,对于呢越大记事越慢,这是显然的,因为他要从这个虚拟基站账啊,迅啊记事一入的开始,比如说从那个虚拟基站开始呢出发,然后呢扫描所有的对象,显然呢对越大,那记事呢,扫描的过程就越慢。 第二点呢,他是存在着内存碎片的这样的一个非常严重的问题啊,存在着内存碎片这样的一个非常严重的问题,那么 gc 的次数越多呢,碎片就会越严重。这个其实比较好理解吧, 对吧,计时一次数越多,碎片越严重,就计时一次数越多呢,那么导致内存的不分配啊,不连续的这种情况呢,就会越发的这个严重。那么这种情况一旦出现之后呢,就会导致刚才我说的那种情况的一个出现。就是 总的空间呢,是足够的,但是呢却没有单个的连续空间呢,能够容纳下带分配的这个对象。 第二个呢,就是这种复制算法,那么复制算法呢,就是道理也比较简单,就是这样一个大块的区域呢,分成两块,那么每次只使用其中的一块, 每次只使用其中的一半,那么当进行回收完之后呢,就把剩下的对象呢,整个的拷贝到另外一个区,另外一个半区。那么他的问题就在于呢,可用的空间大小呢?只是总的空间大小的一半 啊。可用空间大小就是种的空间大小一半,浪费比较严重 啊。现在的商业虚拟机呢,都是在用了这样的一种收集算法来回收什么呢?新生代包括奥尔扣的耗时泡的虚拟机也是采用了这样的一种方式。那么将内存呢分为一块较大的一等空间和两块较少的是歪歪空间。 每次使用一等和其中的一块是歪歪。当回收的时候呢,将一等和是歪歪中还存活的对象啊,一次性拷贝到另外一块是歪歪空间上。然后呢清理掉一等和,使用过的是歪歪。 那么奥尔口的哈斯泡的虚拟金默认的一等和是歪歪的大小比例呢?就是八比一。也就是说呢,每次只有百分之十的内存呢,是被浪费掉的。 复制收集算法呢,在对象存活率比较高的时候呢,效率有所下降。所谓对象存 活率高,就是每一次垃圾回收之后呢,被回收掉的对象呢,比较少,而留下的对象呢,是比较多的啊,这是这种情况下,效率呢会有所下降。那么如果我们不想浪费百分之五十的这个存储空间的话,就需要有额外的空间进行分配担保,用于应付呢。半区中 内存中所有对象都百分之百存活的这种极为特殊的情况。所以呢,老年代一般不能直接选用呢这种算法。 这是复制收集算法的一个基本的图示,大家可以看一下。这个我们之前也讲过了啊。 那么复制收集算法啊,他的做法是只需要扫描存活的对象,效率呢更高啊,只需要扫描存活的对象,效率更高。第二个不会产生碎片,因为他会将半区的存活对 这样呢,整个的拷贝的另外一个半区。所以呢,啊,拷贝完之后呢,原来那个半区的整个的空间就可以完全的被复用了,所以不会产生内存碎片的问题,那么他需要浪费额外的内存呢,作为一个复制区域。 第四个复制算法呢,非常适合于生命周期很短暂的对象,因为每次继续呢,总能回收大部分的对象。复制的开交呢,是比较小的,如果复制的对象比较多,那么肯定效率会大量急剧的下降 对吧。如果每次垃圾回收的这个成功率比较高,或者是说每次被回收的对象数量比较多,留下的很少,那么进行复制的成本呢,也就很低了。 所以呢,复制收集算法呢,通常会被用到新生代,因为新生代呢,大部分对象都是招生熄灭的,这样对象就创建完之后一段时间就被回收掉了,就变成了 垃圾了。那么老年代呢,一般不是这样的。而老年代呢,这对对象存活时间呢,一般都比较长啊,一般都比较长。 第二个呢是呃,接下来是这个标记整理算法,就是 monty 算法。标记过程呢,是跟标记清除呢是一样的。但是后续步骤呢,不是进行直接清理,而是将所有存活的对象呢,一端一动,然后直接清理掉这端边界以外的内存。这样的话就会导致呢,呃,内存呢,变得连续了, 内存变得连续了,这是标记整理算法。我们看这个图,那么他的特点是没有内存碎片。 但是呢,他有自己的一个问题,就是比标记清除算法呢,耗费更多的时间进行什么一个整理,或者进行一个压缩。所以呢,在这些垃圾回收算法当中呢,其实没有任何 一种算法,就是能能够做到说我这一个算法能应付应付所有的情况,都是好处,都是优点,没有缺点,不存在的。如果真的存在这样的一种算法的话,那么其他的算一些算法根本就没有必要再去使用了。就好像我们的 呃这个排序算法一样,对吧?常见的排序算法,比如说有快速排序啊,堆排序啊,吸饵排序啊,冒泡排序啊,选择排序啊,交换排序等等等等。那每一种排序算法呢,都有自己的一个适用场景啊,都有自己的一个适用场景。不会存在某一个排序算法就一定比其他所有的算法都要好的这样的一种情况。 分代收集算法。那么当代的当前的这种商业虚拟机的垃圾收集呢,都是采用了分代收集算法,根据对象不同的存活周期呢,将内存划分为成几块。一般呢,是把加瓦堆分做新生代和老年代啊, 这样的话,就可以根据各个年代的特点,采用最适当的收集算法。比如说新生代美食 gc 呢,都有大量的对象死去,只有少量的对象呢,是处于存活状态的。那么我们就可以选用什么呢?复制算法只需要付出少量的存活对象的复制成本,就可以完成一个收集的过程。 那么分贷算法的特点是综合前面的几种这种垃圾回收算法的各个优缺点。然后呢,针对不同生命周期的对象, 他会采用不同的啊机器算法啊,针对不同的生命周期的对象,他采用不同的垃圾回收算法。比如说新生代 他就采用了这种,根据这个新生代对象的特点,采用了这种复制算法对吧?每一次垃圾回收之后呢,剩下的对象很少,那么呃拷贝的成本呢?是比较低的。那么对于他的老年代来说,就可以采用标记清除算法,或者是说标记整理算法。 那么这个 houspo 这位 m 中划分为几个年代?注意这里面为大家强调了永久带在现在是不存在了,变成了圆空间,给大家了解一下。 那么在新生代呢,他划分成了这样三个空间,一个是一等空间,默认是占百分之八十。还有一个是 fm, 是 vivo 啊,默认占百分之十。还有一个凸式是 vivo 啊,默认也是占百分之十。这种呢是百分之百。 那么新生代下面呢就是老年代啊。新生代下面呢就是老年代。那么当新生代呃某一个对象,按照我们之前起 启动时候的一些配置,如果他经历了多轮的垃圾回收之后呢,依旧还是处于什么一个存活的状态的话 啊,依旧还是处于一个存活的状态的话。那么呃新生代的里面的这个对象呢?就会我们叫做彭某的啊,这个英文的原文叫彭某的就晋升到 office, 晋升到老年代。 好,这个是呃年轻代的,或者是说这个新生代他的一个基本的特点。呃,新生代。这个第二段话大家一定要理解。 新生代呢分为三个区域,一个一等区域,两个是歪歪区域。我们可以通过参数呢来设置是歪歪的个数。 对象呢是在一阵区域中生成,当一阵区满的时候,还存活的对象将被复制到一个死歪文区域 啊。当一等区满的时候,上存活的对象呢,将被复制到一个是歪歪区域,当这个是歪歪区域也满的时候,此区域存活的对象将被复制到另外一个是歪歪区域。 当第二个室外卧区域也满了的时候,从第一个室外卧区域复制过来的,并且此时还存活的对象将被复制到老年代。两个室外卧室呢,完全对称,轮流替换,轮流替换。 老年代呢,存放的是经过了一次或多次窒息呢,还存活的对象。一般呢老年代会采用标记清除或者是标记整理算法来进行垃圾收集。那么有多种垃圾收集器呢?是可以选择的。每种垃圾收集器呢,可以看作是某一个继续算法的具体实现 啊。我们刚才所复习的这些内容呢,都是讲的是一个一个的积极的算法。那么对于具体的垃圾收集器,实 基上就是这种啊,特定的算法的一种具体的实现啊,具体的实现。永久带是不存在的啊,是不存在了,变成了圆空间啊。 max 好,这个是一个内存结构。这个内存结构呢,希望大家呢也能够去好好的去梳理一下或了解一下。首先呢是 pc 计存器,接着呢是 jbm 的方法站,方法站有战争,每个战争呢有局部变量表,还有相应的操作数战 操作出战。接着呢是由本地方法在这边方法区,还有呢这边的堆。那么在号在 在耗时炮中呢,本地方法战和这位 m 方法战是同一个,因此也可以通过这样杠 xss 这个参数来指定战的大小。他默认最小的值呢,在 gdk 八啊当中呢,是一百六十 k。 这我们在之前的课程当中呢,其实 已经见过了。主要记住这一点。在 houspod 中呢,本地方法站和 jbm 的方法站呢,是合二为一了。好。接着再往下看。 内存分配堆上分配,大多数情况下呢,是在一等上分配的,偶尔呢,可以直接在啊老年代上分配,细节取决于继续的实现。还有站上分配原子类型的局部变量啊,原子类型的,比如说印他呀, flot 这个直接在战上呢,就分配了八个原声类型。好。内存回收 这块就不说了,这是介绍一些垃圾回收的算法。那么这几个算法呢,我们都已经都会在后面的课程里面啊,给大家去进行一个提及。据悉的时机在分带模型的基础之上呢,据悉呢,从实际上可以分为两种啊,叫 style g c 和 f g c。 重说一遍,在分代模型就新生代,老年代这样的一种分代模型的基础之上。 g c 呢,从实际上来看可以划分为两种。第一种叫 s gany g c 和 f g c。 那么 sty g c 呢,又叫做 man g c 啊,是单位 gc 呢,又称之为叫 mangc。 触发的时机是新对象生成的时候呢,一等空间呢,满了。 理论上一等区的大多数对象呢,都会在 stylegc, 就是 mangec 当中的回收复制算法的执行效率呢,会非常高。 stylegc 的时间呢,会比较短。 第二个负 g c, 负 g c 呢,会对整个的 j、 v, m 进行整理,包括呃样啊,包括偶的等等。主要的 触发时机第一个老年代满了。第二个,这是永久贷,我们就不说了。第三个调用了 ctm 点 gc, 他的效率很低,尽量减少负 gc。 负 gc 呢,会导致 s t w 的出现,就是 w 出现会阻塞你的业务现成。因为垃圾回收呢,也是通过垃圾回收现成呢来去执行的,这个没有问题吧。 所以呢,呃,我们应该大部分情况下,或者绝大多数的司机应该都是使用触发 manengc 或者是跟 ivgc, 而不要去触发负 gc。 垃圾回收器叫高尔比克莱克 gc。 那么分带模型 gc 的一个宏观的愿景垃圾回收器呢?是 gc 的具体实现。那么 houspodgvm 呢,提供了多种垃圾回收器,我们需要根据具体应用的需要, 采取不同的回收器来进行使用啊。没有万能的垃圾回收器,每种垃圾回收器呢,都有自己的一个特定的适用场景。这我也跟大家说过了,没有淫弹这样的一个概念的。 垃圾收集器的并行 plul 和并发 ctrluse。 并行 plul 指多个收集器的现成呢,同时工作,但是用户现成呢,处于等待状态 啊。病情指的是多个收集器的县城呢,可以同时工作,但是用户县城呢,可以要等待,是业务县城要等待。并发指的是收集器在工作的同时,可以允许用户县城呢去工作。 并发不代表解决了计时停顿的问题。在关键的步骤呢,还是要停顿。比如说在收集器标记垃圾的这个时间 点的时候,但是在清除垃圾的时候,之前用户县城呢,可以和祭司县城呢并发执行啊。就说在关键的步骤还要还是要停顿。在收集器标记垃圾的时候要停顿 啊。在收集器标记这个垃圾的时候要停顿。否则你跟用户现成同时执行的话,在某一个对象是不是垃圾这个问题上可能就会产生一些分歧。 但是呢,标记完之后再清除垃圾的时候,就某一个对象已经被设定为或者已经被标识为是一个垃圾的话。那么这种情况下或者这个时间点上,用户现成呢,是可以和 gc 的现成的并发的去执行。 接着我们开始看 siri sir, 其实就是这个串形的意思吗? siri 收集器就是一个单线层的收集器,收集的时候呢,会暂停所有的工作现成。 也就是说呢,他会产生 storestw 这样的一个问题。使用复制收集算法,虚拟机运行在可爱的模式是默认的。新生代的收集器啊,私人收集器的一个基本的特征。单线程收集器会暂停所有的工作现成的执行。 好,再往前看啊。新生代采用的是复制算法,老年代呢,采用的是标记整理算法啊。标记整理算法,因为是单线程的 gc, 没有多线程切换的一些额外开销,简单实用。 这个是 siri 收集器的一个特点,他是最早的一个收集器,其实时间起来也比较简单。第二个 pang 收集器。 那么 party 收集器呢?就是 siri 收集器的一个多现成版本。 pany 收集器,就是 siri 收集器的多现成版。 除去使用多个收集县城之外,其余行为呢,跟他是跟 siri 收集器呢是一样的。对应了这种收集器呢,是收耳模式的新生代的收集器。而且他一定要运行在多和的这种环境下,要不然多县城是没法派上用场的。 再往下看 plus 跟位收集器。 plus 跟位收集器呢,也是一个多线程收集器,也是使用的是复制算法。但他的对象呢,分配规则和回收策略呢,都与 panl 收集器呢,有所不同。他是以吞吐量最大化为目标的一个收集器。实现 cro 的是单线层的收集器,使用标记整理算法,是老年代的收集器啊。 siriod 是单线层的收集器,使用标记整理算法, 是老年代所采用的一种收集器。再往前看, 嗯嗯, pllood 啊, pllood plus 单位是老年代的一个实现啊,采用多线程标记整理算法。 往前看 cms。 cms 呢,这个收集器是一个实现特别复杂的一个收集器啊。我记得当呃在某一个时间某一个时间上吧, rco 呢,甚至说都准备废弃 cms 收集器了,因为他的实线太复杂了啊,开发人员呢,也不堪这个重压啊。 特别特别复杂的一种收集器实现 cms 就是标记的。呃,并行的,并行的。这种标记清除算法, cmant max 为 cmscms 呢,是一种以最短的停顿时间为目标的收集器啊,是一种以最短停顿时间为目标的收集器。使用 cms 呢,并不能达到 gc 效率最高, 但他尽可能的会降低 gc 十服务的停顿时间啊。 cms 收集器呢,默认使用的是标记清除算法,就是 max 为 cma 收集器呢,是针对于最短的停顿时间,非常适合于外部应用。他只针对于老年代,一般呢会结合 pangle 呢去使用啊。据悉,县城和用户县城会并发工作。使用标记清除算法啊,可以使用这个 这个这位 m 的启动参数 uconc, max, vipc 呢来打开啊。 cms 收集器的一个缺点。 cms 呢,以牺牲 cpu 资源的这个代价来减少用户现成的停顿。那么 当 cpu 个数小于四的时候,有可能对吞吐量的影响非常大。再重说一遍 cms 呢,以牺牲 cpu 资源的这个代价来减少用户现成的停顿。什么叫牺牲 cpu 资源的代价?就是说他对 cpu 的占用呢,会更多一些。 cms 呢,在并发清理的过程当中呢,用户县城还在工作,所以这个时候呢,需要预留出来一部分空间给用户县城。 cms 呢,使用的是标记清除算法,会带来一些碎片化的问题,那么碎片过多的时候呢,就会容易触频繁的触发 负 gc。 这样的一个我们不想看到的一个结果啊。这是 gc 垃圾售机器的 gvm 的参数的定义。这块我就不再给大家去再讲解了。上次课呢,已经详细讲解了每一个,我们在后续课程当中呢,也会呃挑选其中重要的一些参数呢,给大家进行一个实力的分析。 好,那本节课的最后一些时间呢,我们来去呃分析一下。呃,在我们日常的这个开发当中呢,可能会出现加挖内存泄露的一些经典的情况。然后呢,我们来去分析一下出现这些情况的原因是什么,以及解决办法是什么。 这里面呢主要给大家列出来三点。第一个就是对象呢,定义在错误的范围内。第二个异常处理不当。第三个集合数据的管理不当。这里面主要给大家列出来这三种,其实呢还有很多种。这个在后续课程当中呢,我会呃 给大家举出来相应的一些势力啊,给大家举一些相应的势力来去演示一下啊。内存泄漏产生的一些原因,就通过由于我们编码的这个问题产生的。我们首先来看第一个对象定义在错误的范围内。我们首先看一下这个类, 对于这个类来说,我们看它里面有一个字母串数组类型的层面,面量叫内蒙子。然后呢,在这个对应的这个方法当中,他使用到了内蒙子 对内幕首先做了一个判断,是不是为空,是不是长度小于这个特定长度。如果满足这个条件的话,我们 创建了一个新的追导,创建了一个新的自无串数组的对象。然后呢,调用抛票类的方法把这个内幕传进去,然后打印出来这个内幕的值啊。这种代码的写法呢,其实这么一看,其实也没什么问题, 对吧?大家这么一看,逻辑也清晰,对吧?呃,也没有产生什么问题。但是呢,我们来看看原因。如果负实力对象的生命周期较长,就是这个克拉斯负 啊,克拉斯夫,如果他的对象的这个生命周期是比较长的,就会导致临时性的内存泄露。 什么叫临时性内存泄露?就是在他存活的这段期间之内,可能会产生内存泄露的问题。这里的内分子变量其实只有临时的作用。大家看一下,对于这个内分子,假设我们这是我们一个,这是类的一个全部的内容啊,没有其他不是,会再有其他的方法再去直接使用内分子。 那么你可以看一下,在这个局这个呃都意的这个方法当中,对于内幕的使用 是没有问题。但是呢,其他地方由于不会再使用内蒙子这个成型变量了,所以呢,当这个负这个实力,只要他还存在的话,他里面的成型变量一定会存在,对吧?但是呢,这个成型变量其实只是被这一个方法所使用了。 那么我们看下面的这个 m 呢,喜欢生命周期短暂的对象,下面这样做就可以了。我们将这个内蒙由成面变亮,把它移动到这个方法当中,成为一个什么局部变亮 啊。我们将内木子从这个方法类当中呢,从成员变量这个位置上,我们把它移动到局部变量这个位置上,移动到局部变量位置上。大家会看到完全不影响我们这个度议的方法的一个执行逻辑,但是他的特点就不一样了,或者他的结果就不一样了。这个结果不是说程序 输出结果,而是在呃内存当中的一个布局的这样的结果。当这个督役的方法一旦执行完之后,那么他里面定义的所有的变量当然都是局部变量了,对不对?局部变量就会被回收掉。所以呢, 不管这个负他的这个实力存活多长时间,都不会影响内这个局变量的一个立刻的或者是及时的一个回收。而上面这种情况呢,就不会 这样对吧?只要负这个实力存在,那么内蒙这个层面量就一定也是存在的啊。这种情这种问题呢,其实 不好排查。不好排查看起来也没有什么问题啊。程序对吧?好,我们看第二个。第二个叫异常处理不当这种呢,这个这样的问题呢,在一些出血 这这个程序代码当中可能会出现,但是对于有经验的人呢,我相信不会再出现这这类问题了啊。这是一个典型的使用 gdpc 进行操作的一个代码片段。首先呢,我们获取一个连接啊,专用慢卷,点 get clasen 获取到一个数据库连接。然后呢, 啊,定好自我随口语卷。然后呢,定好 propars 的 mant。 然后呢,执行一个在 q 的啊 xq q 为执行一个查询得到一个 readow 晒的,然后便利这个 readow 晒的执行一些逻辑。大家看这两行代码,二 s 就是关闭 readow 晒的,关闭可奈克森。 这种做法呢,就是有问题的。为什么如果都市 mcx 大夫这个方法,这个方法,都市啊都市 ms 大夫这个方法,如果他抛出了异常这行代码, 不管在哪一次歪咬循环当中,一旦这里面的方法调用抛出了异常的话,是不是直接进入到 kit, 直接进入到 kitty 的话,那么二 s 加可漏子和可耐克森点可漏子两个关闭方法,势必就一定不会被掉用, 他俩不会被盗用。导致什么情况?会导致内存泄漏和 db 的数据库的连接泄漏。是这样的吧,就是说,呃,资源关闭这块,我们绝对不能就像这这个程序这样写,直接把它放到踹里面。那么资源关闭呢?一定要把它放到发音的例当中 啊。资源关闭,一定要把它放到发音的当中。你可以看到这个代码片的写法,如果二 s 不为空,关闭二 s, 如果这个不是 pass, 真闷的。不为空关闭不是 pass, 真闷的。然后关闭这个链接。这样的话,会保证这个关闭的动作是永远都有机会 会呢去执行的。像这样的一些,比如关闭连接啊等等。呃,如果你没有关闭呢,当你程序执行的时候,也不会对你程序产生任何影响。但是当程序运行足够长的时间之后,这种影响呢,就积累出来了。然后呢,最后呢,就爆发出来了。 最后一个,集合数据管理不当。集合数据管理不当。呃,当我们去使用耳瑞贝斯的数据结构,就是基于数组的这种数据结构啊。当我们使用基于数组的这种数据结构。什么样的数据结构是基于数组的呢?比如说 led, 他是基于数组的吧?还有哈西麦,哈西麦本质上底层是不是也是一个数组加上一个单项的列表啊啊,这都是基于数组的这种数据结构。我们要尽量的减少 resiz, 因为基于数组的这种数据结构,一旦进行 resiz 的话,就 重新指定大小的话,或者是进扩容的话,一定会涉及到数据的复制。数据的复制的成本其实挺高的。 那么好的做法是,比如说我们在你有一个而为例子的时候,尽量的先去估算啊, 这个集合到底要存放多少个元素。估了估算这个赛子在创建的时候呢,就把这个赛子呢给确定好。如果能明确的确定下来这个 led 的大小是多少,这是最最佳的一种情况。那么减少 减少瑞塞子呢?可以有效的避免没有必要的一些数足拷贝的操作,击碎碎片的一些问题 啊。减少瑞赛的可以避免没有必要的尔瑞 copy 啊。如果一个 fast 只需要顺序访问,不需要随机访问。 重说一遍,如果一个 mist 只需要顺序访问,不需要随机访问,那么可以用 link mist 来代替 rvlex 的。 因为另类似的本质上是一个链表啊。本质上是一个链表,那么是一个双向的这样的一个链表。 尔尔维利斯的本质上是一个数组。数组呢?当然是可以支持随机访问,我们通过锁眼就能直接定位到某一个元素。但是拎克利斯的这样的一个基于列表的这种方式的话,他只能从沿着这个列表呢,从第一个元素一直往下走, 他不能去啊,用随机的这种方式,因为列克利斯的本身呢,他们不是不连续的,对吧?那么当我们只需要顺序访问,不需要随机访问的时候,就可以用利克利斯的。那么因为利克利斯的本质是列表,不需要为塞子,但只是用于顺序访问。什么意思? 当我们对列克利斯的进行一个扩容,或者往这个列克利斯的里面去插中间的某一个位置上去插入元素的话,他并不需要涉及到呃数组元素的移动,也并不需要涉及到数组的扩容,他只需要去改变这个双向链表的这个指向就可以了。这样的话这个效率呢就会非常的高。 好。那么以上呢所讲的就是关于我们 jbm 这门课程的垃圾回收相关的一些重要的理论知识点。 那么希望呢,这个理论的知识点的啊,我前两节课的一些讲解,加上本节课的连讲解,带去呃 适当的复习,能帮助大家呢更好的去把握这个理论的内容。那么在后续课程当中呢,我们将会对理论内容呢进行一些具体的时间操作啊,来去帮助大家呢更好的去消化和吸收。好。那么本节课呢,就到此结束,我们再会。