"\u003Cdiv\u003E\u003Cp class=\"ql-align-center\"\u003E\u003Cbr\u003E\u003C\u002Fp\u003E\u003Cdiv class=\"pgc-img\"\u003E\u003Cimg src=\"http:\u002F\u002Fp1.pstatp.com\u002Flarge\u002Fpgc-image\u002F62aab62566434b8cb920b570f39eea42\" img_width=\"994\" img_height=\"647\" alt=\"你是什么垃圾之垃圾回收机制\" inline=\"0\"\u003E\u003Cp class=\"pgc-img-caption\"\u003E\u003C\u002Fp\u003E\u003C\u002Fdiv\u003E\u003Cp class=\"ql-align-center\"\u003E\u003Cbr\u003E\u003C\u002Fp\u003E\u003Cp class=\"ql-align-justify\"\u003E上海的垃圾分类席卷全国,各地纷纷响应号召,对垃圾分类知识进行普及和倡导。作为中华名族伟大复兴的一员,自然积极主动参与,担负起主人翁的责任与义务。\u003C\u002Fp\u003E\u003Cp class=\"ql-align-justify\"\u003E在大学餐厅有这样一个梗,看见吃完饭就把盘子端走清理的,那是C++程序员;看见吃完直接走人的,那是Java程序员;看见吃了一点就放下走人的,那不算程序员;看见点了两人份的,前方高能,请自觉离开。\u003C\u002Fp\u003E\u003Cp class=\"ql-align-justify\"\u003E自从邂逅了GC,就一发不可收拾了,被它那强大的功能所折服,解决了内存泄漏和溢出等问题,垃圾托管后,作为程序员可以尽情的享受这干净如新的世界,专注于理想,专注于自个。\u003C\u002Fp\u003E\u003Cp class=\"ql-align-justify\"\u003E我是一直享受这GC待遇的人之一,事实上,GC这东西很麻烦,但却是必须的。它是一位少林寺的扫地僧,平日里默默清理这寺院,当然,也是不希望显眼。就是因为它,寺院才一尘不染,才有人前来上香拜佛。也就因为幕后,才使得无数英雄为之倾倒。\u003C\u002Fp\u003E\u003Cp class=\"ql-align-justify\"\u003E说到这,我想先发表一下自个的看法。GC是一种虚拟内存技术,在较小的物理内存开辟一个较大的虚拟空间,用于存储,或许成为称作“垃圾回收”更加亲切。对于C、C++程序员的垃圾回收属于显性回收,对于每一个对象都是从生命开始到终结的维护责任。Java则是把垃圾托管给虚拟机,看似一切都那么美好,一旦出现内存泄漏和溢出异常,不了解虚拟机的内存分配机制,解决异常反而是件麻烦的事。GC性能越好,该系统的处理效率越高,GC的重要性现在不言而喻了。\u003C\u002Fp\u003E\u003Cp class=\"ql-align-justify\"\u003E本文分为算法篇和实践篇,实践篇将在后续更新。《程序员十二时辰》娱乐一下。\u003C\u002Fp\u003E\u003Cdiv class=\"pgc-img\"\u003E\u003Cimg src=\"http:\u002F\u002Fp1.pstatp.com\u002Flarge\u002Fpgc-image\u002F0d19b1bee69b4876b35f9bf0fa311f0b\" img_width=\"1080\" img_height=\"798\" alt=\"你是什么垃圾之垃圾回收机制\" inline=\"0\"\u003E\u003Cp class=\"pgc-img-caption\"\u003E\u003C\u002Fp\u003E\u003C\u002Fdiv\u003E\u003Cp class=\"ql-align-center\"\u003E\u003Cbr\u003E\u003C\u002Fp\u003E\u003Cp class=\"ql-align-justify\"\u003E什么是垃圾\u003C\u002Fp\u003E\u003Cp class=\"ql-align-justify\"\u003E在堆里,确认那些对象还“存活”着,那些已经“死去”(不可能在被任何途径使用的对象)。“死去”的自然称之为垃圾。\u003C\u002Fp\u003E\u003Cdiv class=\"pgc-img\"\u003E\u003Cimg src=\"http:\u002F\u002Fp1.pstatp.com\u002Flarge\u002Fpgc-image\u002Fb328023ab6ce4ab683a2ff751b3b953c\" img_width=\"600\" img_height=\"300\" alt=\"你是什么垃圾之垃圾回收机制\" inline=\"0\"\u003E\u003Cp class=\"pgc-img-caption\"\u003E\u003C\u002Fp\u003E\u003C\u002Fdiv\u003E\u003Cp class=\"ql-align-center\"\u003E\u003Cbr\u003E\u003C\u002Fp\u003E\u003Cp class=\"ql-align-justify\"\u003E什么是垃圾回收\u003C\u002Fp\u003E\u003Cp class=\"ql-align-justify\"\u003E垃圾回收(Garbage Collection,GC),顾名思义就是释放垃圾占用的空间,防止内存泄漏。有效的使用内存空间,对内存堆中已经死亡的或者长时间没有使用的对象进行清除和回收。\u003C\u002Fp\u003E\u003Cdiv class=\"pgc-img\"\u003E\u003Cimg src=\"http:\u002F\u002Fp9.pstatp.com\u002Flarge\u002Fpgc-image\u002Fd68842fb13224613ad5280b06bda94e2\" img_width=\"499\" img_height=\"333\" alt=\"你是什么垃圾之垃圾回收机制\" inline=\"0\"\u003E\u003Cp class=\"pgc-img-caption\"\u003E\u003C\u002Fp\u003E\u003C\u002Fdiv\u003E\u003Cp class=\"ql-align-center\"\u003E\u003Cbr\u003E\u003C\u002Fp\u003E\u003Cp class=\"ql-align-center\"\u003E算法篇\u003C\u002Fp\u003E\u003Cp class=\"ql-align-justify\"\u003E既然我们要做垃圾回收,首先得找到那些内存得回收。\u003C\u002Fp\u003E\u003Cp class=\"ql-align-justify\"\u003E引用计数算法\u003C\u002Fp\u003E\u003Cdiv class=\"pgc-img\"\u003E\u003Cimg src=\"http:\u002F\u002Fp1.pstatp.com\u002Flarge\u002Fpgc-image\u002F99cadaf3726c4d5fa4ab0b283915d8b1\" img_width=\"780\" img_height=\"226\" alt=\"你是什么垃圾之垃圾回收机制\" inline=\"0\"\u003E\u003Cp class=\"pgc-img-caption\"\u003E\u003C\u002Fp\u003E\u003C\u002Fdiv\u003E\u003Cp class=\"ql-align-center\"\u003E\u003Cbr\u003E\u003C\u002Fp\u003E\u003Cp class=\"ql-align-justify\"\u003E引用计数算法(Reachability Counting)是通过在对象头中分配一个空间来保存对该对象被引用的次数。如果该对象被其他对象引用,则它的引用计数加1,如果删除对该对象的引用,那么它的引用计数就减1,当该对象的引用为0时,那么该对象就会被回收。\u003C\u002Fp\u003E\u003Cp class=\"ql-align-justify\"\u003E优点:\u003C\u002Fp\u003E\u003Cp class=\"ql-align-justify\"\u003E(1) 可即刻回收垃圾\u003C\u002Fp\u003E\u003Cp class=\"ql-align-justify\"\u003E(2) 最大暂停时间短\u003C\u002Fp\u003E\u003Cp class=\"ql-align-justify\"\u003E(3) 没有必要沿指针查找\u003C\u002Fp\u003E\u003Cp class=\"ql-align-justify\"\u003E缺点:\u003C\u002Fp\u003E\u003Cp class=\"ql-align-justify\"\u003E(1) 计数器值的增减处理繁重\u003C\u002Fp\u003E\u003Cp class=\"ql-align-justify\"\u003E(2) 计数器需要占用很多位\u003C\u002Fp\u003E\u003Cp class=\"ql-align-justify\"\u003E(3) 实现繁琐复杂\u003C\u002Fp\u003E\u003Cp class=\"ql-align-justify\"\u003E(4) 循环引用无法收回\u003C\u002Fp\u003E\u003Cp class=\"ql-align-justify\"\u003E可达性分析算法\u003C\u002Fp\u003E\u003Cdiv class=\"pgc-img\"\u003E\u003Cimg src=\"http:\u002F\u002Fp1.pstatp.com\u002Flarge\u002Fpgc-image\u002Fe0a138ba93b74b81a0903e2aa2107e18\" img_width=\"830\" img_height=\"440\" alt=\"你是什么垃圾之垃圾回收机制\" inline=\"0\"\u003E\u003Cp class=\"pgc-img-caption\"\u003E\u003C\u002Fp\u003E\u003C\u002Fdiv\u003E\u003Cp class=\"ql-align-justify\"\u003E\u003Cbr\u003E\u003C\u002Fp\u003E\u003Cp class=\"ql-align-justify\"\u003E可达性分析算法(Reachability Analysis)的基本思路是通过一些被称为引用链(GC Roots)的对象作为起点,从这些节点开始向下搜索,搜索走过的路径被称为(Reference Chain),当一个对象到GC Roots没有任何引用链相连接时(用图论的话来说,即从GC Roots节点到该节点不可达),则证明该对象是不可用的。\u003C\u002Fp\u003E\u003Cp class=\"ql-align-justify\"\u003E通过可达性算法,成功解决了引用计数所无法解决的的问题—循环依赖,只要你无法与GC Roots建立直接或间接的连接,系统就会判定你为可回收对象。\u003C\u002Fp\u003E\u003Cp class=\"ql-align-justify\"\u003E标记-清除算法\u003C\u002Fp\u003E\u003Cdiv class=\"pgc-img\"\u003E\u003Cimg src=\"http:\u002F\u002Fp1.pstatp.com\u002Flarge\u002Fpgc-image\u002F408b4a8da58348d78a6a1943df32ffeb\" img_width=\"760\" img_height=\"242\" alt=\"你是什么垃圾之垃圾回收机制\" inline=\"0\"\u003E\u003Cp class=\"pgc-img-caption\"\u003E\u003C\u002Fp\u003E\u003C\u002Fdiv\u003E\u003Cp class=\"ql-align-justify\"\u003E\u003Cbr\u003E\u003C\u002Fp\u003E\u003Cp class=\"ql-align-justify\"\u003E标记-清除算法是世界而上首个被值得纪念的算法,自问世至今有半世纪,一直被各种处理程序所用的伟大算法。算法与它的名称一样,分为两部分,一先把内存区域中的这些对象进行标记;二那些标记回收的清除掉,成为未使用的内存空间,等待再次使用。\u003C\u002Fp\u003E\u003Cp class=\"ql-align-justify\"\u003E优点:\u003C\u002Fp\u003E\u003Cp class=\"ql-align-justify\"\u003E(1) 实现简单\u003C\u002Fp\u003E\u003Cp class=\"ql-align-justify\"\u003E(2) 与保守式GC算法兼容\u003C\u002Fp\u003E\u003Cp class=\"ql-align-justify\"\u003E缺点:\u003C\u002Fp\u003E\u003Cp class=\"ql-align-justify\"\u003E(1) 碎片化\u003C\u002Fp\u003E\u003Cp class=\"ql-align-justify\"\u003E(2) 分配速度\u003C\u002Fp\u003E\u003Cp class=\"ql-align-justify\"\u003E(3) 与写时复制技术不兼容\u003C\u002Fp\u003E\u003Cp class=\"ql-align-justify\"\u003E标记-复制算法\u003C\u002Fp\u003E\u003Cdiv class=\"pgc-img\"\u003E\u003Cimg src=\"http:\u002F\u002Fp3.pstatp.com\u002Flarge\u002Fpgc-image\u002Fd0d4f051de9c45808fcaa31891b9ed5c\" img_width=\"786\" img_height=\"234\" alt=\"你是什么垃圾之垃圾回收机制\" inline=\"0\"\u003E\u003Cp class=\"pgc-img-caption\"\u003E\u003C\u002Fp\u003E\u003C\u002Fdiv\u003E\u003Cp class=\"ql-align-center\"\u003E \u003C\u002Fp\u003E\u003Cp class=\"ql-align-justify\"\u003E标记-复制算法是在标记-清除算法上演化而来,解决标记-清除算法的内存问题。它将可用内存按容量划分为大小相等的两块,每次只是用其中一块。当其中一块的内存用完了,就将还存活的的对象复制到另一块上面,然后再把已使用的内存空间一次清掉。保证了内存的连续可用,内存分配时也就不用考虑内存碎片等复杂情况,逻辑清晰,运行高效。\u003C\u002Fp\u003E\u003Cp class=\"ql-align-justify\"\u003E优点:\u003C\u002Fp\u003E\u003Cp class=\"ql-align-justify\"\u003E(1) 优秀的吞吐量\u003C\u002Fp\u003E\u003Cp class=\"ql-align-justify\"\u003E(2) 可实现高速分配\u003C\u002Fp\u003E\u003Cp class=\"ql-align-justify\"\u003E(3) 不会发生碎片化\u003C\u002Fp\u003E\u003Cp class=\"ql-align-justify\"\u003E(4) 与缓存兼容\u003C\u002Fp\u003E\u003Cp class=\"ql-align-justify\"\u003E缺点:\u003C\u002Fp\u003E\u003Cp class=\"ql-align-justify\"\u003E(1) 堆使用效率低下\u003C\u002Fp\u003E\u003Cp class=\"ql-align-justify\"\u003E(2) 不兼容保守式GC算法\u003C\u002Fp\u003E\u003Cp class=\"ql-align-justify\"\u003E(3) 递归调用函数\u003C\u002Fp\u003E\u003Cp class=\"ql-align-justify\"\u003E标记-整理算法\u003C\u002Fp\u003E\u003Cp class=\"ql-align-center\"\u003E\u003Cbr\u003E\u003C\u002Fp\u003E\u003Cdiv class=\"pgc-img\"\u003E\u003Cimg src=\"http:\u002F\u002Fp3.pstatp.com\u002Flarge\u002Fpgc-image\u002Ffcee02d540d74ffe9e9b0a2371c744a4\" img_width=\"784\" img_height=\"296\" alt=\"你是什么垃圾之垃圾回收机制\" inline=\"0\"\u003E\u003Cp class=\"pgc-img-caption\"\u003E\u003C\u002Fp\u003E\u003C\u002Fdiv\u003E\u003Cp class=\"ql-align-center\"\u003E \u003C\u002Fp\u003E\u003Cp class=\"ql-align-justify\"\u003E标记-整理算法标记过程仍然与标记-清除算法一致,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,在清理掉边界以外的内存区域。标记算法一方面在标记-清除算法上做了升级,解决了内存碎片化问题,也规避了复制算法只能利用一半存储区域的弊端。看起来很美好,但它对内存变动更频繁,需要整理所有存活对象的引用地址,在效率上比复制算法要差很多。\u003C\u002Fp\u003E\u003Cp class=\"ql-align-justify\"\u003E保守式GC算法\u003C\u002Fp\u003E\u003Cdiv class=\"pgc-img\"\u003E\u003Cimg src=\"http:\u002F\u002Fp3.pstatp.com\u002Flarge\u002Fpgc-image\u002F3594c093474c4bfb870d1fd7541ef73f\" img_width=\"690\" img_height=\"410\" alt=\"你是什么垃圾之垃圾回收机制\" inline=\"0\"\u003E\u003Cp class=\"pgc-img-caption\"\u003E\u003C\u002Fp\u003E\u003C\u002Fdiv\u003E\u003Cp class=\"ql-align-center\"\u003E \u003C\u002Fp\u003E\u003Cp class=\"ql-align-justify\"\u003E简单来说,保守式GC指的是“不能识别指针和非指针的GC”。不明确的根,寄存器、调用栈、全局变量空间等属于GC Roots。这些GC均不能识别出是指针还是非指针。\u003C\u002Fp\u003E\u003Cp class=\"ql-align-justify\"\u003E优点\u003C\u002Fp\u003E\u003Cp class=\"ql-align-justify\"\u003E(1) 容易编写语言处理程序\u003C\u002Fp\u003E\u003Cp class=\"ql-align-justify\"\u003E(2) 实现比准确式GC简单\u003C\u002Fp\u003E\u003Cp class=\"ql-align-justify\"\u003E缺点\u003C\u002Fp\u003E\u003Cp class=\"ql-align-justify\"\u003E(1) 识别指针和非指针需要付出代价\u003C\u002Fp\u003E\u003Cp class=\"ql-align-justify\"\u003E(2) 错误识别指针会压迫堆\u003C\u002Fp\u003E\u003Cp class=\"ql-align-justify\"\u003E(3) 能够使用的GC算法有限\u003C\u002Fp\u003E\u003Cp class=\"ql-align-justify\"\u003E准确式GC算法\u003C\u002Fp\u003E\u003Cp class=\"ql-align-justify\"\u003E正好与保守式GC算法相反,它是能识别指针和非指针的GC。创建正确的根需要语言处理程序的实现,创建方法居多,如打标签、不把寄存器和栈等当作根。\u003C\u002Fp\u003E\u003Cp class=\"ql-align-justify\"\u003E优点\u003C\u002Fp\u003E\u003Cp class=\"ql-align-justify\"\u003E(1) 解决保守式GC算法的“错误识别指针”问题\u003C\u002Fp\u003E\u003Cp class=\"ql-align-justify\"\u003E(2) 可以实现GC复制等移动对象的算法\u003C\u002Fp\u003E\u003Cp class=\"ql-align-justify\"\u003E缺点\u003C\u002Fp\u003E\u003Cp class=\"ql-align-justify\"\u003E(1) 创建正确的根仍需语言处理程序支援\u003C\u002Fp\u003E\u003Cp class=\"ql-align-justify\"\u003E(2) 创建正确的根是需要付出代价,打标签和取消标签在重新设置,这就得看语言处理程序的执行效率。\u003C\u002Fp\u003E\u003Cp class=\"ql-align-justify\"\u003E分代垃圾回收算法\u003C\u002Fp\u003E\u003Cdiv class=\"pgc-img\"\u003E\u003Cimg src=\"http:\u002F\u002Fp1.pstatp.com\u002Flarge\u002Fpgc-image\u002F5441e5ab4c77421b8741b1c1d3deb3cf\" img_width=\"744\" img_height=\"408\" alt=\"你是什么垃圾之垃圾回收机制\" inline=\"0\"\u003E\u003Cp class=\"pgc-img-caption\"\u003E\u003C\u002Fp\u003E\u003C\u002Fdiv\u003E\u003Cp class=\"ql-align-center\"\u003E \u003C\u002Fp\u003E\u003Cp class=\"ql-align-justify\"\u003E人们从众多程序案例中总结出了一个经验:“大部分的对象在生成后马上就变成了垃圾,很少有对象能活的很久。”分代垃圾回收利用该经验,在对象中导入了“年龄”的概念,经历了一次GC活下来的对象年龄为1岁。\u003C\u002Fp\u003E\u003Cp class=\"ql-align-justify\"\u003E分代垃圾回收算法严格来说并不是一种思想或理论,而是融合上述算法基础的算法思想,而产生针对不同情况所采用不同算法的一套豪华组合大餐。对象存活周期的不同将内存划分为几块,一般为年轻代和老年代,这样就可以根据各个年代的特点采用最适当的手收集算法。在新生代中,每次垃圾回收时都发现有大批对象死去,只有少量存活,那就选用标记-复制算法,只需要付出少量存活对象的复制成本就可以完成回收。而老年代中因为对象存活率高、没有额外空间对它进行分配担保,就必须使用标记-清除或标记-整理算法来进行回收,这也就是目前最先进的垃圾回收机制。接下来Java虚拟机来进行解说内存模型与回收策略\u003C\u002Fp\u003E\u003Cp class=\"ql-align-justify\"\u003E内存模型与回收策略\u003C\u002Fp\u003E\u003Cdiv class=\"pgc-img\"\u003E\u003Cimg src=\"http:\u002F\u002Fp1.pstatp.com\u002Flarge\u002Fdfic-imagehandler\u002F3ed366fc-fc6f-4841-97d3-2bc774ced93c\" img_width=\"626\" img_height=\"220\" alt=\"你是什么垃圾之垃圾回收机制\" inline=\"0\"\u003E\u003Cp class=\"pgc-img-caption\"\u003E\u003C\u002Fp\u003E\u003C\u002Fdiv\u003E\u003Cp class=\"ql-align-justify\"\u003E \u003C\u002Fp\u003E\u003Cp class=\"ql-align-justify\"\u003EJava 堆(Java Heap)是JVM所管理的内存中最大的一块,堆又是垃圾收集器管理的主要区域,这里我们主要分析一下 Java 堆的结构。\u003C\u002Fp\u003E\u003Cp class=\"ql-align-justify\"\u003EJava 堆主要分为2个区域-年轻代与老年代,其中年轻代又分 Eden 区和 Survivor 区,其中 Survivor 区又分 From 和 To 2个区。可能这时候大家会有疑问,为什么需要 Survivor 区,为什么Survivor 还要分2个区。不着急,我们从头到尾,看看对象到底是怎么来的,而它又是怎么没的。\u003C\u002Fp\u003E\u003Cp class=\"ql-align-justify\"\u003EEden 区\u003C\u002Fp\u003E\u003Cp class=\"ql-align-justify\"\u003EIBM 公司的专业研究表明,有将近98%的对象是朝生夕死,所以针对这一现状,大多数情况下,对象会在新生代 Eden 区中进行分配,当 Eden 区没有足够空间进行分配时,虚拟机会发起一次 Minor GC,Minor GC 相比 Major GC 更频繁,回收速度也更快。\u003C\u002Fp\u003E\u003Cp class=\"ql-align-justify\"\u003E通过Minor GC 之后,Eden 会被清空,Eden 区中绝大部分对象会被回收,而那些无需回收的存活对象,将会进到 Survivor 的 From 区(若 From 区不够,则直接进入 Old 区)。\u003C\u002Fp\u003E\u003Cp class=\"ql-align-justify\"\u003ESurvivor 区\u003C\u002Fp\u003E\u003Cp class=\"ql-align-justify\"\u003ESurvivor 区相当于是 Eden 区和 Old 区的一个缓冲,类似于我们交通灯中的黄灯。Survivor 又分为2个区,一个是 From 区,一个是 To 区。每次执行 Minor GC,会将 Eden 区和 From 存活的对象放到 Survivor 的 To 区(如果 To 区不够,则直接进入 Old 区)。\u003C\u002Fp\u003E\u003Cp class=\"ql-align-justify\"\u003E为啥需要?\u003C\u002Fp\u003E\u003Cp class=\"ql-align-justify\"\u003E不就是新生代到老年代么,直接Eden 到 Old 不好了吗,为啥要这么复杂。想想如果没有 Survivor 区,Eden 区每进行一次 Minor GC,存活的对象就会被送到老年代,老年代很快就会被填满。而有很多对象虽然一次 Minor GC 没有消灭,但其实也并不会蹦跶多久,或许第二次,第三次就需要被清除。这时候移入老年区,很明显不是一个明智的决定。\u003C\u002Fp\u003E\u003Cp class=\"ql-align-justify\"\u003E所以,Survivor 的存在意义就是减少被送到老年代的对象,进而减少 Major GC 的发生。Survivor 的预筛选保证,只有经历16次 Minor GC 还能在新生代中存活的对象,才会被送到老年代。\u003C\u002Fp\u003E\u003Cp class=\"ql-align-justify\"\u003E为啥需要俩?\u003C\u002Fp\u003E\u003Cp class=\"ql-align-justify\"\u003E设置两个Survivor 区最大的好处就是解决内存碎片化。\u003C\u002Fp\u003E\u003Cp class=\"ql-align-justify\"\u003E我们先假设一下,Survivor 如果只有一个区域会怎样。Minor GC 执行后,Eden 区被清空了,存活的对象放到了 Survivor 区,而之前 Survivor 区中的对象,可能也有一些是需要被清除的。问题来了,这时候我们怎么清除它们?在这种场景下,我们只能标记清除,而我们知道标记清除最大的问题就是内存碎片,在新生代这种经常会消亡的区域,采用标记清除必然会让内存产生严重的碎片化。因为 Survivor 有2个区域,所以每次 Minor GC,会将之前 Eden 区和 From 区中的存活对象复制到 To 区域。第二次 Minor GC 时,From 与 To 职责兑换,这时候会将 Eden 区和 To 区中的存活对象再复制到 From 区域,以此反复。\u003C\u002Fp\u003E\u003Cp class=\"ql-align-justify\"\u003E这种机制最大的好处就是,整个过程中,永远有一个 Survivor space 是空的,另一个非空的 Survivor space 是无碎片的。那么,Survivor 为什么不分更多块呢?比方说分成三个、四个、五个?显然,如果 Survivor 区再细分下去,每一块的空间就会比较小,容易导致 Survivor 区满,两块 Survivor 区可能是经过权衡之后的最佳方案。\u003C\u002Fp\u003E\u003Cp class=\"ql-align-justify\"\u003EOld 区\u003C\u002Fp\u003E\u003Cp class=\"ql-align-justify\"\u003E老年代占据着2\u002F3的堆内存空间,只有在 Major GC 的时候才会进行清理,每次 GC 都会触发“Stop-The-World”。内存越大,STW 的时间也越长,所以内存也不仅仅是越大就越好。由于复制算法在对象存活率较高的老年代会进行很多次的复制操作,效率很低,所以老年代这里采用的是标记 --- 整理算法。\u003C\u002Fp\u003E\u003Cp class=\"ql-align-justify\"\u003E除了上述所说,在内存担保机制下,无法安置的对象会直接进到老年代,以下几种情况也会进入老年代。\u003C\u002Fp\u003E\u003Cp class=\"ql-align-justify\"\u003E大对象\u003C\u002Fp\u003E\u003Cp class=\"ql-align-justify\"\u003E大对象指需要大量连续内存空间的对象,这部分对象不管是不是“朝生夕死”,都会直接进到老年代。这样做主要是为了避免在 Eden 区及2个 Survivor 区之间发生大量的内存复制。当你的系统有非常多“朝生夕死”的大对象时,得注意了。\u003C\u002Fp\u003E\u003Cp class=\"ql-align-justify\"\u003E长期存活对象\u003C\u002Fp\u003E\u003Cp class=\"ql-align-justify\"\u003E虚拟机给每个对象定义了一个对象年龄(Age)计数器。正常情况下对象会不断的在 Survivor 的 From 区与 To 区之间移动,对象在 Survivor 区中每经历一次 Minor GC,年龄就增加1岁。当年龄增加到15岁时,这时候就会被转移到老年代。当然,这里的15,JVM 也支持进行特殊设置。\u003C\u002Fp\u003E\u003Cp class=\"ql-align-justify\"\u003E动态对象年龄\u003C\u002Fp\u003E\u003Cp class=\"ql-align-justify\"\u003E虚拟机并不重视要求对象年龄必须到15岁,才会放入老年区,如果 Survivor 空间中相同年龄所有对象大小的总合大于 Survivor 空间的一半,年龄大于等于该年龄的对象就可以直接进去老年区,无需等你“成年”。\u003C\u002Fp\u003E\u003Cp class=\"ql-align-justify\"\u003E这其实有点类似于负载均衡,轮询是负载均衡的一种,保证每台机器都分得同样的请求。看似很均衡,但每台机的硬件不通,健康状况不同,我们还可以基于每台机接受的请求数,或每台机的响应时间等,来调整我们的负载均衡算法。\u003C\u002Fp\u003E\u003Cp class=\"ql-align-justify\"\u003E \u003C\u002Fp\u003E\u003C\u002Fdiv\u003E"
文章来源: https://www.toutiao.com/group/6715841139389235723/