1 JDK虚拟机内存结构
JDK8虚拟机结构如图1所示。
JDK8开始,方法区被元空间替代,并且元空间为物理机内存空间,JDK8开始,堆空间抛弃永久代,当堆空间不足时会触发GC,而元空间不足时,不会触发GC。
2 JDK8堆结构
JDK8堆结构如图2所示。
JDK8中,堆分为两部分,年轻代和老年代,移除永久代。其中,年轻代分为两个区,Eden区和Survivor区,Survivor分为From Survivor区 和To Survivor区,比列为8:1:1。
2.1 MinorGC
大多数情况下,对象在新生代Eden区中分配,当Eden区没有足够空间进行内存分配时,会发起MinorGC。发生MinorGC时,将Eden区和From Survivor中仍然存活的对象拷贝到To Survivor,回收Eden和From Survivor中的对象,Eden:From Survivor:To Survivor=8:1:1,当Eden和Survivor中的存活的对象超过10%,则将一部分对象分配到老年代。
大对象直接进入老年代,大对象即需要大量连续内存空间的Java对象,长期存活的对象进入老年代。
发生MinorGC前,虚拟机必须先检查老年代最大可用的连续空间是否大于新生代所有对象总空间,
- 老年代最大连续空间>新生代所有对象空间
Minor GC是安全的 - 老年代最大连续空间>新生代所有对象空间
虚拟机先检查-XX:HandlePromotionFailture参数的设置值是否允许担保失败,如果允许,会继续检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小,如果老年代最大可用的连续空间>晋升到老年代对象的平均大小,将尝试进行一次Minor GC,如果老年代最大可用的连续空间<晋升到老年代对象的平均大小,则进行Full GC。
2.2 Major GC
清理老年代(Tenured/Old)区,用于回收老年代,出现Major GC通常至少进行一次Minor GC。
2.3 Full GC
针对整个新生代、老年代,触发条件:
- 老年代空间不足
如果创建一个大对象,Eden区域当中放不下这个大对象,会直接保存在老年代当中,如果老年代空间也不足,就会触发Full GC。为了避免这种情况,最好就是不要创建太大的对象 - YGC出现Promotion Failture
promotion failure发生在Young GC, 如果Survivor区当中存活对象的年龄达到了设定值,会就将Survivor区当中的对象拷贝到老年代,如果老年代的空间不足,就会发生promotion failure, 接下去就会发生Full GC - YGC晋升到老年代的对象平均总大小>老年代空闲空间
在发生YGC时会判断,是否安全,这里的安全指的是,当前老年代空间可以容纳YGC晋升的对象的平均大小,如果不安全,就不会执行YGC,转而执行FullGC - 显式调用System.gc
2.4 参数配置
序号 | 描述 | 参数 |
---|---|---|
1 | -XX:SurvivorRatio | 存活区 |
2 | -XX:HandlePromotionFailture | 担保 |
3 | -XX:PretenureSizeThreshold | 对象老年代分配,大于该值的对象分配到老年代 |
4 | -XX:MaxTenuringThreshold | 对象晋升到老年代年龄,当存活的对象年龄高于设定值,对象进入老年代(每回收一次,对象年龄增加1) |
5 | -XX:+PrintGCDetails | 日志收集 |
6 | -Xms | 初始堆大小,可以与Xmx相同,避免垃圾回收完之后JVM重新分配内存 |
7 | -Xmx | 最大堆大小 |
8 | -Xmn | 年轻代大小 |
9 | -Xss | 每个线程堆大小 |
【参考文献】
[1]https://blog.csdn.net/luzhensmart/article/details/103782340
[2]https://zhuanlan.zhihu.com/p/68145978
[3]https://www.cnblogs.com/paddix/p/5309550.html
[4]https://www.cnblogs.com/secbro/p/11718987.html
[5]https://www.cnblogs.com/leeego-123/p/11298267.html
[6]https://www.cnblogs.com/jpfss/p/8618297.html