年轻代与老年代
- 存储在JVM中的Java对象被划分为两类
- 一类是生命周期较短的瞬时对象,这类对象的创建和消亡都非常迅速
- 一类对象的生命周期很长,在某些极端情况下还能够与JVM的生命周期保持一致
- Java堆区进一步细分的话,分为年轻代(YoungGen)和老年代(OldGen)
- 其中年轻代可以划分为Eden空间、Survior0空间和Survior1空间(或from区和to区)
- 配置新生代和老年代在堆结构的占比
- 默认-XX:NewRatio=2,表示新生代占1,老年代占2,新生代占整个堆的1/3
- 可以修改-XX:NewRatio=4,表示新生代1,老年代占4,新生代占整个堆的1/5
- 新生代当中,默认Eden和Survivor的比例为8:1:1,但使用时比例为6:1:1,运行时需要关闭自适应策略,可能不起作用(-XX:-UseAdaptiveSizePolicy)
- 显示指定-XX:SurvivorRatio=8可以将比例调整为8:1:1
- 几乎所有的Java对象是在Eden区被new出来的
- 绝大多数的Java对象的销毁都在新生代进行
- IBM公司研究表明,新生代中80%对象都是朝生夕死的
- 可以通过-Xmn设置新生代最大内存大小
- 这个参数一般使用默认即可
java
C:\Users\Administrator\IdeaProjects\jvm\target\classes\com\chapter05>jinfo -flag SurvivorRatio 8112
-XX:SurvivorRatio=8
设置-XX:NewRatio=2 -Xms512m -Xmx512m,如生命周期比较长的对象多时,建议调大老年代对象比例
java
//先通过jps查看当前进程PID,通过jinfo命令查看进程的NewRation的设置比例
C:\Users\Administrator\IdeaProjects\jvm\target\classes\com\chapter05>jinfo -flag NewRatio 10332
-XX:NewRatio=2
对象分配过程
为新对象分配内存是一件非常严谨和复杂的任务,JVM设计者不仅需要考虑内存如何分配,在哪里分配等问题,并且由于内存分配算法与内存回收算法密切相关,所以还需考虑GC执行完内存回收后是否会在内存空间产生内部碎片。
- new对象先放伊甸园区。此区有大小限制
- 当伊甸园空间满时,程序又需要创建对象,JVM的垃圾回收器将对伊甸园区进行垃圾回收(Minor GC),将伊甸园区中的不再被其他对象所引用的对象进行销毁,再加载新的对象放到伊甸园区
- 然后将伊甸园中的剩余对象移动到幸存者0区(Survivor0)
- 如果再次触发垃圾回收,此时上次幸存下来的放到幸存者0区的,如果没有回收,放到幸存者1区
- 如果再次经历垃圾回收,此时会重新放回幸存者0区,接着再去幸存者1区
- 啥时候去养老区呢,可以设置次数,默认15次
- 设置参数:-XX:MaxTenuringThreshold=进行设置
- 在养老区,相对悠闲,当养老区内存不足时,再次触发GC:Major GC,进行养老区的内存清理
- 若养老区执行了Major GC之后发现依然无法进行对象的保存,就会产生OOM异常
针对幸存者s0,s1区的总结:复制之后有交换,谁空谁是to
关于垃圾回收:频繁在新生区收集,很少在养老区收集,几乎不在永久区/元空间收集
java
public class HeapInstanceTest {
byte[] buffer = new byte[new Random().nextInt(1024*1024)];
public static void main(String[] args) {
List<HeapInstanceTest> list = new ArrayList<>();
while (true) {
list.add(new HeapInstanceTest());
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
//执行结果
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at com.chapter06.HeapInstanceTest.<init>(HeapInstanceTest.java:12)
at com.chapter06.HeapInstanceTest.main(HeapInstanceTest.java:16)