zoukankan      html  css  js  c++  java
  • JVM探秘:内存分配与回收策略

    本系列笔记主要基于《深入理解Java虚拟机:JVM高级特性与最佳实践 第2版》,是这本书的读书笔记。

    内存分配一般关注的是对象在堆上分配的情况,对象主要分配在新生代的Eden区中,如果启用了本地线程分配缓冲,将按线程优先在TLAB上分配。少数情况下也会直接分配在老年代中,这取决于使用的垃圾收集器组合,以及设置的JVM参数。

    对象优先在Eden分配

    大多数情况下,对象在Eden区中分配。当Eden区没有足够空间进行分配时,虚拟机会发起一次Minor GC。

    • Minor GC:发生在新生代的垃圾收集,因为一般大多对象具有朝生夕灭的特性,所以Minor GC发生非常频繁,回收速度也比较快。
    • Major GC/Full GC:发生在老年代的垃圾收集,发生了Major GC,Major GC的回收速度比较慢。

    当Minor GC时,发现Eden中已存在的对象,也无法放入Survivor区,那么就会通过分配担保机制提前转移到老年代。

    大对象直接进入老年代

    大对象一般是指需要大量连续内存空间的Java对象,最典型的大对象就是那种很长的字符串或数组,例如new byte[10 * 1024 * 1024]是一个10M的大对象。遇到大对象对虚拟机来说是可怕的,更可怕的是遇到一群朝生夕灭的短命大对象。经常遇到大对象,会导致在内存还有不少空间时就提前触发垃圾收集,用以获取足够的连续内存空间来安置大对象。

    虚拟机提供了一个参数-XX:PretenureSizeThreshold,大于这个参数的对象会直接在老年代分配。这样能避免在Eden区和两个Survivor区之间大量的内存复制(新生代采用复制算法)。

    长期存活的对象进入老年代

    虚拟机采用了分代收集的思想来回收内存,既然这样,那内存回收时,虚拟机需要能识别哪些对象应该放到新生代,哪些对象应该放到老年代。为了做到这点,虚拟机给每个对象定义了一个对象年龄计数器。

    如果对象在Eden区出生后经过一次Minor GC仍然存活,并且能被Survivor区容纳的话,那么对象将被移动到Survivor空间,同时对象年龄设为1。对象在Survivor区中每熬过一次Minor GC,对象年龄就增加1岁。当对象年龄达到一定值后(默认15),对象就会晋升到老年代中。这个晋升到老年代的年龄阈值,可以通过参数-XX:MaxTenuringThreshold设置。

    动态对象年龄判定

    有时为了适应不同的内存情况,虚拟机并不是死板的按照-XX:MaxTenuringThreshold的值,要求对象必须达到年龄阈值才能晋升老年代。

    如果在Survivor空间中,所有相同年龄的对象的大小总和,大于Survivor空间的一半,那么年龄大于或等于该年龄的对象,都可以直接进入老年代,无须等到-XX:MaxTenuringThreshold设置的年龄阈值。

    空间分配担保

    新生代采用了复制收集算法,为了提高内存利用率,通常将新生代划分为了1个Eden区,和2个相同大小的Survivor区,它们的大小比例是8:1:1。这样其中的一个Survivor区来作为轮换备份,当出现大量对象在Minor GC后仍然存活时(极端情况例如所有对象都存活),Survivor区无法容纳,这时就需要老年代进行分配担保,将Survivor无法容纳的对象直接进入老年代。

    老年代进行这样的分配担保,前提是老年代本身还有容纳这些对象的剩余空间。而有多少对象能存活下来在内存回收之前是无法准确预知的,所以只好根据之前每次回收晋升到老年代对象容量的平均大小,作为经验值,与老年代剩余空间进行比较。如果经验值大于剩余空间,这时老年代空间不足,就会发起一次Full GC。如果经验值小于剩余空间,就会Minor GC,这时新生代内存回收实际发生后,如果晋升到老年代时老年代空间不足,就会担保失败,担保失败则会再发起Full GC。

    可见,担保失败后再Full GC,绕的圈子是最大的。按照现在分配担保的规则,只要老年代的连续空间大于新生代对象总大小,或历次晋升的平均大小,就会进行Minor GC,否则进行Full GC。

  • 相关阅读:
    vim lua对齐indent无效
    C中的私有成员
    Lua 设置table为只读属性
    c语言结构体可以直接赋值
    Lua5.3 注册表 _G _ENV
    火狐浏览器调试ajax异步页面时报错NS_ERROR_UNEXPECTER
    ajax向后台请求数据,后台接收到数据并进行了处理,但前台就是调用error方法
    maven安装之后,或者升级之后遇到的问题:could not find or load main class org.codehaus.plexus.class.....
    jenkins执行shell命令,有时会提示“Command not found”
    shell 脚本替换文件中某个字符串
  • 原文地址:https://www.cnblogs.com/cellei/p/12153679.html
Copyright © 2011-2022 走看看