JVM总结四:内存分配策略
概要分析
java的自动内存管理包括:对象内存的回收和对象内存的分配。
在java虚拟机中的五块内存空间中.程序计数器,java虚拟机栈,本地方法栈的内存分配和回收都是具有确定性的,一般在编译时就能确定需要分配的内存大小,并且这三个空间都是线程私有的,他们在线程创建的时创建,线程销毁时而回收。在java虚拟机的方法区中:主要是用来存储类的信息、变量、静态变量,这些数据的变动较小,故在内存分配的策略中:程序计数器、java虚拟机栈、本地方法栈、方法区他们的内存分配都不需要为他们考虑其具体算法。
在堆中,是所有线程共享,所有的对象都在堆上面创建和回收。虽然每个对象的类的大小在类加载时就能确定大小,但是对象的数量只能在程序运行时才能够被确定,但对象的数量只有在程序的运行期间才能被确定,因此堆中的分配具有较大的不确定性;并且每个对象的生命周期长短不一,因此需要针对不同的生命周期的对象采用不同的内存分配策略。
内存分配策略
对象优先在Eden区中分配
目前主要的垃圾收集器的算法都是将堆分为新生代和老年代,在新生代为了防止内存碎片问题,因此垃圾收集器一般选用“复制算法”,新生代进一步被分区为:Eden+Survoir1+Survior2这三个区。
每次创建对象时,优先在Eden 中分配,若Eden+Survoir1剩余的内存太少,导致对象无法进入该区域时,就会启用“分配担保”,当当前Eden+Survior1中的所有对象都复制到老年代中,然后将新的对象存入Eden中。
大对象直接进入老年代
所谓大对象指一个占用大量连续的存储空间的对象,比如数组。对于大对象,直接存放到老年代中去,从而避免大量的复制操作,大对象一般指:通过-XX:PretrnureSizeThreshold参数设置大对象。
生命周期长的进入老年代
老年代主要用来存储生命周期较长的对象,在新生代中每个对象都有一个年龄计时器,当新生代发生一次MinorGC后,存活下来的对象年龄就+1,当年龄超过设定的值时,就将对象转移到老年代中。
相同年龄的对象内存超过Survior内存一半的对象进入老年代
如果新生代的Survior中,年龄相同的对象的内存空间总和超过Survior内存空间的一半,那么年龄相同的对象和超过该年龄的对象都被转移到老年代中去。不需要等到最大年龄的时候转移。
注意:
1. 分配担保是老年代为新生代作担保;
2. 新生代中使用“复制”算法实现垃圾回收,老年代中使用“标记-清除”或“标记-整理”算法实现垃圾回收,只有使用“复制”算法的区域才需要分配担保,因此新生代需要分配担保,而老年代不需要分配担保。