分配内存策略和回收策略
时间:2018-10-21
- 1. Minor GC 与 Full GC 之间的区别?
- 2. 新创建的对象优先分配在 Eden 区域
- 3. 大对象直接分配到老年代
- 4. 长期存活的对象将进入老年代
- 5. 动态对象年龄判定
- 6. 空间分配担保
Java 技术体系中所提倡的自动内存管理最终可以归结为自动化地解决了两个问题:
- 自动分配对象内存
- 自动回收内存中的对象
这 2 点正是本节我们要讲的内容。
1. Minor GC 与 Full GC 之间的区别?
- (新生代GC)Minor GC:指发生在新生代的垃圾收集动作。
- (老年代GC)Full GC/Major GC:指发生在老年代的GC,出现了 Major GC,经常会伴随至少一次的 Minor GC。
2. 新创建的对象优先分配在 Eden 区域
当 Eden 区没有足够的空间进行分配对象时,JVM 会发起一次 Minor GC。
新生代将内存分为一块较大的 Eden 空间和两块较小的 Survivor 空间。
具体对新生代的分配和回收的步骤如下:
-
每次使用 Eden 和其中一块 Survivor;
-
当 JVM 启动 Minor GC 时,将 Eden 和使用中的 Survivor 这两块内存区域中还存活的对象一次性复制到另一块 Survivor 中;
-
接着再去清理 Eden 和使用过的 Survivor;
-
后面就使用 Eden 和父之过去的那块 Survivor 空间,重复步骤 2。
3. 大对象直接分配到老年代
-
怎样的对象才算是大对象?
这是一个相对的值,可以通过
-XX:PretenureSizeThreshold
参数来自定义大对象,单位是 bit。比如,设定新对象大小超过 3M 的就是为大对象,那么,-XX:PretenureSizeThreshold = 3145728
。 -
大对象对 JVM 的影响
-
大对象很容易导致内存还有不少空间就提前触发 GC,以获取足够的连续空间来储存它们,所以应该尽量避免使用创建大对象。
-
长期存活的对象将进入老年代
-
4. 长期存活的对象将进入老年代
上面一小节提到了大对象将直接被分配到老年代,那么,那些长期存活在新生代的对象是如何进入到老年代的呢?
Java 虚拟机通过给每个对象定义一个 对象年龄计数器,再通过设定 -XX:PretenureSizeThreshold
参数来判定对象进入老年代的时机,默认值为 15。
对象年龄大于 -XX:PretenureSizeThreshold
设定值的对象进入老年代,否则依然存活于新生代。
对象年龄计算流程:
-
在 Eden 中分配的对象,经过 Minor GC 后还存活,就复制移动到另一个 Survivor 区,其年龄为 1;
-
而后,每经历一次 Minor GC 后对象还存活,在 Survivor 区复制移动一次,其年龄就增加一岁;
-
如果对象年龄大于
-XX:PretenureSizeThreshold
设定的值时,就将这个对象移动到老年代中。
5. 动态对象年龄判定
为了更好的利用新生代空间,Java 虚拟机并不是永远都要等到 -XX:PretenureSizeThreshold
设定的年龄值,才将对象从新生代晋升到老年代。
只要满足:
如果在 Survivor 空间中相同年龄的所有对象大小总和大于 Survivor 空间的一半,那些年龄大于或等于这个年龄的对象就可以直接进入老年代。
6. 空间分配担保
空间分配担保,其实就是将新生代的对象晋升到老年代。前面也多次提及对象晋升到老年代,但是都是从对象本身或者是新生代的容量的角度来看,
下面从老年代的角度来看看: