当前位置: 首页 > news >正文

需要做网站建设和推广的行业在线式crm

需要做网站建设和推广的行业,在线式crm,广州万户网站公司,网络体系结构一、前言 1.自动内存管理 有句经典的话是这样说#xff0c;Java与C之间有一堵由内存动态分配和垃圾收集技术所围成的高墙#xff0c;墙外面的人想进去#xff0c;墙里面的人却想出来。对于Java程序员来说#xff0c;在虚拟机自动内存管理机制的帮助下#xff0c;不再需要…一、前言 1.自动内存管理 有句经典的话是这样说Java与C之间有一堵由内存动态分配和垃圾收集技术所围成的高墙墙外面的人想进去墙里面的人却想出来。对于Java程序员来说在虚拟机自动内存管理机制的帮助下不再需要为每一个new操作去写配对的delete/free代码不容易出现内存泄漏和内存溢出问题看起来由虚拟机管理内存一切都很美好。不 过也正是因为Java程序员把控制内存的权力交给了Java虚拟机一旦出现内存泄漏和溢出方面的问 题如果不了解虚拟机是怎样使用内存的那排查错误、修正问题将会成为一项异常艰难的工作。 今天主要分享的JVM垃圾回收相关的知识既然有自动内存管理那就有垃圾回收关于垃圾回收其实就是三件事也是我今天要分享的三件事。 二、正文 1、哪些内存需要回收? 2.、什么时候回收? 3、如何回收? 第一件事哪些内存需要回收 其实答案很简单就是垃圾即不存活的对象需要回收。那是不是很简单就这结束了 其实这件事有意思的点在于如果判断对象是垃圾 Java用的是什么可达性分析算法。此算法的思路通过一系列称为“GCRoots”的根对象作为起始节点集从这些节点开始根据引用关系向下搜索搜索过程所走过的路径称为“引用链”(ReferenceChain)如果某个对象到GCRoots间没有任何引用链相连或者用图论的话来说就是从GCRoots到这个对象不可达时则证明此对象是不可能再被使用的。 除了可达性分析算法有其他常见算法引用计数法。在对象中添加一个引用计数器每当有一个地方 引用它时计数器值就加一;当引用失效时计数器值就减一;任何时刻计数器为零的对象就是不可 能再被使用的。 为什么选择可达性分析算法或者换句话说可达性分析算法有什么好处引用计数法有什么缺点 对于此问题的答案八股文的回答循环引用无法解决。如果两个对象再无任何引用但是它们因为互相引用着对方导致它们的引用计数都不为零引用计数算法也就无法回收它们。那Python呢用的是引用计算算法是不是也有此问题无法解决其实通过更高级的引用计数实现可以解决。 其实基于引用计数与基于trace这两大类别的自动内存管理方式最大的不同之处在于引用计数只需要局部信息而基于trace需要全局信息。 在内存充裕的前提下tracingGC的整体开销比引用计数方式更低一些所以吞吐量throughput高一些。因为引用计数方式通常需要统计冗余的局部信息而tracingGC则可以通过全局信息一口气批量判断对象的生死如果是带整理的tracingGC则其内存分配通常也会更快。 不过tracingGC通常会比引用计数方式的延迟latency大一些而且内存越紧张的时候tracingGC的效率反而越低所以在内存不太充裕的地方使用引用计数仍然是个合理的选择例如iOS5上的ARC。我这边引用的是R哥的回答感兴趣的话可以自己查相关资料详细了解。 既然Java用的是可达性算法那必然会有两个缺点是那两个缺点呢一是要延迟释放内存而不是在一个对象引用计数为0时立即释放内存。二是stoptheworld根节点枚举这一步骤时都是必须暂停用户线程的从JDK1.3开始一直到现在最新的JDK13HotSpot虚拟机开发团队为消除或者降低用户线程因垃圾收集而导致停顿的努力一直持续进行着从Serial收集器到Parallel收集器再到ConcurrentMarkSweep(CMS)和GarbageFirst(G1)收集器最终至现在垃圾收集器的最前沿成果Shenandoah和ZGC等我们看到了一个个越来越构思精巧越来越优秀也越来越复杂的垃圾收集器不断涌现用户线程的停顿时间在持续缩短ZGC据说停顿不超过10ms但是仍然没有办法彻底消除。 那在Java技术体系里面哪些对象可以作为GCRoots的呢基本上是分两类一类是固定的另一类是动态的。 固定的有在虚拟机栈(栈帧中的本地变量表)中引用的对象譬如各个线程被调用的方法堆栈中使用到的参数、局部变量、临时变量等 在方法区中类静态属性引用的对象譬如Java类的引用类型静态变量。 在方法区中常量引用的对象譬如字符串常量池(StringTable)里的引用。 在本地方法栈中JNI(即通常所说的Native方法)引用的对象。·Java虚拟机内部的引用如基本数据类型对应的Class对象一些常驻的异常对象(比如NullPointExcepiton、OutOfMemoryError)等还有系统类加载器。·所有被同步锁(synchronized关键字)持有的对象。 反映Java虚拟机内部情况的JMXBean、JVMTI中注册的回调、本地代码缓存等。 除了这些固定的GCRoots集合以外根据用户所选用的垃圾收集器以及当前回收的内存区域不同还可以有其他对象“临时性”地加入共同构成完整GCRoots集合。譬如后文将会提到的分代收集和局部回收(PartialGC)如果只针对Java堆中某一块区域发起垃圾收集时(如最典型的只针对新生代的垃圾收集)必须考虑到内存区域是虚拟机自己的实现细节(在用户视角里任何内存区域都是不可见的)更不是孤立封闭的所以某个区域里的对象完全有可能被位于堆中其他区域的对象所引用说白了就是年轻代可能引用老年代的这时候就需要将这些关联区域的对象也一并加入GCRoots集合中去才能保证可达性分析的正确性。 在CMS之前最多有并行回收没有并发回收即进行垃圾收集时必须暂停其他所有工作线程直到它收集结束。这里解释下在垃圾收集器语境里并行和并发的概念。 并行(Parallel):并行描述的是多条垃圾收集器线程之间的关系说明同一时间有多条这样的线程在协同工作通常默认此时用户线程是处于等待状态。 并发(Concurrent):并发描述的是垃圾收集器线程与用户线程之间的关系说明同一时间垃圾收集器线程与用户线程都在运行。由于用户线程并未被冻结所以程序仍然能响应服务请求但由于垃圾收集器线程占用了一部分系统资源此时应用程序的处理的吞吐量将受到一定影响。 例如原来的单线程回收SerialParNew到多线程并行回收(Parallel ScavengeParallel Old)他们回收就比较简单垃圾线程运行时StopTheWorld用户线程被冻结只不过一个是单线程一个是多线程。“StopTheWorld”这个词语也许听起来很酷但这项工作是由虚拟机在后台自动发起和自动完成的在用户不可知、不可控的情况下把用户的正常工作的线程全部停掉这对很多应用来说都是不能接受的例如这次channelmanger的问题。早期HotSpot虚拟机的设计者们表示完全理解但也同时表示非常委屈:“你妈妈在给你打扫房间的时候肯定也会让你老老实实地在椅子上或者房间外待着如果她一边打扫你一边乱扔纸屑这房间还能打扫完?”这也是CMS研究那么长的时间原因。那CMS之后包括CMSG1是怎么实现可达性分析的 这就设计到三色标记了。所谓三色标记是把遍历对象图过程中遇到的对象按照“是否访问过”这个条件标记成以下三种颜色。 白色:表示对象尚未被垃圾收集器访问过。显然在可达性分析刚刚开始的阶段所有的对象都是白色的若在分析结束的阶段仍然是白色的对象即代表不可达。 黑色:表示对象已经被垃圾收集器访问过且这个对象的所有引用都已经扫描过。黑色的对象代表已经扫描过它是安全存活的如果有其他对象引用指向了黑色对象无须重新扫描一遍。黑色对象不可能直接(不经过灰色对象)指向某个白色对象。 灰色:表示对象已经被垃圾收集器访问过但这个对象上至少存在一个引用还没有被扫描过。 三色标记大家已经了解但是三色标记会产生两个问题是那两个问题 一种错标是把原本消亡的对象错误标记为存活如图所示。另一种是漏标如图所示。错标其实还好是可以容忍的只不过产生了一点逃过本次收集的浮动垃圾而已下次收集清理掉就好这也是CMSG1会产生浮动垃圾的原因。漏标是无法接受的会产生非常严重的后果。那错标怎么解决呢首先要知道什么条件下回产生错标问题。关于这问题已经证明在理论上证明了当且仅当以下两个条件同时满足时会产生“对象消失”的问题即原本应该是黑色的对象被误标为白色: 一是赋值器插入了一条或多条从黑色对象到白色对象的新引用; 二是赋值器删除了全部从灰色对象到该白色对象的直接或间接引用。 因此我们要解决并发扫描时的对象消失问题只需破坏这两个条件的任意一个即可。由此分别产生了两种解决方案:增量更新(Incremental Update)和原始快照(Snapshot At The BeginningSATB)。 首先是增量更新增量更新要破坏的是第一个条件当黑色对象插入新的指向白色对象的引用关系时就将这个新插入的引用记录下来等并发扫描结束之后再将这些记录过的引用关系中的黑色对象为根重新扫 描一次。 CMS关注新增但是会把新增对象的前一个引用变为灰色重新对所有子对象进行遍历有一定性能损耗。G1引用的打破不需要重新遍历对象因此效率较高。但是维护快照以及其保守策略增加了一定的内存成本。当然这里面有很多细节例如G1的增量更新是存在那里感兴趣的可以自行查资料。 下面讲第二件事什么时候回收 主要分两个一是主动显示回收System.gc()(通知jvm进行一次垃圾回收具体执行还要看JVM。另一个是自动隐式回收当任何某一个区域不够时就会进行GC。可以看下图图里有个Virtual暂时可以忽略在JVM内部如果Xms小于Xmx堆的大小并不会直接扩展至其上限也就是说保留的空间reserved大于实际能够使用的空间committed。当内存需求不断增大时JVM会扩展所以Virtual区是暂时不使用区域。但是咱们一直设置的最大堆大小和最小堆大小是一样的所以是没有Virtual区的。 从抽象逻辑上方法区确实是堆的一部分 虚拟机规范里面说了方法区属于堆但是它有一个别名叫作“非堆”(Non-Heap)。 虚拟机规范Chapter 2. The Structure of the Java Virtual Machine 从物理实现上简单来看Hotspot实现方法区1.8以前是永久代1.8是matespace。1.6是堆永久代1.7堆永久代1.8 堆metaspace。最大区别metaspace是直接内存永久代是jvm内存1.7移动interned Strings 和 class statics 到堆详情见JEP 122: Remove the Permanent Generation Eden满了不能分配新的对象的了会进行新生代收集称之为Minor GC/Young GC。 老年代满了会进行老年代收集称之为Major GC/Old GC。目前只有CMS收集器会有单独收集老年代的行为。另外请注意“Major GC”这个说法现在有点混淆在不同资料上常有不同所指 读者需按上下文区分到底是指老年代的收集还是整堆收集。 G1收集器会有混合收集收集整个新生代以及部分老年代称之Mixed GC。 整堆收集(Full GC):收集整个Java堆和方法区的垃圾收集。 现在可以打开普罗米修斯看下相关指标 https://metrics.xtadmins.com/d/vLl76-x4k/jvm-micrometer?orgId1 最后讲第三件如何回收 前面讲了Java主流的虚拟机用的都是“追踪式垃圾收集”(Tracing GC)下面介绍的所有算法均属于追踪式垃圾收集的范畴。 标记-清除算法 最早出现也是最基础的垃圾收集算法是“标记-清除”(Mark-Sweep)算法在1960年由Lisp之父John McCarthy所提出。如它的名字一样算法分为“标记”和“清除”两个阶段:首先标记出所有需要回收的对象在标记完成后统一回收掉所有被标记的对象也可以反过来标记存活的对象统一回收所有未被标记的对象。标记过程就是对象是否属于垃圾的判定过程这在前一节讲述垃圾对象标记判定算法时其实已经介绍过了。 之所以说它是最基础的收集算法是因为后续的收集算法大多都是以标记-清除算法为基础对其缺点进行改进而得到的。它的主要缺点有两个:第一个是执行效率不稳定如果Java堆中包含大量对象而且其中大部分是需要被回收的这时必须进行大量标记和清除的动作导致标记和清除两个过程的执行效率都随对象数量增长而降低;第二个是内存空间的碎片化问题标记、清除之后会产生大量不连续的内存碎片空间碎片太多可能会导致当以后在程序运行过程中需要分配较大对象时无法找到足够的连续内存而不得不提前触发另一次垃圾收集动作。 标记-复制算法 标记-复制算法常被简称为复制算法。为了解决标记-清除算法面对大量可回收对象时执行效率低的问题1969年Fenichel提出了一种称为“半区复制”(Semispace Copying)的垃圾收集算法它将可用内存按容量划分为大小相等的两块每次只使用其中的一块。当这一块的内存用完了就将还存活着的对象复制到另外一块上面然后再把已使用过的内存空间一次清理掉。如果内存中多数对象都是存活的这种算法将会产生大量的内存间复制的开销但对于多数对象都是可回收的情况算法需要复制的就是占少数的存活对象而且每次都是针对整个半区进行内存回收分配内存时也就不用考虑有空间碎片的复杂情况只要移动堆顶指针按顺序分配即可。这样实现简单运行高效不过其缺陷也显而易见这种复制回收算法的代价是将可用内存缩小为了原来的一半空间浪费未免太多了一点。 现在的商用Java虚拟机大多都优先采用了这种收集算法去回收新生代IBM公司曾有一项专门研究对新生代“朝生夕灭”的特点做了更量化的诠释——新生代中的对象有98%熬不过第一轮收集。因此并不需要按照1∶1的比例来划分新生代的内存空间。 在1989年Andrew Appel针对具备“朝生夕灭”特点的对象提出了一种更优化的半区复制分代策略现在称为“Appel式回收”。HotSpot虚拟机的Serial、ParNew等新生代收集器均采用了这种策略来设计新生代的内存布局[1]。Appel式回收的具体做法是把新生代分为一块较大的Eden空间和两块较小的Survivor空间每次分配内存只使用Eden和其中一块Survivor。发生垃圾搜集时将Eden和Survivor中仍然存活的对象一次性复制到另外一块Survivor空间上然后直接清理掉Eden和已用过的那块Survivor空间。HotSpot虚拟机默认Eden和Survivor的大小比例是8∶1也即每次新生代中可用内存空间为整个新生代容量的90%(Eden的80%加上一个Survivor的10%)只有一个Survivor空间即10%的新生代是会被“浪费”的。当然98%的对象可被回收仅仅是“普通场景”下测得的数据任何人都没有办法百分百保证每次回收都只有不多于10%的对象存活因此Appel式回收还有一个充当罕见情况的“逃生门”的安全设计当Survivor空间不足以容纳一次MinorGC之后存活的对象时就需要依赖其他内存区域(实际上大多就是老年代)进行分配担保(Handle Promotion)。 标记-整理算法 标记-复制算法在对象存活率较高时就要进行较多的复制操作效率将会降低。更关键的是如果不想浪费50%的空间就需要有额外的空间进行分配担保以应对被使用的内存中所有对象都100%存活的极端情况所以在老年代一般不能直接选用这种算法。 针对老年代对象的存亡特征1974年Edward Lueders提出了另外一种有针对性的“标记-整理”(Mark-Compact)算法其中的标记过程仍然与“标记-清除”算法一样但后续步骤不是直接对可回收对象进行清理而是让所有存活的对象都向内存空间一端移动然后直接清理掉边界以外的内存“标记-整理”算法的示意图如图3-4所示。 如果移动存活对象尤其是在老年代这种每次回收都有大量对象存活区域移动存活对象并更新所有引用这些对象的地方将会是一种极为负重的操作而且这种对象移动操作必须全程暂停用户应用程序才能进行[1]这就更加让使用者不得不小心翼翼地权衡其弊端了像这样的停顿被最初的虚拟机设计者形象地描述为“Stop The World”[2]。但如果跟标记-清除算法那样完全不考虑移动和整理存活对象的话弥散于堆中的存活对象导致的空间碎片化问题就只能依赖更为复杂的内存分配器和内存访问器来解决。譬如通过“分区空闲分配链表”来解决内存分配问题(计算机硬盘存储大文件就不要求物理连续的磁盘空间能够在碎片化的硬盘上存储和访问就是通过硬盘分区表实现的)。内存的访问是用户程序最频繁的操作甚至都没有之一假如在这个环节上增加了额外的负担势必会直接影响应用程序的吞吐量。 基于以上两点是否移动对象都存在弊端移动则内存回收时会更复杂不移动则内存分配时会更复杂。从垃圾收集的停顿时间来看不移动对象停顿时间会更短甚至可以不需要停顿但是从整个程序的吞吐量来看移动对象会更划算。此语境中吞吐量的实质是赋值器(Mutator可以理解为使用垃圾收集的用户程序本书为便于理解多数地方用“用户程序”或“用户线程”代替)与收集器的效率总和。即使不移动对象会使得收集器的效率提升一些但因内存分配和访问相比垃圾收集频率要高得多这部分的耗时增加总吞吐量仍然是下降的。HotSpot虚拟机里面关注吞吐量的Parallel Scavenge收集器是基于标记-整理算法的而关注延迟的CMS收集器则是基于标记-清除算法的这也从侧面印证这点。 另外还有一种“和稀泥式”解决方案可以不在内存分配和访问上增加太大额外负担做法是让虚拟机平时多数时间都采用标记-清除算法暂时容忍内存碎片的存在直到内存空间的碎片化程度已经大到影响对象分配时再采用标记-整理算法收集一次以获得规整的内存空间。前面提到的基于标记-清除算法的CMS收集器面临空间碎片过多时采用的就是这种处理办法 既然要讲G1那得先讲CMSG1是从CMS改进过来的。CMS是一个划时代的产物虽然他有很多缺点但是他开辟了并发回收的先河。在讲CMS垃圾回收之前需要强调下CMS只进行老年代的回收新生代还是Serial或者ParNew打开普罗指示一下。 CMS(Concurrent Mark Sweep) CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器。目前很大一部分的Java应用集中在互联网网站或者基于浏览器的B/S系统的服务端上这类应用通常都会较为关注服务的响应速度希望系统停顿时间尽可能短以给用户带来良好的交互体验。CMS收集器就非常符合这类应用的需求。 从名字(包含“Mark Sweep”)上就可以看出CMS收集器是基于标记-清除算法实现的它的运作过程相对于前面几种收集器来说要更复杂一些整个过程分为四个步骤包括: 1)初始标记(CMS initial mark)  2)并发标记(CMS concurrent mark)  3)重新标记(CMS remark)  4)并发清除(CMSconcurrent sweep) 其中初始标记、重新标记这两个步骤仍然需要“Stop The World”。初始标记仅仅只是标记一下GC Roots能直接关联到的对象速度很快;并发标记阶段就是从GC Roots的直接关联对象开始遍历整个对象图的过程这个过程耗时较长但是不需要停顿用户线程可以与垃圾收集线程一起并发运行;而重新标记阶段则是为了修正并发标记期间因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录(详见关于增量更新的讲解)这个阶段的停顿时间通常会比初始标记阶段稍长一些但也远比并发标记阶段的时间短;最后是并发清除阶段清理删除掉标记阶段判断的已经死亡的对象由于不需要移动存活对象所以这个阶段也是可以与用户线程同时并发的。 由于在整个过程中耗时最长的并发标记和并发清除阶段中垃圾收集器线程都可以与用户线程一起工作所以从总体上来说CMS收集器的内存回收过程是与用户线程一起并发执行的。通过图可以比较清楚地看到CMS收集器的运作步骤中并发和需要停顿的阶段。 首先CMS收集器对处理器资源非常敏感。事实上面向并发设计的程序都对处理器资源比较敏感。在并发阶段它虽然不会导致用户线程停顿但却会因为占用了一部分线程(或者说处理器的计算能力)而导致应用程序变慢降低总吞吐量。CMS默认启动的回收线程数是(处理器核心数量3)/4也就是说如果处理器核心数在四个或以上并发回收时垃圾收集线程只占用不超过25%的处理器运算资源并且会随着处理器核心数量的增加而下降。但是当处理器核心数量不足四个时CMS对用户程序的影响就可能变得很大。如果应用本来的处理器负载就很高还要分出一半的运算能力去执行收集器线程就可能导致用户程序的执行速度忽然大幅降低。为了缓解这种情况虚拟机提供了一种称为“增量式并发收集器”(Incremental Concurrent Mark Sweep/i-CMS)的CMS收集器变种所做的事情和以前单核处理器年代PC机操作系统靠抢占式多任务来模拟多核并行多任务的思想一样是在并发标记、清理的时候让收集器线程、用户线程交替运行尽量减少垃圾收集线程的独占资源的时间这样整个垃圾收集的过程会更长但对用户程序的影响就会显得较少一些直观感受是速度变慢的时间更多了但速度下降幅度就没有那么明显。实践证明增量式的CMS收集器效果很一般从JDK7开始i-CMS模式已经被声明为“deprecated”即已过时不再提倡用户使用到JDK9发布后i-CMS模式被完全废弃。 然后由于CMS收集器无法处理“浮动垃圾”(FloatingGarbage)有可能出现“Con-current Mode Failure”失败进而导致另一次完全“Stop The World”的Full GC的产生。在CMS的并发标记和并发清理阶段用户线程是还在继续运行的程序在运行自然就还会伴随有新的垃圾对象不断产生但这一部分垃圾对象是出现在标记过程结束以后CMS无法在当次收集中处理掉它们只好留待下一次垃圾收集时再清理掉。这一部分垃圾就称为“浮动垃圾”。同样也是由于在垃圾收集阶段用户线程还需要持续运行那就还需要预留足够内存空间提供给用户线程使用因此CMS收集器不能像其他收集器那样等待到老年代几乎完全被填满了再进行收集必须预留一部分空间供并发收集时的程序运作使用。在JDK5的默认设置下CMS收集器当老年代使用了68%的空间后就会被激活这是一个偏保守的设置如果在实际应用中老年代增长并不是太快可以适当调高参数-XX:CMSInitiatingOccu-pancyFraction的值来提高CMS的触发百分比降低内存回收频率获取更好的性能。到了JDK6时CMS收集器的启动阈值就已经默认提升至92%。但这又会更容易面临另一种风险:要是CMS运行期间预留的内存无法满足程序分配新对象的需要就会出现一次“并发失败”(Concurrent Mode Failure)这时候虚拟机将不得不启动后备预案:冻结用户线程的执行临时启用Serial Old收集器来重新进行老年代的垃圾收集但这样停顿时间就很长了。所以参数-XX:CMSInitiatingOccupancyFraction设置得太高将会很容易导致大量的并发失败产生性能反而降低用户应在生产环境中根据实际应用情况来权衡设置。 还有最后一个缺点在本节的开头曾提到CMS是一款基于“标记-清除”算法实现的收集器这意味着收集结束时会有大量空间碎片产生空间碎片过多时将会给大对象分配带来很大麻烦所以CMS一定会触发FullGC。往往会出现老年代还有很多剩余空间但就是无法找到足够大的连续空间来分配当前对象而不得不提前触发一次FullGC的情况。为了解决这个问题CMS收集器提供了一个-XX:UseCMS-CompactAtFullCollection开关参数(默认是开启的此参数从JDK9开始废弃)用于在CMS收集器不得不进行FullGC时开启内存碎片的合并整理过程由于这个内存整理必须移动存活对象(在Shenandoah和ZGC出现前)是无法并发的。这样空间碎片问题是解决了但停顿时间又会变长因此虚拟机设计者们还提供了另外一个参数-XX:CMSFullGCsBefore-Compaction(此参数从JDK9开始废弃)这个参数的作用是要求CMS收集器在执行过若干次(数量由参数值决定)不整理空间的FullGC之后下一次进入FullGC前会先进行碎片整理(默认值为0表示每次进入FullGC时都进行碎片整理), CMS老年代垃圾回收停顿时间不可控对应新生代的时间停顿时间也不可控CMS还是JVM中唯一一款抢占式垃圾回收器在CMS设计中老年代的回收即可以主动进行有可以被动进行。主动进行回收指的是老生代按照固定的时间间隔有可能会和被动回收冲突导致CMS也比较复杂。 Garbage First收集器 可控的停顿时间是交互式应用的刚性需求为什么CMS的停顿时间不可控呢该如何设计来尽量避免停顿时间不可控 1新生代回收中停顿时间不可控的原因主要是新生代大小是固定的在回收过程中无洗根据应用使用内存的情况来调整新生代大小。如果新生代的大小能够根据应用使用内存的情况做出调整那么从理论上讲就能解决停顿时间不可控的问题。 2老生代回收中的初始标记和再标记的引起的停顿时间不可控主要是对象个数不同导致的可以通过并发处理来限制处理对象的个数。 3解决碎片化唯一的方法是不再采用空闲列表的分配方法从而避免因为碎片化而导致的垃圾回收。 插入一个知识假设Java堆中内存是绝对规整的所有被使用过的内存都被放在一边空闲的内存被放在另一边中间放着一个指针作为分界点的指示器那所分配内存就仅仅是把那个指针向空闲空间方向挪动一段与对象大小相等的距离这种分配方式称为“指针碰撞”(Bump The Pointer)。但如果Java堆中的内存并不是规整的已被使用的内存和空闲的内存相互交错在一起那 就没有办法简单地进行指针碰撞了虚拟机就必须维护一个列表记录上哪些内存块是可用的在分配的时候从列表中找到一块足够大的空间划分给对象实例并更新列表上的记录这种分配方式称为“空闲列表”(Free List)。 作为CMS收集器的替代者和继承人设计者们希望做出一款能够建立起“停顿时间模型”(Pause PredictionModel)的收集器停顿时间模型的意思是能够支持指定在一个长度为M毫秒的时间片段内消耗在垃圾收集上的时间大概率不超过N毫秒这样的目标这几乎已经是实时Java(RTSJ)的中软实时垃圾收集器特征了。 在G1收集器出现之前的所有 其他收集器包括CMS在内垃圾收集的目标范围要么是整个新生代(Minor GC)要么就是整个老 年代(M ajor GC)再要么就是整个Java堆(Full GC)。而G1跳出了这个樊笼它可以面向堆内存任 何部分来组成回收集(Collection Set一般简称CSet)进行回收衡量标准不再是它属于哪个分代而 是哪块内存中存放的垃圾数量最多回收收益最大这就是G1收集器的Mixed GC模式。 G1开创的基于Region的堆内存布局是它能够实现这个目标的关键。虽然G1也仍是遵循分代收集理论设计的但其堆内存的布局与其他收集器有非常明显的差异:G1不再坚持固定大小以及固定数量的分代区域划分而是把连续的Java堆划分为多个大小相等的独立区域(Region)每一个Region都可以根据需要扮演新生代的Eden空间、Survivor空间或者老年代空间。收集器能够对扮演不同角色的Region采用不同的策略去处理这样无论是新创建的对象还是已经存活了一段时间、熬过多次收集的旧对象都能获取很好的收集效果。 Region中还有一类特殊的Humongous区域专门用来存储大对象。G1认为只要大小超过了一个Region容量一半的对象即可判定为大对象。每个Region的大小可以通过参数-XX:G1HeapRegionSize设定取值范围为1MB~32MB且应为2的N次幂。而对于那些超过了整个Region容量的超级大对象将会被存放在N个连续的HumongousRegion之中G1的大多数行为都把HumongousRegion作为老年代的一部分来进行看待如图3-12所示。 虽然G1仍然保留新生代和老年代的概念但新生代和老年代不再是固定的了它们都是一系列区域(不需要连续)的动态集合。G1收集器之所以能建立可预测的停顿时间模型是因为它将Region作为单次回收的最小单元即每次收集到的内存空间都是Region大小的整数倍这样可以有计划地避免在整个Java堆中进行全区域的垃圾收集。更具体的处理思路是让G1收集器去跟踪各个Region里面的垃圾堆积的“价值”大小价值即回收所获得的空间大小以及回收所需时间的经验值然后在后台维护一个优先级列表每次根据用户设定允许的收集停顿时间(使用参数-XX:MaxGCPauseMillis指定默认值是200毫秒)优先处理回收价值收益最大的那些Region这也就是“GarbageFirst”名字的由来。这种使用Region划分内存空间以及具有优先级的区域回收方式保证了G1收集器在有限的时间内获取尽可能高的收集效率。 可以看下metric的图。 G1收集器的运作过程大致可划分为以下四个步骤: 初始标记(Initial Marking):仅仅只是标记一下GCRoots能直接关联到的对象并且修改TAMS指针的值让下一阶段用户线程并发运行时能正确地在可用的Region中分配新对象。这个阶段需要停顿线程但耗时很短而且是借用进行Minor GC的时候同步完成的所以G1收集器在这个阶段实际并没有额外的停顿。 并发标记(Concurrent Marking):从GCRoot开始对堆中对象进行可达性分析递归扫描整个堆里的对象图找出要回收的对象这阶段耗时较长但可与用户程序并发执行。当对象图扫描完成以后还要重新处理SATB记录下的在并发时有引用变动的对象。 最终标记(Final Marking):对用户线程做另一个短暂的暂停用于处理并发阶段结束后仍遗留下来的最后那少量的SATB记录。 筛选回收(Live Data Counting and Evacuation):负责更新Region的统计数据对各个Region的回收价值和成本进行排序根据用户所期望的停顿时间来制定回收计划可以自由选择任意多个Region构成回收集然后把决定回收的那一部分Region的存活对象复制到空的Region中再清理掉整个旧Region的全部空间。这里的操作涉及存活对象的移动是必须暂停用户线程由多条收集器线程并行完成的。 刚才讲了G1的大致垃圾回收过程大家有没有问题呢我这变先提出三个小问题 1、4个阶段停了3个阶段会不会更慢 它并非纯粹地追求低延迟官方给它设定的目标是在延迟可控的情况下获得尽可能高的吞吐量所以才能担当起“全功能收集器”的重任与期望[4]。 2、G1有了mixed gc后还会不会有full gc 会如果内存回收的速度赶不上内存分配的速度 G1收集器也要被迫冻结用户线程执行导致Full GC而产生长时间“Stop The World”。 3、G1是不是优于其他垃圾回收器 不过G1相对于CM S仍然不是占全方位、压倒性优势的从它出现几年仍不能在所有应用场景中代替CM S就可以得知这个结论。比起CM SG1的弱项也可以列举出不少如在用户程序运行过程 中G1无论是为了垃圾收集产生的内存占用(Footprint)还是程序运行时的额外执行负载 (Overload)都要比CM S要高。 就内存占用来说虽然G1和CM S都使用卡表来处理跨代指针但G1的卡表实现更为复杂而且 堆中每个Region无论扮演的是新生代还是老年代角色都必须有一份卡表这导致G1的记忆集(和 其他内存消耗)可能会占整个堆容量的20%乃至更多的内存空间;相比起来CM S的卡表就相当简单 只有唯一一份而且只需要处理老年代到新生代的引用反过来则不需要由于新生代的对象具有朝生夕灭的不稳定性引用变化频繁能省下这个区域的维护开销是很划算的[6]。 在执行负载的角度上同样由于两个收集器各自的细节实现特点导致了用户程序运行时的负载会 有不同譬如它们都使用到写屏障CM S用写后屏障来更新维护卡表;而G1除了使用写后屏障来进行 同样的(由于G1的卡表结构复杂其实是更烦琐的)卡表维护操作外为了实现原始快照搜索(SATB)算法还需要使用写前屏障来跟踪并发时的指针变化情况。相比起增量更新算法原始快照 搜索能够减少并发标记和重新标记阶段的消耗避免CM S那样在最终标记阶段停顿时间过长的缺点 但是在用户程序运行过程中确实会产生由跟踪引用变化带来的额外负担。由于G1对写屏障的复杂操作 要比CM S消耗更多的运算资源所以CM S的写屏障实现是直接的同步操作而G1就不得不将其实现 为类似于消息队列的结构把写前屏障和写后屏障中要做的事情都放到队列里然后再异步处理。 以上的优缺点对比仅仅是针对G1和CM S两款垃圾收集器单独某方面的实现细节的定性分析通常 我们说哪款收集器要更好、要好上多少往往是针对具体场景才能做的定量比较。按照笔者的实践经 验目前在小内存应用上CM S的表现大概率仍然要会优于G1而在大内存应用上G1则大多能发挥其 优势这个优劣势的Java堆容量平衡点通常在6GB至8GB之间当然以上这些也仅是经验之谈不 同应用需要量体裁衣地实际测试才能得出最合适的结论随着HotSpot的开发者对G1的不断优化也 会让对比结果继续向G1倾斜。 今天大致讲了下垃圾回收的相关知识对于一般使用面试来说够用了但是还有很多细节很复杂感兴趣的可以继续深入了解下。 是不是任何时候都可以STW?怎么STW? 衰减预测模型的大致原理是什么 当一个region的剩余空间不够创建一个对象时怎么办 ··· 等等最后我们再一起简单看下metric指标和GC日志今天的分享到此感谢大家聆听。 参考资料 深入理解Java虚拟机JVM高级特性与最佳实践第3版周志明 深入探索JVM垃圾回收ARM服务器垃圾回收的挑战和优化 彭成寒 垃圾回收机制中引用计数法是如何维护所有对象引用的 - 知乎 Chapter 2. The Structure of the Java Virtual Machine Garbage First Garbage Collector Tuning JEP 122: Remove the Permanent Generation 图片上自带的水印
http://www.w-s-a.com/news/850409/

相关文章:

  • 做网站的软件是哪个上蔡做网站
  • 前后端分离实现网站开发紧急通知网页升级
  • 河北专业网站建设公司推荐佛山小程序开发平台
  • 网站开发强制开启浏览器极速模式建设网站有什么风险
  • 360全景网站建设常州专业网站建设公司咨询
  • 重庆大渡口网站建设网站增加一体化建设功能的好处
  • 网站开发完整视频网站上传 404
  • 自适应网站做推广北京建设工程招标网
  • 外贸网站设计注意事项网上商城官网入口
  • 正规的营销型网站建设公司微官网是网站吗
  • 南京行业门户网站无锡阿里巴巴做网站
  • 河北省和城乡住房建设厅网站wamp wordpress打不开
  • 在哪个平台做网站比较好自动app优化
  • 有没有能帮人快速网站备案的机构个人学做网站
  • 凌云县 城市建设 网站西安市建网站
  • 织梦xml网站地图公众号公众平台
  • 长春省妇幼网站做四维学校网站系统破解版
  • 安阳免费搭建自己的网站个人网站做商城会怎样
  • 网站建设专家公司排行网站举报有奖平台
  • 程序员不是做网站的公司装修效果全景图
  • 桥东区住房和建设局网站怎么做网上问卷
  • 做期货要看哪些网站伪装的福祉 wordpress
  • 做网站需要多少费用网站建设需要懂什么语言
  • 网站手机端做app开发商城设计方案
  • 在建设厅网站上查询注销建造师查域名是否注册
  • 企业网站推广方案策划公司网站在国外打开很慢使用cdn好还是国外租用服务器好
  • 龙华o2o网站建设百度不收录什么网站吗
  • 模板搭建网站百度信息流推广
  • 移动端网站制作模板自己做的网站点击赚钱
  • 网站站长如何赚钱wordpress抓取别人网站