JVM学习笔记---JVM运行时内存之堆空间

JVM学习笔记—JVM运行时内存之堆空间

1. Java 堆简介

  1. 对于Java应用程序来说, Java堆(Java Heap) 是虚拟机所管理的内存中最大的一块。
  2. Java堆是被所有线程共享的一块内存区域, 在虚拟机启动时创建。
  3. 此内存区域的唯一目的就是存放对象实例, Java 世界里“几乎”所有的对象实例都在这里分配内存。
  4. “几乎”是指从实现角度来看, 随着Java语 言的发展, 现在已经能看到些许迹象表明日后可能出现值类型的支持, 即使只考虑现在, 由于即时编译技术的进步, 尤其是逃逸分析技术的日渐强大, 栈上分配、 标量替换优化手段已经导致一些微妙的变化悄然发生, 所以说Java对象实例都分配在堆上也渐渐变得不是那么绝对了。

2. Java 堆的特点

  1. 是Java虚拟机所管理的内存中最大的一块。
  2. 堆是jvm所有线程共享的。堆中也包含私有的线程缓冲区 Thread Local Allocation Buffer (TLAB),这部分是私有
  3. 在虚拟机启动的时候创建。
  4. 唯一目的就是存放对象实例,几乎所有的对象实例以及数组都要在这里分配内存。
  5. Java堆是垃圾收集器管理的主要区域。
  6. 因此很多时候java堆也被称为“GC堆”(Garbage Collected Heap)。从内存回收的角度来看,由于现在收集器基本都采用分代收集算法,所以Java堆还可以细分为:新生代和老年代;新生代又可以分为:Eden 空间、From Survivor空间、To Survivor间。
  7. java堆是计算机物理存储上不连续的、逻辑上是连续的,也是大小可调节的(通过-Xms和-Xmx控制)。
  8. 方法结束后,堆中对象不会马上移出仅仅在垃圾回收的时候时候才移除。
  9. 如果在堆中没有内存完成实例的分配,并且堆也无法再扩展时,将会抛出OutOfMemoryError异常

3. 设置堆空间的大小

  1. 内存大小设置:-Xmx(最大)/-Xms(最小)

4. 堆的分类

  1. 现在垃圾回收器都使用分代理论,堆空间也分类如下:
  2. JDK7中将java的堆空间分为以下几个结构:
    1. 青年代:Young Generation,Eden区,S0区,S1区
    2. 老年代:Old Generation
    3. 永久代:Permanent Generation
      在这里插入图片描述
  3. 在Java8以后,由于方法区的内存不在分配在Java堆上,而是存储于本地内存元空间Metaspace中,所以永久代就不
    存在了,在几天前(2018年9约25日)Java11正式发布以后,关于Java11中垃圾收集器的官方文档,文档中没有提到“永久代”,而只有青年代和老年代。
  4. 原来对应的永久代被换到了本地内存中的元空间
    在这里插入图片描述

5. 堆的年轻代和老年代

  1. JVM中存储java对象可以被分为两类:
  2. 年轻代(Young Gen):年轻代主要存放新创建的对象(在Eden区创建对象),内存大小相对会比较小,垃圾回收会比较频繁。年轻代分成1个Eden Space和2个Suvivor Space(from 和to)。
  3. 年老代(Tenured Gen):年老代主要存放JVM认为生命周期比较长的对象(经过几次的Young Gen的垃圾回收后仍然存在),内存大小相对会比较大,垃圾回收也相对没有那么频繁。
  4. 年轻代和老年代大小的比例是1:2
  5. 年轻代中Eden,S0,S1的比例是8:1:1
    在这里插入图片描述

6. 堆中年轻代和老年代结构占比

  1. 默认 -XX:NewRatio=2 , 标识新生代占1 , 老年代占2 ,新生代占整个堆的1/3
  2. 修改占比 -XX:NewPatio=4 , 标识新生代占1 , 老年代占4 , 新生代占整个堆的1/5
  3. Eden空间和另外两个Survivor空间占比分别为8:1:1
  4. 可以通过操作选项 -XX:SurvivorRatio 调整这个空间比例。 比如 -XX:SurvivorRatio=8
  5. 几乎所有的java对象都在Eden区创建, 但80%的对象生命周期都很短,创建出来就会被销毁.
  6. 由下图可知:
    1. 堆大小 = 新生代 + 老年代。其中,堆的大小可以通过参数 –Xms、-Xmx 来指定。
    2. 默认来说,新生代 ( Young ) 与老年代 ( Old ) 的比例的值为 1:2 ( 该值可以通过参数 –XX:NewRatio 来指定 ),即:新生代 ( Young ) = 1/3 的堆空间大小。老年代 ( Old ) = 2/3 的堆空间大小。
    3. 新生代 ( Young ) 被细分为 Eden 和 两个Survivor 区域,这两个 Survivor 区域分别被命名为 from 和 to,以示区分。
    4. 默认的,Edem : from : to = 8 : 1 : 1 ( 可以通过参数 –XX:SurvivorRatio 来设定 ),即: Eden = 8/10 的新生代空间大小,from = to = 1/10 的新生代空间大小。
    5. JVM 每次只会使用 Eden 和其中的一块 Survivor 区域来为对象服务,所以无论什么时候,总是有一块 Survivor 区域是空闲着的。因此,新生代实际可用的内存空间为 9/10 ( 即90% )的新生代空间。
      在这里插入图片描述

7. 堆中对象位置分配过程

  1. JVM设计者不仅需要考虑到内存如何分配,在哪里分配等问题,并且由于内存分配算法与内存回收算法密切相关,因此还需要考虑GC执行完内存回收后是否存在空间中间产生内存碎片。
  2. 分配过程
    1. new的对象先放在伊甸园区。该区域有大小限制
    2. 当伊甸园区域填满时,程序又需要创建对象,JVM的垃圾回收器将对伊甸园预期进行垃圾回收(Minor GC),将伊甸园区域中不再被其他对象引用的额对象进行销毁,再加载新的对象放到伊甸园区
    3. 然后将伊甸园区中的剩余对象移动到幸存者0区
    4. 如果再次触发垃圾回收,此时上次幸存下来的放在幸存者0区的,如果没有回收,就会放到幸存者1区
    5. 如果再次经历垃圾回收,此时会重新返回幸存者0区,接着再去幸存者1区,这样新生代每次垃圾的年龄都会+1,而且要保证每次S0或者S1区中都有一个是空的。
    6. 如果累计次数到达默认的15次,这会进入养老区。可以通过设置参数,调整阈值 -XX:MaxTenuringThreshold=N
    7. 养老区内存不足是,会再次出发GC:Major GC 进行养老区的内存清理
    8. 如果养老区执行了Major GC后仍然没有办法进行对象的保存,就会报OOM异常.
      在这里插入图片描述
      在这里插入图片描述
      在这里插入图片描述

8. 分配对象的流程

在这里插入图片描述

  1. 给创建的新对象分配区域的过程:
    1. 创建一个新的对象,需要申请对应的内存,新创建的对象我们要放在Eden区
    2. 判断Eden区有没有对应的空间?有空间的话我们正常存,如果Eden区空间不足就要清理一下Eden区,进行垃圾回收,执行MinorGC也叫YGC
    3. 清理完之后查看Eden区是否放得下?如果能就放入Eden,如果放不下只能把这个对象放到老年区了
    4. 如果老年区放得下我们依旧会分配内存,如果老年区还放不下说明这个对象特别大,这时候要清理一下老年区,执行一个Full GC(FGC)的操作,FGC会将年轻代和老年代都清理
    5. FGC清理完之后看还能不能放得下?如果能就分配内存放到老年区了,如果放不下就报错OOM
  2. 现在看一下YGC的过程(给垃圾分配区域的过程):
    1. 在Eden区放不下,要进行YGC,先查看Servivor区是否有足够的内存,如果有就放到S0/S1区,否则放到老年代
    2. 判断幸存者区中的独享是否超过阈值,没超过则放入年轻代,否则放入老年代
    3. 如果老年代还放不下就要报错OOM了

10. 堆GC

  1. Java 中的堆也是 GC 收集垃圾的主要区域。

  2. GC 分为两种:一种是部分收集器(Partial GC)另一类是整堆收集器(Fu’ll GC)

  3. 部分收集器:不是完整收集java堆的的收集器,它又分为:

    1. 新生代收集(Minor GC / Young GC): 只是新生代的垃圾收集
    2. 老年代收集 (Major GC / Old GC): 只是老年代的垃圾收集 (CMS GC 单独回收老年代)
    3. 混合收集(Mixed GC):收集整个新生代及老年代的垃圾收集 (G1 GC会混合回收, region区域回收)
  4. 整堆收集(Full GC):收集整个java堆和方法区的垃圾收集器,但是回收速度也会非常慢

  5. 年轻代GC触发条件:

    1. 年轻代空间不足,就会触发Minor GC, 这里年轻代指的是Eden代满,Survivor满也不会引发GC
    2. Minor GC会引发STW(stop the world) ,暂停其他用户的线程,等垃圾回收接收,用户的线程才恢复
  6. 老年代GC (Major GC)触发机制

    1. 老年代空间不足时,会尝试触发MinorGC(YGC). 如果空间还是不足,则触发Major GC
    2. 如果Major GC , 内存仍然不足,则报错OOM
    3. Major GC的速度比Minor GC慢10倍以上.
  7. FullGC 触发机制:

    1. 调用System.gc() , 系统会执行Full GC ,不是立即执行.
    2. 老年代空间不足
    3. 方法区空间不足
    4. 通过Minor GC进入老年代平均大小大于老年代可用内存
  8. 注意:条有的过程中尽量减少FGC的执行

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值