本篇文章为你整理了4 内存分配和回收策略(内存的主要分配方式有哪些,回收时可能出现什么情况)的详细内容,包含有内存分配与回收流程图 内存的主要分配方式有哪些,回收时可能出现什么情况 内存分配与回收实验报告 内存分配与回收实验心得 4 内存分配和回收策略,希望能帮助你了解 4 内存分配和回收策略。
目录1 自动内存管理2 对象优先在Eden分配2.1 总结2.2 验证过程3 大对象直接进入老年代3.1 总结3.2 验证过程4 长期存活的对象将进入老年代4.1 总结4.2 验证过程5 动态对象年龄判定5.1 总结5.2 验证过程6 空间分配担保6.1 总结6.2 验证过程
扩展:Gc日志分析工具
GC日志分析工具-GCEasy
GC日志分析神器-GCEasy详解
1 自动内存管理
Java技术体系的自动内存管理,最根本的目标是自动化地解决两个问题:分配与回收
自动给对象分配内存
自动回收分配给对象的内存
Java的对象内存分配的一般规则:
一般情况下,给对象分配堆内存;即时编译下,也会间接地分配栈内存
新生对象通常会分配在新生代中,少数情况下(例如对象大小超过一定阈值)也可能会直接分配在老年代。
《Java虚拟机规范》并未规定新对象的创建和存储细节,这取决于虚拟机当前使用的是哪一种垃圾收集器,以及虚拟机中与内存相关的参数的设定。
本章节所以用的JVM环境为:
JDK8默认:
垃圾收集器:
新生代使用 Parallel Scavenge:标记-复制算法
老年代配合使用的是 Serial Old:标记-整理算法
2 对象优先在Eden分配
2.1 总结
新生代分为一个Eden区和两个survivor区,默认比例为是 8:1:1。(设置参数-XX:-UseAdaptiveSizePolicy可固定该比例)
对象优先被分配在eden区,eden区满了后会触发minor gc,把剩余存活的对象挪到为空的那块survivor区,下一次eden区满了后又会触发minor gc,把eden区和survivor区垃圾对象回收,把剩余存活的对象一次性挪动到另外一块为空的survivor区。
如果survivor区空间不足,通过担保机制挪动到老年代。
jvm参数:
-Xms30M -Xmx30M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:-UseAdaptiveSizePolicy -XX:+PrintCommandLineFlags -XX:+PrintGCDateStamps
-Xms30M #固定堆内存大小为30M
-Xmx30M
-Xmn10M #新生代为分配10M,实际可用为9M
-XX:+PrintGCDetails
-XX:SurvivorRatio=8
-XX:-UseAdaptiveSizePolicy #固定8:1:1
-XX:+PrintCommandLineFlags #打印运行时jvm参数
-XX:+PrintGCDateStamps
特别注意:
未手动分配空间前,运行main方法占用:1476KB,即1.44MB
测试代码:
public class GcDemo {
public static void main(String[] args) {
byte[] a1 = new byte[5 * 1024 * 1024];
byte[] a2 = new byte[2 * 1024 * 1024];
GC日志及分析:
[GC (Allocation Failure) :收集类型是:Minor GC (GC代表MinorGC,System.gc()代表Full GC)
[PSYoungGen: 6432K- 640K(9216K)] 翻译为 [新生代:新生代收集前内存使用量- 新生代垃圾收集后内存使用量(新生代代可用内存总大小)],垃圾收集前新生代已使用 6432K(6.28M),垃圾收集后已使用640K,总可用大小为 9216K(9M) 。
6.28M:初始占用的1.44m+a1先被分配在eden区
a2分配时,eden剩余空间不足,触发MinorGC,此时有1476K-640K被回收,a1仍被使用,不被回收。
9216K,MinorGC后,总共可用内存为eden+1个survivor区
PSYoungGen total 9216K, used 2770K #新生代已使用2770K包含:对象a2+剩余未回收的640K
eden space 8192K, 26% used # eden区总大小8192K,已使用26%,包含对象a2
from space 1024K, 62% used #S0总大小1024K,已使用62%,包含 剩余未回收的640K
to space 1024K, 0% used #S1总大小1024K,已使用0%
ParOldGen total 20480K, used 5128K #对象a1被搬移到老年代
object space 20480K, 25%
综上:
创建a1时,a1被分配在eden区
创建a2时,eden区剩余空间不足,触发MinorGC,同时survivor空间不足以存放a1,所以通过分配担保机制将a1提前转移到老年代去。同时将回收了一部分默认占用的1476K,未被回收的640K存放在其中一个survivor
用图例所示:
3 大对象直接进入老年代
3.1 总结
在Java虚拟机中要避免大对象的原因是,在分配空间时,它容易导致内存明明还有不少空间时就提前触发垃圾收集,以获取足够的连续空间才能安置好它们,而当复 制对象时,大对象就意味着高额的内存复制开销。
HotSpot虚拟机提供了-XX:PretenureSizeThreshold 参数,指定大于该设置值的对象直接在老年代分配,这样做的目的就是避免在Eden区及两个Survivor区 之间来回复制,产生大量的内存复制操作。
3.2 验证过程
如上:并未触发gc,而是直接分配在老年代
4 长期存活的对象将进入老年代
4.1 总结
虚拟机给每个对象定义了一个对 象年龄(Age)计数器,存储在对象头中
对象通常在Eden区里诞生,如果经过第一次 Minor GC后仍然存活,并且能被Survivor容纳的话,该对象会被移动到Survivor空间中,并且将其对象 年龄设为1岁
对象在Survivor区中每熬过一次Minor GC,年龄就增加1岁,当它的年龄增加到一定程 度(默认为15),就会被晋升到老年代中。
可以通过参数-XX: MaxTenuringThreshold 设置“年龄”最大值
4.2 验证过程
todo
5 动态对象年龄判定
5.1 总结
HotSpot虚拟机并不是永远要求对象的年龄必须达到- XX:MaxTenuringThreshold才能晋升老年代
如果在Survivor空间中相同年龄所有对象大小的总和大于 Survivor空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代,无须等到-XX: MaxTenuringThreshold中要求的年龄。
5.2 验证过程
todo
6 空间分配担保
6.1 总结
在发生Minor GC之前,虚拟机必须先检查老年代最大可用的连续空间是否大于新生代所有对象总空间,如果这个条件成立,那这一次Minor GC可以确保是安全的
如果不成立,则虚拟机会先查看- XX:HandlePromotionFailure参数的设置值是否允许担保失败;如果允许,那会继续检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小,如果大于,将尝试进行一次Minor GC;尽管这次Minor GC是有风险的;如果小于,或者-XX: HandlePromotionFailure设置不允许冒险,那这时就要改为进行一次Full GC。
6.2 验证过程
todo
以上就是4 内存分配和回收策略(内存的主要分配方式有哪些,回收时可能出现什么情况)的详细内容,想要了解更多 4 内存分配和回收策略的内容,请持续关注盛行IT软件开发工作室。
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。