java内存分配
来源:互联网 发布:百度云连接网络失败bug 编辑:程序博客网 时间:2024/05/09 15:17
java对内存的回收和收集器的主要思想我在前一篇博客进行了详细的描述,这里主要讲的是java如何实现对内存的管理的,在讲解之前我们需要做的是理解如何配置jvm参数和参数的意义,下面我也会提到一些参数的作用和使用的场合,并会达到什么效果,但前提是必须了解jvm中堆内存是如何分配的,也可以看我之前的一篇文章:
http://blog.csdn.net/maodoubi/article/details/47981693
以下的部分资料也可以看看周志明老师写的《深入理解java虚拟机》这本书,对java虚拟机进行了详细的介绍
1、对象优先在Eden区进行分配
大多数情况下,对象都是在新生代Eden区分配,当Eden区没有足够的空间进行分配时,虚拟机就会发生一次MinorGC(新生代垃圾回收),所谓MinorGC就是在给对象分配内存时,发现此时Eden区已经没有任何空间能够容纳这个对象,就会把Eden区中存活的对象放入Survivor区,但是如果Survivor区不能容纳这些存活的对象,就只能通过分配担保机制提前转移到老年代去,旧生代用于存放新生代中经过多次垃圾回收 (也即Minor GC) 仍然存活的对象,下面是一个图示可以非常完美的说明,Form Space和To Space分别就是两个Survivor区:
public class Test { //设计一个byte数组长度,大小为1024*1024,其实就是1MB private static final int _1MB=1024*1024; public static void testAllocation(){ byte[] allocation1,allocation2,allocation3,allocation4; //为四个byte数组分别分配2M,2M,2M,4M的空间 allocation1=new byte[2*_1MB]; allocation2=new byte[2*_1MB]; allocation3=new byte[2*_1MB]; /* 当为第四个数组分配内存时已经没有多余的内存可以容纳下这个数组了 * 所以就会在新生代Eden区域进行一次垃圾回收,但此时Survivor区域也不能容纳 * 就会将上面的三个数组全部放入到老年代中进行担保 * 这就是所谓的进行一次MinorGC(新生代垃圾回收) */ allocation4=new byte[4*_1MB]; } public static void main(String[] args) { Test.testAllocation(); }}
看一下日志的结果:
2、大对象直接进入老年代:
public class Test { //设计一个byte数组长度,大小为1024*1024,其实就是1MB private static final int _1MB=1024*1024; public static void testAllocation(){ byte[] allocation1; /* * 直接放入一个4M字节的数组,这个时候由于超过了-XX:PretenureSizeThreshold指定的 * 范围3M,这个参数只能使用字节的形式表示,这个时候会直接将这个4M数组放入到老年代中 */ allocation1=new byte[4*_1MB]; } public static void main(String[] args) { Test.testAllocation(); }}打印日志内容:
从上面的日志中可以看到当直接放入一个4M的数据时,由于超过了PretenureSizeThreshold参数3M的限制,而被直接放到老年代中去了,因为老年代中刚好也增加了4M的内存
3、长期存活的对象将直接被放入到老年代:
我们知道垃圾收集器回收对象是通过分代收集的思想实现的,那么内存必须知道将哪些对象应该放入老年代,哪些对象放入新生代,jvm给出了一中对象年龄的计数器,上面提到到eden区域没有足够的内存空间存放我们定义的对象时,就会进行一次MinorGC,但是如果Survivor区域可以容纳的话,就会被移动到Survivor区域,并将对象的年龄设置成1,对象没熬过一次MinorGC,年龄就会加1,当年龄超过一个阈值就会被放入老年代中,我们可以通过-XX:MaxTenuringThreshold=10来设置阈值,通过-XX:+PrintTenuringDistribution可以打印出比较清楚的日志,可以看出阈值增加时的内存变化,下面是一个实例,分别是阈值为1和阈值为10的情况:
public class Test { //设计一个byte数组长度,大小为1024*1024,其实就是1MB private static final int _1MB=1024*1024; public static void testAllocation() throws InterruptedException{ System.gc();//做一次垃圾清理,保证Survivor区域空间不会超过1/2 Thread.sleep(500);//休眠500ms,保证清理已经结束了 @SuppressWarnings("unused") byte[] allocation1,allocation2,allocation3,allocation5; allocation1=new byte[_1MB/4]; allocation2=new byte[4*_1MB]; allocation3=new byte[4*_1MB]; allocation3=null; allocation3=new byte[4*_1MB]; } public static void main(String[] args) throws InterruptedException { Test.testAllocation(); }}阈值为1时的情况,可以看到当进行第一次垃圾回收时survivor区域被放入了256K的内存,但是第二次回收时survivor空间变为了0K,并与阈值为10的情况相比,老年代还多了3%的内存,初略计算刚好是256KB左右,说明新生代survivor区域的内容被放入到了老年代中:
阈值为10时的情况,可以看到在发生两次收集时survivor区域的内存始终是256KB:
4、动态年龄对象判断:
jvm其实不是一定要求对象的年龄必须达到MaxTenuringThreshold才能晋升到老年代,如果Survivor空间中相同年龄所有对象的大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象就会直接进入到老年代,而不需要等到MaxTenuringThreshold中要求的年龄,比如上方的实例中可以看到,我们在做操作时进行了一次垃圾清理,可以尽量保证survivor中的内存被清理,否则可能会出现survivor区域的内存大于1/2而导致直接将survivor中相同年龄的对象放入老年代,也就不会出现想要的结果,例如在上方的代码allocation1后面给allocation5分配一个256kb的空间,最后得到结果如下所示:
结果显示当插入512KB的内存时,此时survivor中年龄相等的有allocation1和allocation5,并且他们之和等于survivor区域1/2的大小,所以即使没有等到MaxTenuringThreshold中要求的年龄,这两个对象仍然被放置到了老年代中进行担保,因为老年代中的内存刚好比上面一个阈值为10的实例增加了5%左右的内存,大约就是512KB左右的内存空间。
5、空间分配担保:
- C++ 内存分配 vs java内存分配
- JAVA中的内存分配
- java内存分配初探
- JAVA中的内存分配
- java内存分配
- 初探java内存分配
- java内存分配
- java内存分配研究
- java内存分配初探
- java中的内存分配
- java中内存分配
- java的内存分配
- Java 内存分配
- java内存分配图
- Java内存分配
- java中的内存分配
- java 内存分配
- Java中的内存分配
- hdu2553(回溯)
- ios textView 输入时,输入框里面的文字上下浮动Bug
- private final static ×× 和private static final xx 两者修饰变量的区别
- Advanced Machine Learning 第四节 线性回归
- 做产品收集的一些网址
- java内存分配
- boost::lexical_cast
- Android ViewDragHelper完全解析 自定义ViewGroup神器
- centos 安装mysql过程
- nyoj 628 小媛在努力【大水题】
- iPad开发,UIPopoverController的使用
- 我把我的公司投诉了,北京新锐互动
- C++基础篇--运算符重载
- 给app嵌入广告ADBannerView