文章收录在网站:http://hardyfish.top/
文章收录在网站:http://hardyfish.top/
文章收录在网站:http://hardyfish.top/
文章收录在网站:http://hardyfish.top/
JVM的堆内存如何分区?
从垃圾收集(
Garbage Collection,GC
)的角度看Java堆(Heap)主要被划分为以下几个区域:
新生代(Young Generation):
- 新生代是存放新创建的对象的地方。
- 新生代又被分为三个部分:
- 一个Eden区和两个Survivor区(
Survivor 0
和Survivor 1
)。- 大部分情况下,新创建的对象首先被分配到Eden区。
老年代(Old Generation):
- 当对象在新生代中存活时间较长,或者Survivor区无法容纳的时候,就会被移动到老年代。
- 老年代的空间一般比新生代大,用于存放生命周期较长的对象。
持久代(Permanent Generation)或元空间(Metaspace):
- 这部分内存主要用于存放JVM加载的类信息、常量、静态变量等数据。
- 在Java 8中,持久代被废弃,改为使用元空间,元空间使用的是本地内存。
JVM的垃圾收集器主要根据对象所在的区域进行垃圾回收。
新生代中的垃圾收集称为
Minor GC
,这种垃圾收集的频率较高,但每次收集的时间较短。老年代中的垃圾收集称为
Major GC
或Full GC
,这种垃圾收集的频率较低
- 但每次收集的时间较长,可能会导致应用的暂停。
总的来说,从GC的角度看,Java堆主要被划分为新生代、老年代和持久代(或元空间)
- 不同的区域对应不同的垃圾收集策略。
新生代为什么要进一步分为Eden和Survivor区?
新生代将内存分为一个Eden区和两个Survivor区(S0和S1,也称为From和To区)的目的:
- 是为了实现一种称为分代复制算法(
Generational Copying Algorithm
)的垃圾收集策略
- 从而提高垃圾回收的效率。
分代复制算法的基本思想是将新创建的对象分配到Eden区,当Eden区满时,触发一次Minor GC。
- 在这次垃圾回收过程中,JVM会检查Eden区的对象
- 将仍然存活的对象复制到一个Survivor区(例如:S0区),同时清空Eden区。
- 之后,新创建的对象仍然分配到Eden区。
当下一次Minor GC发生时,JVM会再次检查Eden区和已经存有对象的Survivor区(例如:S0区)
- 将仍然存活的对象复制到另一个Survivor区(例如:S1区)
- 同时清空Eden区和之前的Survivor区(例如:S0区)。
- 这个过程会反复进行,直到某个对象在Survivor区中经历了一定次数的复制
- 由JVM参数
-XX:MaxTenuringThreshold
设置- 这个对象就会被认为是长寿对象,会被移动到老年代。
采用这种分代复制算法的好处在于:
减少内存碎片:
- 每次GC时,存活对象都被复制到另一个Survivor区,保持内存的连续性,减少内存碎片。
提高GC效率:
- 由于大部分新创建的对象都会很快变得不可达
- 所以很少有对象需要从Eden区复制到Survivor区,这使得Minor GC的效率很高。
延长老年代GC间隔:
- 分代复制算法可以有效地过滤掉生命周期短的对象
- 只有经过多次复制仍然存活的对象才会被移动到老年代,这有助于减少老年代的垃圾回收频率。
总之,将新生代分为Eden区和两个Survivor区是为了实现分代复制算法
- 从而提高垃圾回收的效率,减少内存碎片,以及延长老年代的垃圾回收间隔。
新生代各个分区的默认空间比例是怎样的?
在HotSpot虚拟机中,新生代(
Young Generation
)的默认内存划分比例是:
- Eden区:
- 占新生代总空间的8/10,也就是80%。
- Survivor区:
- 两个Survivor区(
Survivor 0
和Survivor 1
)各占新生代总空间的1/10,也就是10%。也就是说,Eden区和两个Survivor区的默认比例大约是
8:1:1
。这个比例可以通过JVM的参数
-XX:SurvivorRatio
来调整。
- 例如,如果你希望Eden区和Survivor区的比例是
6:1:1
,可以设置-XX:SurvivorRatio=6
。这个默认比例是基于经验得出的,大多数情况下,新创建的对象会很快变得不可达并被回收
所以Eden区被分配了更多的空间。
而Survivor区的空间较小,主要用于存放从Eden区复制过来仍然存活的对象。
需要注意的是,虽然两个Survivor区的总空间占新生代的2/10
- 但在任何时候,两个Survivor区只有一个被使用,另一个是空闲的。
这是因为在进行Minor GC时,存活的对象会在两个Survivor区之间来回复制。
例如,一次GC后,存活对象被复制到Survivor 0,下一次GC时
- 存活对象会被复制到Survivor 1,Survivor 0则被清空。
描述对象何时会从新生代晋升到老年代
存活对象会在以下情况下进入老年代:
年龄达到阈值:
- 在新生代中,每个对象都有一个年龄计数器。
- 当对象在Survivor区中经历一次Minor GC后,其年龄就会增加1。
- 当对象的年龄达到一定的阈值:
- 默认值是15,可通过
-XX:MaxTenuringThreshold
参数设置,就会被晋升到老年代。- 这个阈值可以通过虚拟机参数
-XX:MaxTenuringThreshold
来设定。Survivor空间不足:
- 在进行Minor GC时,如果Survivor空间不足以容纳Eden区和Survivor区中所有存活的对象
- 那么大于等于某个年龄的对象会直接被移动到老年代,这个年龄阈值会动态调整
- 以使得Survivor区能够容纳下其他存活对象。
动态对象年龄判定:
- 如果Survivor区中相同年龄所有对象大小的总和大于Survivor空间的一半,
- 年龄大于等于该年龄的对象就可以直接进入老年代,无须等到
-XX:MaxTenuringThreshold
设定的年龄。大对象直接进入老年代:
- 大对象是指需要大量连续内存空间的Java对象,如很长的字符串或者数组。
- 大对象会直接被分配到老年代,这是因为对大对象进行复制回收,存活率高的情况下
- 会产生大量的内存复制操作,效率相对较低。
这些策略的目的是尽可能将生命周期长的对象提前移入老年代,减少新生代的GC次数,提高系统的运行效率。