问题
新生代(Young Generation) 里为什么要分成以下内存块?
Eden + Survivor 0 + Survivor 1为什么这些内存块默认的比例是8:1:1?
以下要说的内容是从《深入理解JAVA虚拟机》书中总结出来的。
新生代内存结构演进:
1. 单块内存
如果jvm 只分配单一块内存进行存储所有新创建的对象,那么JVM可以怎么样回收内存呢? 请参考以下内存分配情况:
在回收内存时,先把可回收的对象进行标记(如图中的对象2),
然后清除这些被标记的对象。
这种过程叫【标记-清除】算法,但有两个缺点:
- 标记过程 和 清除过程的效率都不高;
- 会造成很多内存碎片,如图对象2被清除后,留下来的这一块内存就属于内存碎片,后续jvm应该是不会使用,因为如果要使用还得要计算这块内存碎片的范围,这么一来效率就更低。
2. 分成两块1:1的内存
基于第一点的情况,把内存分成两块比例一样的内存。每次回收内存时,将存活的对象直接复制到另外一块内存中,然后清除原来内存块。
这种过程叫【复制】算法,但也有缺点:
- 运行时只使用50%的内存进行分配对象,剩余50%的内存空间是被浪费的;
- 如果每次GC时只扫描第一块内存,那么第二块内存上的对象岂不是长期存活而不能被释放?所以这里缺少了一个“再次检查是否存活”的机制。
3. 分成 8:1:1 三块内存
基于第2点,实际情况下,98%的对象都是存活率为0,所以可以将运行时的内存(即第2点中的第一块内存)所占用的比例调整大一点。并且增加“再次检查对象是否存活”的机制,分配成8:1:1 三块内存,分别为:
eden , survivor A , survivor B
并将eden 和 survivor A 标记成运行时内存,将survivor B 标记成保留内存(用于复制的保留内存)。
每次GC时,都从将eden 与其中一块属于“运行时”的内存survivor A所存活对象复制到 另外一块用于复制的内存 survivorB,并标记这些对象的age 递增1
,然后将原来内存 eden 和 survivor A 内存的对象全部清除,
最后将survivor A 跟 survivor B(survivorB存在存活的对象) 角色交换。
那么下一次GC时,将扫描 eden + survivor B 内存的存活对象,而survivor A内存作为复制目标内存块。
存活对象只要age 达到某一个数值(默认jvm配置是15)则直接将该对象迁移到老年代中。这样做的目的是为了防止其中一块survivor被挤爆,增加了一个迁移到老年代的机制。
关于存活对象的age 设置:
-XX:MaxTenuringThreshold=15
补充: 在以上推算过程,是基于每次GC时存活率对象都小于survivor区也就是10%。
但实际情况有可能GC时,存活的对象大小可能比survivor 大,那么这个时候就直接复制到用于担保机制的老年代中。
以上纯属个人理解,有说不对请上砖。