南京小程序开发网站制在线做网站 自动生成手机版
南京小程序开发网站制,在线做网站 自动生成手机版,百度关键词优化软件网站,湖州网站开发公司这里我想首先说明一下#xff0c;虽然我们经常会拿垃圾回收器来做比较#xff0c;虽然想挑选一个最好的收集器出来#xff0c;但是目前也没有说哪一款收集器是完美的#xff0c;更不存在万能的收集器#xff0c;我们也只是对收集器选择最适合场景的一个收集器。 那么作者将… 这里我想首先说明一下虽然我们经常会拿垃圾回收器来做比较虽然想挑选一个最好的收集器出来但是目前也没有说哪一款收集器是完美的更不存在万能的收集器我们也只是对收集器选择最适合场景的一个收集器。 那么作者将在接下来和大家探讨一些垃圾回收器的发展历程和优缺点相信读完这篇的你也绝对不是白花时间不过理解的过程有点枯燥也希望大家有耐心看完或者进行完善。
Serial收集器 这款收集器可谓是最原始的垃圾收集器了这个收集器在回收的时候只会使用一个处理器或者一条收集线程直到收集结束。也就是说用户的线程是可以并行的但是在新生代和老年代的收集都是单线程GC新生代采用复制老年代采用标记整理也就是说只要收集垃圾就会stop the world并且不可控还不可知这对于用户来说简直是灾难。 那大师们首先想到的思路就是既然用户线程都可以进行多线程了那么垃圾回收的时候如果也进行多线程会不会更快呢因此ParNew收集器就来了。
ParNew收集器 这里笔者也就不画图了它和serial的区别就是在gc线程的时候在新生代采用复制算法并且用多线程并行收集。但是在老年代依然是采用标记整理算法的单线程。但是不得不说在jdk7之前除了serial收集器只有它能和cms收集器配合工作。 在jdk5发布的时候hotsopt也推出了一个cms收集器这个可以说是具有划时代的意义了。但遗憾的是cms作为老年代收集器却没有办法与jdk4中存在的新生代Parallel Scavenge配合工作。所以在jdk5中使用cms来收集老年代新生代只能选择ParNew或者Serial收集器中的一个。parNew也算是抱上了cms的大腿。以后的出场它俩也是搭配出场。但遗憾的是什么就是在g1回收器出来之后就不需要其他新生代收集器配合了因为g1是面向全堆的。 通过堆parnew的了解也知道parnew在单核情况下其实是和serial差不多的。
Parallel Scavenge收集器 Parallel Scavenge是一款新生代的收集器是采用标记-复制算法实现的同样从名字看出来它也是可以实现并行收集的回收器。但是我们要探究一下他和parnew的区别在哪里。 Parallel Scavenge的特点就是关注点和其他的不同cms关注的是延迟也就是说尽可能缩短垃圾回收时候用户所暂停的时间而Parallel Scavenge面向的是一个希望达到一个可控的吞吐量。吞吐量也就是处理器用于运行用户代码的时间和处理器总耗时的比值。当然是希望用户运行代码的时间越长越好。因此这款收集器也常常被称为吞吐量优先的垃圾处理器。 这里呢也可以提出一个参数如果我们自己把我不好新生代伊甸园和幸存区的比例新生代大小或者晋升老年大对象的大小就激活参数-XX:UseAdaptiveSizePolicy就可以了这个开关打开之后虚拟机会根据当前系统的运行情况去动态设置的。
Serial Old收集器 serial old收集器是serial收集器的老年代版本同样的他也是一个单线程收集器使用标记整理算法。这个垃圾收集器的意义就是供客户端模式下的虚拟机用。在服务端的话jdk之前可以配合 Parallel Scavenge另外一种就是作为cms收集器失败之后发生fullGc等等的后备方案。
Parallel Old收集器 这个收集器看名字就直到是Parallel Scavenge的老年代版本采用标记整理算法这个是支持多线程并行收集的直到jdk6才有。不然的话往前看Parallel Scavenge其实很尴尬啊如果新生代选择了这个那么老年代除了serial oldPS MarkSweep)意外就没得选啦cms就不好使啊。如果但是用Parallel Scavenge作为新生代的话由于serial old太垃圾的话也没有办法充分利用服务端的多处理器并行能力直到这个Parallel Old出来了吞吐量优先才算是脑子和脖子接上了表示我自己还没有适应这具躯体哈哈哈。
注意第一个是新生代Parallel是采用的复制算法。后面的才是老年代gc的标记整理。 CMS收集器 接下来我们终于介绍到了小时代1.0的cms收集器了。在之前提到的垃圾回收器的时候优化点在于把单线程的垃圾回收工作转为多线程那么有没有更好的优化方式呢随着科技的进步答案显而易见就是我们可不可以让垃圾回收的过程和用户线程一起并发呢这就是需要很大的挑战了下面我们来看看cms是怎么做的。 concurrent mark sweep可以看出这个是基于标记清除的垃圾处理器因此下意思的反应就是它是否会产生内存碎片呢答案是肯定的。 cms的回收工作可以拆分成四个过程初始标记并发标记重新标记并发清除。除了带并发两个字的其他的都是需要stop world的。 但是初始标记过程只是标记一下GC Roots能关联到的对象这个速度是很快的而并发标记就是从GcRoot直接关联的对象开始遍历整个对象图的过程。这个过程耗时较长但是不需要停顿用户线程。而重新标记阶段则是要修正并发标记期间因为用户线程可以与垃圾回收器一起工作和产生变动的那一部分对象这个是需要重新标记的这个阶段的停顿时间比初始标记稍长但也远比并发标记的时间要短。最后就是并发清除阶段用于清除已经标记为死亡的对象这个阶段也是可以与用户线程进行并发的。 这里面耗时最长的就是并发标记和并发回收了。总体上来说cms内存回收阶段是和用户线程一起进行的。 缺点
但是我们也不得不提到几个问题
1.cms处理器既然不回导致用户停顿肯定是因为占用一部分线程或者说是处理器那么应用程序就会变慢因为资源是有限的不可避免吞吐量会降低。也就是说处理器的核心数非常重要太少了就别来擦边了。那么为了缓解这种情况虚拟机也提供了一种增量式并发收集器主打在并发标记和清理的时候让收集器线程和用户线程交替执行就是要干活就只有一个干活别一起上。这样只管速度感受就好一些但这个已经不建议使用了。
2.由于用户线程和并发标记和并发清理阶段线程是并行的所以不可避免地就会造成有一部分对象是无法进行回收只能留着下一次回收这种垃圾就是浮动垃圾。因此cms收集器不能像其他处理器一样等老年代几乎填满了再进行收集而是必须预留一部分空间供并发收集时地程序使用。如果cms运行期间预留地空间无法分配对象了那么不好意思只能并发失败冻结用户线程临时用serial old去重新收集老年代这样用户停顿时间就会很长几乎game over了。
3.在开头我们就已经提到了标记删除不可避免地会带来空间碎片空间不连续的问题,这个也会造成我们之前提到地full gc的问题。
Garbage First收集器
G1收集器又是一个划时代的垃圾回收器。简称小时代2.0版本吧。他有一个很大的思想转变就是把内存的区域化整为零采用面向局部收集基于region的内存布局形式。g1是主要面向服务端的垃圾回收器。
它的阶段可以分为初始标记并发标记最终标记筛选回收。采用复制整理算法。这里可以知道这个是没有内存碎片的。
jdk9发布时直接取代了PSPS Old了。成为默认垃圾回收器。这里主要讲解它的闪光点和它的缺点。 一款可预测暂停时间的停顿预测模型。这里只指定一个长度为m毫秒的时间片段内消耗在垃圾收集上的时间打开率不超过n毫秒这样的目标。 g1跳出了回收面向的对象要么是老年代要么是新生代的局限它可以面向堆内存的任何一部分来组成回收集衡量标准不再是哪个代而是那块内存中垃圾数量最多回收效益最大就回收哪部分。 region的每一块是想等大小的每一个region都可以根据需要来扮演新生代的eden空间survior空间或者老年代空间。收集器能够根据某一块的角色不同区采用不同的策略。 region还有一类特殊的就是humongous区域专门用来存储大对象的。当对象空间大于一region容量一般的时候就可以判定为大对象。而g1对humongous当作老年代来看如果大小超过了一个的话他也会用连续的内存空间进行存储。 G1之所以能建立可预测的停顿模型是因为它将region作为单词回收的最小单元即每次收集到的内存空间都是region大小的整数倍这样可以有计划地避免在整个java堆中进行全区域地垃圾收集。更具体地处理就是让G1跟踪各个region地垃圾堆积地价值大小然后在后台维护一个优先级表每次再结合用户允许地收集停顿时间去回收价值收益最大的那些region。 但是往往将一个整体细化成各个细碎的东西虽然更灵活反之带来的代价就是它的维护更麻烦。这个概念就跟单体项目和微服务项目一样。 问题
1.难维护耗内存 将堆分为region中跨代引用如何解决我们可以使用记忆集来避免全堆作为GCRoot扫描但g1收集器毫无疑问会更加的复杂。g1的记忆集本质上是一个哈希表key是region的起始位置value是一个集合存储元素是卡表的索引号。这种双向卡既知道我指向谁又知道谁指向我。并且由于region的庞大数量索引g1至少要花费相当于java堆内容10%-20%的内存来维持收集器工作。 2. 如何在并发标记阶段保收集线程与用户线程互不干扰的运行这里首先要解决的就是用户线程改变对象引用关系的问题需要保证不能打乱原来的图结构导致结果错误。cms通过增量更新解决而g1则是通过satb快照进行解决有点类似与mvcc里的readview参数的快照这个应该是类似于可重复读级别的快照。而用户线程在运行的时候肯定又会产生新对象这部分对象region每一个region设计了两个名为tams的指针把region的一部分空间分出来用来给这部分新对象进行分配。但是如回收速度赶不上对象创建的速度就又会导致fullGC与cms类似冻结用户线程产生长时间的stop the world。
3.如何建立起可靠的停顿预测模型呢 这里最好是更新region的统计状态这个状态越新肯定效果最好。当然停顿时间不是越短越好如果垃圾太多却来不及收集空间没了依然会发生fullGc的。
4.执行负载高
为了实现原始快照satb还需要使用写前屏障来跟踪并发时的指针变化相比增量更新虽然能减少并发标记和重新标记的损耗但在用户程序运行过程中会产生由跟踪引用变化带来的额外负担。
由于g1的写屏障比cms的复杂很多所以g1不像cms是同步的而是采取类似消息队列的结构去异步处理。
最后来看在小内存的应用上还是cms比较好但随着g1的不断优化这是谁也都说不好的事情。
低延迟垃圾收集器 这里不得不说一下前面垃圾回收器的思想的转变我的理解是由单线程到多线程。由只能暂停用户线程到可以部分和用户线程一起进行再到对管理的对象化整为零从整个堆内存变成分region的堆内存。
Shenandoah G1已经那么牛逼了。但是我们是不是还有优化空间呢随着计算机的发展硬件提升这个内存量是越来越能容忍大内存了。但是作文衡量垃圾回收器的指标内存占用延迟和吞吐量来说大内存带来的反而是低延迟因为回收小区和楼道的垃圾的时间是不一样的。那么在和用户并行的阶段不可避免地问题就是新对象的不断创建和旧对象的地址引用修改。 Shen的优化思路能否能让 回收的整个过程都是并发的呢
因此shen只有在初始标记和最终标记有短暂的停顿而这个停顿时间基本都是固定的与GC ROOt对象有关和堆大小对象数量没有正比例关系。 shen和G1有着相似的堆内存布局思想上几乎是一致那么我们来说说他们的不同了吧。
1.G1的回收阶段不可以与用户并发但是shen可以。
2.shen没有实现分代但并不代表着分代不重要。这个只不过权衡利弊后的结果不用花费大内存去维护记忆集而是利用连接矩阵来记录region的跨region引用问题这也降低了伪共享的概率。
它的细碎阶段分为初始标记并发标记最终标记并发清理并发回收初始引用更新并发引用更新和并发清理阶段。
其实最重要的阶段可以分为三个并发标记并发回收并发引用更新。
说精华并发标记是遍历处GcRoot全部可达的对象。时间长短取决于堆中对象存活对象的数量以及对象图结构的复杂度。
并发回收把存活的对象复制到其他未被使用的region里这点有点类似与copy on write了。但是旧的引用时很难改变到新的引用的这里用到了读屏障和转发指针来解决。时间长短取决于会收集的大小。
并发引用更新与用户线程并发时间长短取决于内存中涉及的引用数量的多少。并发引用更新和并发标记不同。它不再沿着对象图搜索吗而是按照内存物理地址顺序线性搜索索引引用类型把旧值更改为新的值旧可以了。最后一次停顿只与GCroot数量有关。
最后的并发清理此后没有任何存活对象了。
问题
这里要注意转发指针改变地址过程中更新某个对象的字段的问题。这个有点和指令交错的问题有点像所以这里采用了cas来保证成功实现。但是转发指针每一次使用都会产生一次额外的引用开销。
2.读屏障的过度使用
读屏障由于过于频繁所以会堆积大量的操作这里也是不希望有任何重量级的操作所以后期优化过程中将内存屏障模型改为引用访问屏障只拦截对象中数据类型为引用类型的读写操作而不会去管原生数据类型等其他非引用字段的读写。
成果
最大停顿时间没有实现10ms以内但是却也远远优于G1,CMS,PS了。因此是低延迟的垃圾回收器。但是吞吐量却下降了。
ZGC
ZGC的目标是希望在对吞吐量影响不大的目标下还能进一步优化低延迟的回收。
这里说一下它的不同点。堆内存布局这回不仅仅是面向region了还从灵活到具体为region分配了不同大小例如2MB的32MB的小型中型region还有大小不定的大region。 那么它的核心问题并发算法是如何实现的呢。这里与Shenandoah的转发指针不同且牛逼的是它采用了染色指针的技术。如果在对象头中存储信息例如哈希码锁记录等这样的但是如果对象存在被移动的可能性又如何能够访问成功呢。而ZGC的染色指针直接最存粹最直接直接把标记信息放到了引用对象的指针上了。 linux下尽管64位指针的高18位不能用来寻址但剩余的46位指针所能支持的依然可以满足大部分服务器的需要。所以ZGC把高四位提取出来存储4个标志信息。通过标志位虚拟机就能看到其引用对象的三色标记状态。
染色指针 染色指针可以使得一旦某个region存活的对象被移走之后这个region就能够被立刻释放掉和重用掉而不必等待整个堆中所有指向该region的对象被修正后才能清理这里可以与shen做一个横向对比。 染色指针可以大幅减少在垃圾回收过程中内存屏障使用数量。这些信息直接维护在指针中而且目前为止ZGc并未使用过写屏障只是用读屏障。一部分是因为使用了染色指针技术一部分是因为还没实现分代收集。 但是要顺利应用染色指针还需要解决一个问题。java虚拟机只是一个进程随意定义内存中指针的某几位数字操作系统大哥是否支持呢处理器大哥是否支持呢。这个问题在Solaris/SpARC平台就比较容易解决因为他是支持虚拟地址掩码的设置之后就可以直接忽略染色指针的标志位有点类似于子网掩码了。但是在X86-64平台上却没有这样的能力因此ZGC采用了虚拟内存映射技术。ZGC在Linux/x86-64上使用了多重地址映射将多个不同的虚拟内存地址映射到同一个物理内存地址上。这意味着ZGC在虚拟内存中看到的地址空间要比实际的堆内存容量更大。只需要将染色指针的标志位看成是分段符将这些不同的地址段都映射到同一个物理内存空间就好了这样多重映射后就可以用染色指针正常寻址了。
运行
它的阶段可以分为并发标记并发预备重分配并发重分配并发重映射。
并发标记阶段与G1,Shen不同的是zgc的标记是在指针上而不是对象上更新染色指针的mark0和marked1标志位。
并发预备重分配这里并不会像G1做收益优先回收反而是用范围更大的扫描成本去换取省去G1中记忆集的维护成本。因此ZGC重分配只决定了里面的存活对象会被复制到其他的region中复制过后原本的region会释放。这个标记过程针对全堆。
并发重分配核心。这个也是完成对旧对象到新对象得引用转换上。访问操作会被内存屏障截获从region得转发表上转发到新复制得对象并且修正新引用得值。直接指向新对象。与shen相比只有第一次访问会陷入转发其他就不会影响了因为它会自愈。
并发重映射这个也不是迫切需要做得工作因为染色指针有自愈能力它可以慢慢得改变旧的引用定位到新的引用上面去。这样旧的转发表就可以释放掉了。
缺点
zgc能接收的对象分配速率实际上不是很高因为zgc要准备对一个非常大的堆做垃圾回收并发收集因为他是并行的所以不回给用户感觉上的停顿创造的大量对象由于来不及进入当此标记的收集范围因此只能当作全部存活来看这就是大量的浮动垃圾如果告诉分配持续的话一次完整的并发收集周期会很长除了增大堆内存容量外更根本的方法是希望引入分带收集。
成果
zgc目前虽然还在实验状态但它的成果是非常显著的在吞吐量方面已经无线接近于PS直接超越G1在停顿时间延迟上面直接控制在10ms以内秒杀其他所有。相信它商业化之后会非常的牛逼。
好啦如果大家喜欢的化就点个赞吧分享整理笔记不易欢迎沟通交流
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/92519.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!