网站备案幕布尺寸,论坛类的网站怎么做,wordpress固定链接打不开,网站更换网址如何查找GC发展Java不像C或C那样#xff0c;需要程序员在编程的过程中#xff0c;时刻注意申请内存保存对象#xff0c;在对象使用完成后#xff0c;要在合适的时机将对象占用的内存释放掉(析构函数)#xff1b;Java得意与内部的三大机制#xff0c;保证了程序开发方便#xff1… GC发展Java不像C或C那样需要程序员在编程的过程中时刻注意申请内存保存对象在对象使用完成后要在合适的时机将对象占用的内存释放掉(析构函数)Java得意与内部的三大机制保证了程序开发方便解释执行 即时编译器JIT对热点代码的即时编译执行为机器在方法区中保存代码缓存执行引擎垃圾回收机制我们今天的重点就是要阐述垃圾回收机制。为什么要进行垃圾回收原因很简单现在的计算机系统都是跑在程序存储结果的计算机上的程序必须首先通过IO加载到内存中才能开始创建程序进程开始运行。而内存的大小是有上限的无休止地在内存中创建对象占用内存而不去清理必然是会造成内存不足导致操作系统将应用进程杀死。所以就需要java字节码要么字节码中管理内存申请内存扩展内存释放内存要么JVM自己提供垃圾回收机制要进行垃圾回收首先就要知道对象保存在哪里什么样的对象是垃圾对象保存在哪里对象在堆中保存小的对象可能会保存在栈的TLAB中JVM的内存区域分五个部分其实在实际中java所使用的内存包括有(后续补充)java内存 PC寄存器 java栈 本地栈 年轻代 老年代 直接内存空间堆的逻辑划分分别介绍如下程序计数器PC是一块较小的内存空间作用可以看做是当前线程所执行的字节码的行号的指示器线程私有。JVM方法栈和本地方法栈在sun的jdk中JVM方法栈和本地方法栈是算在一起的虚拟机栈为虚拟机执行java方法本地方法栈为虚拟机执行Native方法线程私有。java heap是虚拟机中内存区域最大的一块细分可以分为新生代和旧生带新生代可以划分为E、S0、S1其中S1和S0又可以叫做from 或 to线程共享。方法区存放了要加载的类的信息类中的静态变量定义为final的常量类中的Field信息方法信息等全局共享又叫做持久带可以通过 -XX:PermSize和-XX:MaxPermSize设置最小值和最大值线程共享。各个区的作用图示新生代 大多数情况下java中新建的对象都是在新生代上分配的新生代由Eden和两块相同大小的S0和S1组成其中S0和S1又称为From和To(这个划分没有先后顺序)可以通过-Xmn来设置新生代的大小-XXSurvivorRatio设置Eden和S区的比值有些垃圾回收器会对S0或者S1进行动态的调整。 之所以说大多数情况下新建的对象在新生代上分配是因为有两种情况下java新创建的对象会直接到旧生带一种是大的数组对象且对象中无外部引用的对象另外一种是通过启动参数上面进行设置-XXPretenureSizeThreshold1024(单位是字节)意思是对象超过此大小就直接分配到老年代的堆内存中此外并行垃圾回收器可以在运行期决定那些对象可以直接创建在旧生带。老年代多次回收之后仍然存活的对象大小是-Xms减去-Xmn。常见的参数设置-XX: 启用选项-XX:- 不启用选项-XX: 给选项设置一个数字类型值可跟单位例如 32k, 1024m, 2g-XX: 给选项设置一个字符串值例如-XX:HeapDumpPath./dump.core什么是垃圾对象Java采用的是可达性分析法。判断对象是否为垃圾的方法有两种引用计数法可达性分析算法可达性分析算法中有个非非常常的概念根集合引用链根集合(1)根集合包含了一组对象这些是对运行时数据区的对象快照的扫描主要包含有java虚拟机栈(栈帧中的本地变量表)中的引用的对象本地方法栈中JNI本地方法的引用对象。方法区中的类静态属性引用的对象方法区中的常量引用的对象。常驻的异常对象锁对象其他代的堆内存中对当前回收内存区域有引用关系的对象比如young gc时出现的老年代中的对象G1回收时Region中的remember set对象引用链从根集合出发开始遍历堆内存中的对象在引用链上的对象就是正在使用的对象不是垃圾没有在引用链上的对象就是垃圾垃圾回收器就要对其进行回收处理。STW的时机为了确保堆内存中的对象间的引用关系是不变的在进行确定根集合时需要将应用程序暂时停顿这也是后续垃圾回收器在努力奋斗进行攻克的一个重要参数停顿时间越小越好吞吐量越大越好。STW的时机安全点哪些时刻作为安全点方法开始执行方法返回之前异常抛出进入循环调用前安全区域哪些时间区域作为安全区域sleep()操作线程阻塞时垃圾回收算法三种垃圾回收算法和java最后选择的回收算法复制算法所有的年轻代的收集算法标记-清除算法CMS的垃圾回收算法标记-整理算法Parallel old的收集算法Java选择的垃圾回收算法分代回收算法将堆内存进行逻辑划分为根据对象的年龄大小划分为年轻代年老代保存在不同的代使用不同的回收算法。垃圾回收器第一阶段 串性回收用在客户端模式的java虚拟机上暂停应用线程单线程地回收垃圾对象第二阶段 并行回收暂停应用线程启动多个GC线程地回收垃圾对象对比串性回收的速度高了停顿的时间变短了但是仍是在整个回收过程中只有GC运行应用线程是不工作的第三阶段 并发回收在GC的过程中根据GC过程的不同阶段将部分阶段设置为GC线程和应用线程同时运行并且最大限度地减少GC造成的暂停时间这个过程包括有收集根对象初始标记并发标记最终标记并发清除举例CMS垃圾回收器第四阶段 标记和回收分离标记和回收都是并发的并且标记有标记的触发条件回收有回收的触发条件两个条件相互影响。举例G1ZGCCMS三个很重要的参数-XX:CMSInitiatingOccupancyFraction设置CMS收集器在老年代空间被使用多少(百分比)后触发垃圾收集。默认设置-XX:CMSInitiatingOccupancyFraction68表示老年代空间使用比例达到68%时触发CMS垃圾收集。仅当老年代收集器设置为CMS时候这个参数才有效。-XX:UseCMSCompactAtFullCollection设置CMS收集器在完成垃圾收集后是否要进行一次内存碎片整理。仅当老年代收集器设置为CMS时候这个参数才有效。-XX:CMSFullGCsBeforeCompaction设置CMS收集器在进行多少次垃圾收集后再进行一次内存碎片整理。如设置-XX:CMSFullGCsBeforeCompaction2表示CMS收集器进行了2次垃圾收集之后进行一次内存碎片整理。仅当老年代收集器设置为CMS时候这个参数才有效。与日志有关的三个参数- -XX:PrintGCDateStamps-XX:PrintGCDetails-Xloggc指定gc日志的存放位置。如-Xloggc:/var/log/myapp-gc.log表示将gc日志保存在磁盘/var/log/目录文件名为myapp-gc.log到底Java GC是在什么时候对什么东西做了什么事情(1)时间JVM进程启动后就会不断地在新生代中保存新创建的对象当Eden区满了后触发minor gc用的是复制算法当Survivor中的一半以上的对象存活年龄大于平均年龄时或者对象的年龄大于设置的参数-XXMaxTenuringThreshold时就会触发对象晋升从年轻代晋升到老年代保存当晋升到老年代的对象大小总和大于老年代剩余空间full gc时并且晋升担保机制设置为ture时就会直接尝试晋升老年代内存不足时触发老年代full gc或者小于时被HandlePromotionFailure参数强制full gcgc与非gc的耗时比较后超过了GCTimeRatio的限制引发OOM通过参数NewRatio控制年轻代和老年代的堆空间的比例通过参数SurvivorRatio控制年轻代中伊甸园区和幸存者区的比例通过参数-XX:MaxTenuringThreshold设置从年轻代晋升到老年代的对象年龄其中CMS是默认15G1默认是6GC关键技术三色标记法白色还没标记或者标记为了垃圾对象灰色标记一半属性没有标记完成黑色自己和自己的属性都已近全部标记完成。记忆集用来保存其他堆内存区对当前GC线程作用的堆内存区的引用关系卡表用来记录对其他代或其他Region中的对象有当前card的引用指向时就将当前card标记为dirty并且在垃圾回收时将dirty保存到drity card queue中目的在进行垃圾回收时防止对全堆进行扫描从而降低并发标记或最终标记的暂停时间全称是Remembered Set是辅助GC过程的一种结构典型的空间换时间工具和Card Table有些类似。还有一种数据结构也是辅助GC的Collection Set(CSet)它记录了GC要收集的Region集合集合里的Region可以是任意年代的。在GC的时候对于old-young和old-old的跨代对象引用只要扫描对应的CSet中的RSet即可。逻辑上说每个Region都有一个RSetRSet记录了其他Region中的对象引用本Region中对象的关系属于points-into结构(谁引用了我的对象)。而Card Table则是一种points-out(我引用了谁的对象)的结构每个Card 覆盖一定范围的Heap(一般为512Bytes)。G1的RSet是在Card Table的基础上实现的每个Region会记录下别的Region有指向自己的指针并标记这些指针分别在哪些Card的范围内。这个RSet其实是一个Hash TableKey是别的Region的起始地址Value是一个集合里面的元素是Card Table的Index。下图表示了RSet、Card和Region的关系SATB当G1开始进行回收时为了确保堆内存中对象之间的引用关系不变而获取一个内存快照进行并发标记的作用对象就是这个SATB内存快照CMS和G1算法都涉及对可达对象的并发标记。并发标记的主要问题是collector在标记对象的过程中mutator可能正在改变对象引用关系图从而造成漏标和错标。错标不会影响程序的正确性只是造成所谓的浮动垃圾。但漏标则会导致可达对象被当做垃圾收集掉从而影响程序的正确性。为解决漏标问题GC Handbook一书首先将对象分为三类即所谓的black对象grey对象和white对象。white对象是那些还没有被collector标记到的对象grey对象是那些自身已经被标记到但其所有引用字段还没有处理的对象而black对象则是自身已经被标记到且其引用的所有对象也已经被标记的对象。基于上述分类一个white对象在并发标记阶段会被漏标的充分必要条件是1、mutator插入了一个从black对象到该white对象的新引用2、mutator删除了所有从grey对象到该white对象的直接或者间接引用。因此要避免对象的漏标只需要打破上述2个条件中的任何一个即可。CMSIncremental update关注的是第一个条件的打破即引用关系的插入。Incremental update利用write barrier将所有新插入的引用关系都记录下来最后以这些引用关系的src为根STW地重新扫描一遍即避免了漏标问题。G1SATB关注的是第二个条件的打破即引用关系的删除。SATB利用pre write barrier将所有即将被删除的引用关系的旧引用记录下来最后以这些旧引用为根STW地重新扫描一遍即可避免漏标问题。在G1中使用的是STAB(snapshot-at-the-beginning)的方式删除的时候记录所有的对象它有3个步骤1在开始标记的时候生成一个快照图标记存活对象2在并发标记的时候所有被改变的对象入队(在write barrier里把所有旧的引用所指向的对象都变成非白的)3可能存在游离的垃圾将在下次被收集G1的写前屏障G1在并发标记过程中当对象与对象之间的引用关系发生变化时就会将引用断开的对象保存起来在并发阶段的后期重新对这些对象进行扫描标记目的防止对象消失CMS的增量更新CMS在进行并发标记过程中当对象间的引用关系发生变化时就会引用发生变化的对象利用增量更新的条件使用写后屏障技术保存起来在最终标记阶段将会把根集合和并发标记阶段引用发生变化的对象为新的根集合重新扫描一遍所有的对象目的防止对象消失缺点可能存在浮动垃圾染色指针多个虚拟内存地址根据地址中特殊位置的标记位用来表示哪个地址段是有效的从而实现多个虚拟内存地址对应一个物理内存地址可支持最大内存空间为16TG1的标记过程概述STAB全称Snapshot-At-The-Beginning由字面理解是GC开始时活着的对象的一个快照。它是通过Root Tracing得到的作用是维持并发GC的正确性。那么它是怎么维持并发GC的正确性的呢根据三色标记算法我们知道对象存在三种状态白对象没有被标记到标记阶段结束后会被当做垃圾回收掉即灰色节点的子节点。灰对象被标记了但是它的field还没有被标记或标记完。黑对象被标记了且它的所有field也被标记完了。由于并发阶段的存在那就有可能在并行运行期间之前的标记过的对象的引用关系可能被改变就会出现白对象漏标的情况这种情况发生的前提是把一个白对象的引用存到黑对象的字段里,如果这个情况发生因为标记为黑色的对象认为是扫描完成的不会再对它进行扫描。某个白对象失去了所有能从灰对象到达它的引用路径。对于第一个条件在并发标记阶段如果该白对象是new出来的并没有被灰对象持有那么它会不会被漏标呢如果灰对象到白对象的直接引用或者间接引用被替换了或者删除了白对象就会被漏标从而导致被回收掉这是非常严重的错误。解决新创建对象产生的漏标问题 SATB算法机制中会在GC开始时先创建一个对象快照在并发标记时所有快照中当时的存活对象就认为是存活的标记过程中新分配的对象也会被标记为存活对象不会被回收。这种机制能够很好解决新创建对象漏标的情况。STAB核心的两个结构就是两个Bitmap。 Bitmap分别存储在每个Region中并发标记过程里的两个重要的变量preTAMS(pre-top-at-mark-start代表着Region上一次完成标记的位置) 以及nextTAMS(next-top-at-mark-start随着标记的进行会不断移动一开始在top位置)。SATB通过控制两个变量的移动来进行标记移动规则如下假设第n轮并发标记开始将该Region当前的Top指针赋值给nextTAMS在并发标记标记期间分配的对象都在[ nextTAMS, Top ]之间SATB能够确保这部分的对象都会被标记默认都是存活的。当并发标记结束时将nextTAMS所在的地址赋值给previousTAMSSATB给[ Bottom, previousTAMS ]之间的对象创建一个快照Bitmap所有垃圾对象能通过快照被识别出来。第n1轮并发标记开始过程和第n轮一样。A阶段初始标记阶段需要STW将扫描Region的Top值赋值给nextTAMS。A-B阶段并发标记阶段。B阶段并发标记结束阶段此时并发标记阶段生成的新对象都会被分配在[nextTAMSTop]之间这些对象会被定义为“隐式对象”同时_next_mark_bitmap也开始存储nextTAMS标记的对象的地址。C阶段清除阶段_next_mark_bitmap和_prev_mark_bitmap会进行交换同时清理[ Bottom, previousTAMS ]之间被标记的所有对象对于“隐式对象”会在下次垃圾收集过程进行回收(如第F步)这也是SATB存在弊端会一定程度产生未能在本次标记中识别的浮动垃圾。解决对象引用被修改产生的漏标问题 SATB利用pre-write barrier将所有即将被修改引用关系的白对象旧引用记录下来最后以这些旧引用为根重新扫描一遍以解决白对象引用被修改产生的漏标问题。在引用修改时把原引用保存到satb_mark_queue中每个线程都自带一个satb_mark_queue。在下一次的并发标记阶段会依次处理satb_mark_queue中的对象确保这部分对象在本轮GC中是存活的。 如果被修改引用的白对象就是要被收集的垃圾这次的标记会让它躲过GC这就是float garbage。因为SATB的做法精度比较低所以造成的float garbage也会比较多。总结 本文主要对GC发展做了详述分布式高并发的环境造就了GC不断发展要在最短的时间内给用户响应最多的返回数据就需要后端服务有很强的响应能力就需要GC的暂停时间段GC回收的效率越高越好。