jvm---4、内存分配及回收策略

内存分配:
对象的内存分配就是在堆上分配的(也可以经过JIT 编译后被拆散为标量型并间接地栈上分配) ,对象主要分配在Eden 区上,如果启动了本地线程分配缓冲,将按线程优先在TLAB 上分配,少数情况(如对象较大)会直接分配到老年代。具体的到看使用哪一种垃圾收集器组合和内存相关配置。


回收过程:
如果一个对象不可达,也不会立刻被回收,要先宣告一个对象死亡,至少要通过两次标记:对象进行可达性分析后,发现一个对象不可达,那么会被第一次标记并且进行一次筛选,筛选条件是此对象是否有必要执行finalize() 方法。没有覆盖finalizer() 方法或者 finalizer() 已经被调用过,虚拟机将这两种情况都视为“没有必要执行”。如果一个对象被判定有必要执行finalize() 方法,那么这个对象会被放入到 F-Queue 的队列之中,并在稍后由一个虚拟机建立的、并且低优先级的Finalizer 线程去执行对象的finalizer() 方法,但是不保证等待执行结束,因为如果一个对象finalizer()方法执行很慢(比如死循环了),很可能F-Queue队列中的其他对象永久在等待,导致整个内存回收系统崩溃。如果在finalizer() 方法中使用this 把自己赋值给某个变量或者成员变量让这个对象变成可达的,那么这个对象就会移出F-Queue 队列。
注意:一个对象的finalizer() 方法只会被执行一次,如一个对象被回收是执行了finalizer() 方法又被设置为可达的了,那么在下一次回收时就不执行finalizer() 方法了。


虚拟机栈、本地方法栈、程序计数器 随着方法结束或者线程的结束,内存就回收了。堆的回收就要看GC 组合了。


内存分配普遍规则:
1、对象在新生代Eden 中分配,当Eden 中没有足够的空间分配时,进行一次 Minor GC
2、大对象(大量连续内存空间的对象,如大数组,大的字符串)直接进入老年代。这个对象大小有个控制参数:PretenureSizeThreshold
3、长期存活的对象进入在老年代分配。JVM 给每一个对象定义了一个年龄计数器。如果对象在Eden创建并经过第一次GC后仍然存活,并且能被Survivor容纳的话,就被复制到Survivor,并且对象年龄加1。对于Surivivor 中的对象每经过一个Minor GC 还存活,年龄就加1 ,当对象年龄到一定程度就会被复制到老年代。这个年龄有控制对数 MaxTenuringThreshold,默认是15。
这个对象年龄也不是绝对有用的,因为如果在Surivivor空间中的相同年龄所有对象大小的总和大于Surivivor 空间的一半,对象的年龄大于或者等于这个年龄就可以直接进入老年代,无须等到 MaxTenuringThreshold 设置的这个值。这叫做:动态对象年龄判定。
4、分配空间的担保,  在Eden 分配内存时,如果内存不够分配了,那么需要进行一次Minor GC,Minor GC 时,可能新生代存活对象多Survivor 放不下,那么就要把一些对象放到老年代了。这就是分配担保。
过程:在发生Minor GC 之前,虚拟机会先检查老年代最大可用的连续空间是否大于新生代所有对象总空间。如果大于,那这次Minor GC 是安全的,可以进行Minor GC。如果小于,则查看 HandlePromotionFailure 的值是否允许担保失败。如果不允许担保失败,则要进行一次 Full GC ; 如果允许担保失败,那么继续查老年代最大可用连续空间是否大于历次晋升到老年代对象的平均大小,如果大于将会尝试进行一次Minor GC(这次Minor 是有风险);如果小于则进行一次Full GC。

控制对数总结:
-XX:PrintGCDetails   收集器日志参数,在JVM 发生垃圾收集时打印内存回收日志,并有进程退出时输出当前内存各区域分配情况
-XX:PretenureSizeThreshold=字节   大于这个值的对象直接在老年代分配(可以避免大对象在新生代的Eden区和Survivor区之间复制)
-XX:MaxTenuringThreshold=15    对象年龄达到这个值时,被复制到老年代中
-XX:+HandlePromotionFailure    允许担保失败  -号就是不允许。 JDK6 Update 24 之后这个参数就没有用了,规则变为:只要老年代的连续空间大于新生代对象总大小或者大于历次晋升的平均大小就会进行Minor GC,否则进行Full GC


注:
Minor GC  新生代GC, 发生在新生代的垃圾收集动作,java 对象大多数都是在新生代并且存活时间短,所以Minor GC 非常频繁并且速度比较快

Major GC /Full GC  老年代GC ,指发生在老年代的GC,出现了Major GC 经常会伴随一次Minor GC (并非绝对),Major GC 比较 Minor GC 慢10倍以上


参考《深入理解JVM》

猜你喜欢

转载自blog.csdn.net/convict_eva/article/details/80592710