Java内存分配策略
2016-06-13 19:02
344 查看
Java内存分配策略
1. 优先在Eden上分配。
Java的对象优先会在新生代的Eden上分配。我们可以看一个例子:
我设置了这些参数:-XX:+PrintGCDetails -Xms20m -Xmx20m -Xmn10m,堆内存分配20M,新生代10M,老生代10M,默认情况下Survivor区为8:1,所以Eden区域为8M
我运行这段代码:
<span style="font-family:FangSong_GB2312;font-size:18px;">public class JavaTest { static int m = 1024 * 1024; public static void main(String[] args) { //分配2兆 byte[] a1 = new byte[2 * m]; System.out.println("a1 ok"); //分配2兆 byte[] a2 = new byte[2 * m]; System.out.println("a2 ok"); } } </span>
控制台日志:
<span style="font-family:FangSong_GB2312;font-size:18px;">a1 ok a2 ok Heap def new generation total 9216K, used 4603K [0x331d0000, 0x33bd0000, 0x33bd0000) eden space 8192K, 56% used [0x331d0000, 0x3364ef50, 0x339d0000) from space 1024K, 0% used [0x339d0000, 0x339d0000, 0x33ad0000) to space 1024K, 0% used [0x33ad0000, 0x33ad0000, 0x33bd0000) tenured generation total 10240K, used 0K [0x33bd0000, 0x345d0000, 0x345d0000) the space 10240K, 0% used [0x33bd0000, 0x33bd0000, 0x33bd0200, 0x345d0000) compacting perm gen total 12288K, used 381K [0x345d0000, 0x351d0000, 0x385d0000) the space 12288K, 3% used [0x345d0000, 0x3462f4d0, 0x3462f600, 0x351d0000) ro space 10240K, 55% used [0x385d0000, 0x38b51140, 0x38b51200, 0x38fd0000) rw space 12288K, 55% used [0x38fd0000, 0x396744c8, 0x39674600, 0x39bd0000)</span>
日志中非常清晰的可以看到,我们分配了一个4M内存大小,直接是分配在了eden space里面。
2. 大对象直接进入老生代。
参数:-XX:PretenureSizeThreshold(该设置只对Serial和ParNew收集器生效) 可以设置进入老生代的大小限制,我们设置为3M,则大于3M的大对象就直接进入老生代测试代码:
<span style="font-family:FangSong_GB2312;font-size:18px;">public class JavaTest { static int m = 1024 * 1024; public static void main(String[] args) { //分配2兆 byte[] a1 = new byte[2 * m]; System.out.println("a1 ok"); byte[] a3 = new byte[4 * m]; System.out.println("a2 ok"); } } </span>
控制台日志:
<span style="font-family:FangSong_GB2312;font-size:18px;">a1 ok a2 ok Heap def new generation total 9216K, used 2555K [0x331d0000, 0x33bd0000, 0x33bd0000) eden space 8192K, 31% used [0x331d0000, 0x3344ef40, 0x339d0000) from space 1024K, 0% used [0x339d0000, 0x339d0000, 0x33ad0000) to space 1024K, 0% used [0x33ad0000, 0x33ad0000, 0x33bd0000) tenured generation total 10240K, used 4096K [0x33bd0000, 0x345d0000, 0x345d0000) the space 10240K, 40% used [0x33bd0000, 0x33fd0010, 0x33fd0200, 0x345d0000) compacting perm gen total 12288K, used 381K [0x345d0000, 0x351d0000, 0x385d0000) the space 12288K, 3% used [0x345d0000, 0x3462f4d0, 0x3462f600, 0x351d0000) ro space 10240K, 55% used [0x385d0000, 0x38b51140, 0x38b51200, 0x38fd0000) rw space 12288K, 55% used [0x38fd0000, 0x396744c8, 0x39674600, 0x39bd0000) </span>
上面的日志中,可以清洗看到第一次分配的2M留存在了eden space中,而4M超过了大对象设置的值3M,所以直接进入了老生代tenured generation
3. 长期存活的对象进入老年代
为了演示方便,我们设置-XX:MaxTenuringThreshold=1(默认15),当在新生代中年龄为1的对象进入老年代。测试代码:
<span style="font-family:FangSong_GB2312;font-size:18px;">public class JavaTest { static int m = 1024 * 1024; public static void main(String[] args) { //分配2兆 byte[] a1 = new byte[1 * m / 4]; System.out.println("a1 ok"); byte[] a2 = new byte[7 * m]; System.out.println("a2 ok"); byte[] a3 = new byte[3 * m]; //GC System.out.println("a3 ok"); } } </span>
控制台日志:
<span style="font-family:FangSong_GB2312;font-size:18px;">a1 ok a2 ok [GC [DefNew: 7767K->403K(9216K), 0.0062209 secs] 7767K->7571K(19456K), 0.0062482 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] a3 ok Heap def new generation total 9216K, used 3639K [0x331d0000, 0x33bd0000, 0x33bd0000) eden space 8192K, 39% used [0x331d0000, 0x334f9040, 0x339d0000) from space 1024K, 39% used [0x33ad0000, 0x33b34de8, 0x33bd0000) to space 1024K, 0% used [0x339d0000, 0x339d0000, 0x33ad0000) tenured generation total 10240K, used 7168K [0x33bd0000, 0x345d0000, 0x345d0000) the space 10240K, 70% used [0x33bd0000, 0x342d0010, 0x342d0200, 0x345d0000) compacting perm gen total 12288K, used 381K [0x345d0000, 0x351d0000, 0x385d0000) the space 12288K, 3% used [0x345d0000, 0x3462f548, 0x3462f600, 0x351d0000) ro space 10240K, 55% used [0x385d0000, 0x38b51140, 0x38b51200, 0x38fd0000) rw space 12288K, 55% used [0x38fd0000, 0x396744c8, 0x39674600, 0x39bd0000) </span>
4. 动态对象年龄判定
为了使内存分配更加灵活,虚拟机并不要求对象年龄达到MaxTenuringThreshold才晋升老年代如果Survivor区中相同年龄所有对象大小的总和大于Survivor区空间的一半,年龄大于或等于该年龄的对象在Minor GC时将复制至老年代
5. 空间分配担保
新生代使用复制算法,当Minor GC时如果存活对象过多,无法完全放入Survivor区,就会向老年代借用内存存放对象,以完成Minor GC。在触发Minor GC时,虚拟机会先检测之前GC时租借的老年代内存的平均大小是否大于老年代的剩余内存,如果大于,则将Minor GC变为一次Full GC,如果小于,则查看虚拟机是否允许担保失败,如果允许担保失败,则只执行一次Minor GC,否则也要将Minor GC变为一次Full GC。
说白了,新生代放不下就会借用老年代的空间来进行GC
参考资料:《深入理解JVM》
相关文章推荐
- java对世界各个时区(TimeZone)的通用转换处理方法(转载)
- java-注解annotation
- java-模拟tomcat服务器
- java-用HttpURLConnection发送Http请求.
- java-WEB中的监听器Lisener
- Android IPC进程间通讯机制
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- 介绍一款信息管理系统的开源框架---jeecg
- 聚类算法之kmeans算法java版本
- java实现 PageRank算法
- PropertyChangeListener简单理解
- c++11 + SDL2 + ffmpeg +OpenAL + java = Android播放器
- 插入排序
- 冒泡排序
- 堆排序
- 快速排序
- 二叉查找树