JVM会动态地调整实际的年龄阈值,不过通过指定-XX:+MaxTenuringThreshold参数可以给该值设置一个上限。将-XX:+MaxTenuringThreshold设置为0则立即触发对象提升,而不会复制到存活区中。在现代的JVM中,这个值默认会被设置为15个GC周期。在HotSpot虚拟机中这也是该值的上限。
如果存活区的大小不足以存放所有的新生代存活对象,则会出现过早提升。
老生代
老生代的内存空间的实现则更为复杂。老生代的空间通常都会非常大,里面存放的对象都是不太可能会被回收的。
老生代的GC比新生代的GC发生的频率要少得多。由于老生代中的多数对象都被认为是存活的,也就不会存在标记-复制操作了。在GC中,这些对象会被挪到一起以减少碎片。老生代的回收算法通常都是根据不同的理论来构建的。不过大体上都会分成如下几步:
- 标记可达对象,设置GC根对象可达的所有对象后的标记位
- 删除不可达对象
- 整理老生代空间的对象,将存活对象复制到老生代开始的连续空间内。
从以上描述中可知,为了避免过度碎片化,老生代的GC是明确需要进行整理操作的。
持久代
在Java 8以前还有一个特殊的空间叫做持久代(Permanent Generation)。这是元数据比如类相关数据存放的地方。除此之外,像驻留的字符串(internalized string)也会被存放在持久代中。这的确给Java开发人员带来了不少麻烦事,因为很难评估这究竟会使用到多少空间。评估不到位偏会抛出 java.lang.OutOfMemoryError: Permgen space 的异常。只要不是真的因为内存泄漏而引起的OutOfMemoryError异常,可以通过增加持久代空间的大小来解决这一问题,比如下例中的把持久代最大空间设置为256MB:
java -XX:MaxPermSize=256m com.mycompany.MyApplication