摘要:主要在堆上分配内存,而堆又分为新生代和老年代两个部分,新生代又再分为区和区两部分,本文根据堆的划分,描述的内存分配策略。
java主要在堆上分配内存,而Java堆又分为新生代(YoungGen)和老年代(OldGen)两个部分,新生代又再分为Eden区和Survivor区两部分,本文根据java堆的划分,描述hotspot的内存分配策略。
GC垃圾收集分类Minor GC: 发生在新生代中的垃圾收集,采用复制算法。
Major GC/Full GC: 发生在老年代的垃圾收集动作,所采用的是标记-清除或者标记-整理算法。
Eden区和Survivor区对于采用复制算法的虚拟机,新生代通常有一个Eden区和两个Survivor区。
对象优先在Eden区分配,当Eden区没有足够的空间进行分配时,虚拟机讲发起一次Minor GC,Eden中存活的对象将被移动到第一块Survivor区S1,Eden被清空。
当Eden区再次填满,再次触发Minor GC,Eden区和S1中的存活对象被复制送入第二块Survivor区S2中,S1和Eden被清空,下一轮交换S1和S2的角色。
使用两个Survivor区能够简化复制算法的过程,并且避免复制过程中内存碎片的产生。
当对象的复制次数达到-XX:MaxTenuringThreshold设置的值(默认-XX:MaxTenuringThreshold=15)时,将被移至老年代。
实验/** * VM Options: * -Xmx20M 堆最大20M * -Xms20M 堆最小20M * -Xmn10M 新生代为10M * -XX:SurvivorRatio=8 Eden区和Survivor区的比值为8:1 * -XX:+PrintGCDetails 输出回收日志及退出时内存各区域情况 */ private static final int _1MB = 1024 * 1024; public static void testAllocation(){ byte[] allocation1,allocation2,allocation3,allocation4; allocation1 = new byte[2 * _1MB]; allocation2 = new byte[2 * _1MB]; allocation3 = new byte[2 * _1MB]; allocation4 = new byte[4 * _1MB]; }
结果:
PSYoungGen total 9216K, used 7479K [0x00000007bf600000, 0x00000007c0000000, 0x00000007c0000000) eden space 8192K, 91% used [0x00000007bf600000,0x00000007bfd4df10,0x00000007bfe00000) from space 1024K, 0% used [0x00000007bff00000,0x00000007bff00000,0x00000007c0000000) to space 1024K, 0% used [0x00000007bfe00000,0x00000007bfe00000,0x00000007bff00000) ParOldGen total 10240K, used 4096K [0x00000007bec00000, 0x00000007bf600000, 0x00000007bf600000) object space 10240K, 40% used [0x00000007bec00000,0x00000007bf000010,0x00000007bf600000) Metaspace used 2692K, capacity 4486K, committed 4864K, reserved 1056768K class space used 288K, capacity 386K, committed 512K, reserved 1048576K
在退出时各个区域结果如上, PSYoungGen的PS指的是Parallel Scavenge垃圾回收器,ParOldGen中的Par指的是Parallel Old垃圾回收器。
设置中堆为10M,新生代为10M,因此老年代为10M。设置SurvivorRatio为8:1,因此Eden区大小为8M,Survivor区大小为1M。因为Survivor区同一时刻只有一个能用于分配,因此PSYoungGen区域总可用大小为9M。
在allocation4进行分配时,新生代eden区已经用了6M,只剩2M,无法进行分配,触发MinorGC。新生代中3个2M大小的对象全部无法放入1M的Survivor区中,所以只能通过分配担保机制将两个2M的对象放入老年代中,再将allocation4的4M对象放入Eden区中。
最终Eden区分配6M,survivor区中没有对象,老年代分配4M。
大对象是指需要大量连续内存空间的Java对象。为了避免在Eden区及两个Survivor区之间发生大量的内存复制,虚拟机提供了-XX:PretenureSizeThreshold参数。大于该参数设置值的对象将直接分配在老年代。
长期存活对象直接进入老年代虚拟机给每个对象定义了一个对象年龄计数器,对象每经过一个MinorGC仍然存活,则年龄加一,当年龄增加到超过-XX:MaxTenuringThreshold设置的值(默认为16)时,将被移至老年代。
空间分配担保如文章开头的例子中,当出现大量对象在MinorGC后仍然存活的情况,Survivor区无法容纳多余的对象,此时,需要老年代进行分配担保,把Survivor无法容纳的对象直接进入老年代。JDk 6 Update 24之后,担保的规则是,只要老年代的连续空间大于新生代对象总大小或者历次晋升的平均大小就会进行MinorGC,否则将进行Full GC。
参数设置小结参数 | 描述 |
---|---|
-Xms20M | 堆最小值 |
-Xmx20M | 堆最大值 |
-Xmn10M | 新生代大小 |
-XX:SurvivorRatio=8 | Eden区比Survivor区的大小 |
-XX:+PrintGCDetails | 输出回收日志和退出时各内存区域情况 |
-XX:PretenureSizeThreshold=10M | 大于该参数设置值的对象直接分配在老年代 |
-XX:MaxTenuringThreshold=15 | 新生代对象年龄增加到超过该值时,将被移至老年代 |
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/69445.html
摘要:概要要理解的内存管理策略,首先就要熟悉的运行时数据区,如上图所示,在执行程序的时候,虚拟机会把它所管理的内存划分为多个不同的数据区,称为运行时数据区。 这是一篇有关JVM内存管理的文章。这里将会简单的分析一下Java如何使用从物理内存上申请下来的内存,以及如何来划分它们,后面还会介绍JVM的核心技术:如何分配和回收内存。 JMM ( Java Memory Model )概要 show...
摘要:目录往期博客课堂篇初识常量池简单理解字符串常量池静态常量池大整型常量池为什么要了解垃圾收集和内存分配如何判断对象已死引用计数算法可达性分析算法之后引用的扩充回收方法区垃圾收集算法分代收集理论标记清除标记复制标记整理对象分 ...
摘要:腾讯特约作者姚潮生首先以一个内存泄露实例来开始本节基础概念的内容。堆内存用于存放所有由创建的对象内容包括该对象其中的所有成员变量和数组。回到我们的问题,为什么内存会泄露堆内存中的长生命周期的对象持有短生命周期对象的强软引用,尽管 腾讯Bugly特约作者: 姚潮生 首先以一个内存泄露实例来开始本节基础概念的内容。 实例1:单例导致内存对象无法释放而泄露 showImg(http://i....
摘要:虚拟机所处的区域,则表示它是属于新生代收集器还是老年代收集器。虚拟机总共运行了分钟,其中垃圾收集花掉分钟,那么吞吐量就是。收集器线程所占用的数量为。 本文主要从GC(垃圾回收)的角度试着对jvm中的内存分配策略与相应的垃圾收集器做一个介绍。 注:还是老规矩,本着能画图就不BB原则,尽量将各知识点通过思维导图或者其他模型图的方式进行说明。文字仅记录额外的思考与心得,以及其他特殊情况 内存...
摘要:内存泄漏总结内存管理的目的就是让我们在开发中怎么有效的避免我们的应用出现内存泄漏的问题。在中,内存泄漏的范围更大一些。 Android 内存泄漏总结 内存管理的目的就是让我们在开发中怎么有效的避免我们的应用出现内存泄漏的问题。内存泄漏大家都不陌生了,简单粗俗的讲,就是该被释放的对象没有释放,一直被某个或某些实例所持有却不再被使用导致 GC 不能回收。最近自己阅读了大量相关的文档资料,打...
阅读 1019·2021-11-12 10:34
阅读 946·2021-09-30 09:56
阅读 602·2019-08-30 15:54
阅读 2550·2019-08-30 11:14
阅读 1421·2019-08-29 16:44
阅读 3175·2019-08-29 16:35
阅读 2450·2019-08-29 16:22
阅读 2404·2019-08-29 15:39