内存分配
内存区域划分
- 年轻代young区
又分为新生代eden和两块survivor区。 - 老年代old/tenured区
- 永久区permanent区
内存分配
- 优先进去eden区域,当eden区域内存达到一定水位,则触发一次minor GC,将eden区域中活跃对象复制到第一块survivor from区域,并清除eden区域中的所有对象,eden区继续接受内存申请,若eden区域再次达到一定水位,则将eden区域中活跃对象复制到第二块survivor to区域,并清除eden区域中所有对象,同时将第一块survivor from区域中活跃对象也复制到第二块survivor to区域,并清除第一块survivor from区域中所有对象,此时所有存活对象都存在第二块survivor to区域。eden区域继续接受内存空间申请。
- 对象第一次从eden区域复制到suvivor区域分代年龄记为1,此后每次在两块survivor区域之间发生复制就+1,如果分代年龄达到15,则转移到老年代。
- 在某个对象从young向老年代转移时,如果此时老年代区域内存到达一定水位,就会触发full GC,同时也伴随着一次minor GC,也就是说所有内存区域同时全部发生GC,产生长时间的stop the world,所以这种情况要尽可能避免。
- 如果fullGC产生了足够的内存,那就完成这个对象的转移,但如果依然无法产生足够的内存,那就会发生OOM。
- 大对象在第一次申请内存时就会直接进入老生代,大对象指需要大量连续内存空间的Java对象,最典型的大对象就是那种很长的字符串以及数组。
虚拟机提供了一个-XX:PretenureSizeThreshold参数,令大于这个设置值的对象直接在老年代分配。目的就是避免在Eden区及两个Survivor区之间发生大量的内存复制。
垃圾回收算法
复制
需要两块大小一样的内存区域A和B,优先在A区域分配内存,此时B区域为空,当A区域内存达到一定水位时,将A区域内存中活动对象复制到B区域,然后A区域清空。
特点
- 需要两块大小一样的内存区域,因而空间使用率为50%,如果存活率高的话,大量的复制操作效率也存在问题。
标记清除
第一步遍历标记内存区域中可回收的对象,第二步遍历清除内存中标记为可回收对象。
特点*
- 内存空间碎片化的问题,标记、清除后会产生大量的不连续内存碎片,空间碎片太可能会导致当以后需要分配大对象时无法找到足够的连续内存二不得不提前触发另一次垃圾收集动作。
- 执行效率不稳定,如果Java堆中包含大量对象,而且大部分是需要被回收的,这时必须记性大量标记及清除动作,导致标记和清除两个过程执行效率都随对象数量增长而降低。
标记整理
标记过程仍然与“标记-清除”算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。
特点
- 避免内存碎片,但整理操作的效率同样是个问题。
分代回收
- 对于年轻代 Young("eden" & "survivor from" & "survivor to"),由于对象存活率较低,采用复制算法,不会因大量复制操作导致效率问题,同时合理调节新生代eden和存活区survivor的空间比例(例如8:1:1),提高空间利用率。
- 对于老生代 Old/Tenured,由于对象存活率较高,且本身占用空间大,不存在额外的担保,只能使用标记清除或者标记整理算法。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。