我是靠谱客的博主 怕孤独日记本,最近开发中收集的这篇文章主要介绍JVM内存和垃圾回收-07.堆,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

1.核心概述

  • 一个JVM只存在一个堆内存
  • 堆在JVM启动时就被创建,大小也确定下来了(可以调节)
  • 堆可以在物理上不连续,逻辑上视为连续
  • 所有线程共享Java堆,但可以划分线程私有的缓冲区(TLAB)
  • 所有对象实例和数组都分配在堆上
  • 方法结束后堆中的对象不会马上被移除,在GC时才被移除(堆是GC的重点区域)
  • 内存细分:
    • Java7及之前堆内存逻辑上分为:新生区+养老区+永久区
    • Java8及之后堆内存逻辑上分为:新生区+养老区+元空间

在这里插入图片描述


2.堆空间大小设置

  • -Xms:表示堆区(年轻代+老年代)的起始内存(默认物理电脑内存大小/64)
  • -Xmx:表示堆区(年轻代+老年代)的最大内存,堆中内存大小超过该值就抛出OOM(默认物理电脑内存大小/4)

通过将上述两个参数设置为相同的值,目的是在GC清理完堆区后不需要重新分隔计算堆区大小,提供性能


3.年轻代和老年代

  • 在JVM中的对象可以根据存活时间分为两类:

    • 生命周期短的对象,该类对象的创建和消亡非常迅速

    • 生命周期长的对象,该类对象没必要每次GC都去判断是否回收

  • 堆区的划分(一般不包括方法区)可以进一步分为年轻代和老年代(可以通过-XX:NewRatio设置二者占比;通过-XX:SurvivorRatio设置年轻代中三个部分的占比)

在这里插入图片描述

  • 几乎所有Java对象都是在Eden区被new出来的(一些较大的对象并不是在该区new出来的)
  • 大部分的Java对象的销毁都是在新生代进行(即GC频繁发生在新生区,很少在养老区,几乎不在永久区/元空间收集)

为什么要分代?为了优化GC性能。假如不分代,则GC的时候要找到哪些对象没用,则需要对整个堆进行扫描。其实很多对象都是存活周期很短,把这些对象放在一个特定的地方(即分代),GC的时候就可以经常去处理这块区域


4.对象分配过程

内存分配算法和内存回收算法密切相关,所以需要考虑GC执行完内存回收后是否会存在内存碎片

对象分配过程如下:

  1. new的对象放在Eden区
  2. Eden区填满后,此时程序还要继续创建对象时,垃圾回收器会在Eden区进行GC(Minor GC/Young GC),将该区中不再被其他对象引用的对象销毁,然后再加载新对象
  3. 将Eden区中剩余对象移动到Survivor0区(绿色代表幸存对象,红色代表被GC的对象,数字1表示年龄计数器为1)

在这里插入图片描述

  • 如果再次触发垃圾回收,会将此次幸存对象(Eden区的)和Survivor0区且未被GC的对象都放在Survivor1区,同时年龄计数器也要相应改变(注意此时Survivor0区变空)。此时的Survivor0区为from区,Survivor1区为to区

在这里插入图片描述

  • 下次再进行GC就是将当前Eden区幸存对象和在Survivor1区且未被GC的对象都放在Survivor0区。此时的Survivor1区为from区,Survivor0区为to区

  • 当在Survivor1区或Survivor0区中的对象的年龄计数器已经累加到15,将该对象转移到养老区

在这里插入图片描述

  • 在养老区中内存不足时,会触发Major GC;如果进行GC后依旧无法保存对象,报OOM异常

注意:Minor GC是在Eden区满了才触发,Survivor0/1区满了是不会触发Minor GC,但是Minor GC在回收的时候是将Eden区和Survivor0/1区一起回收

综上所述,整体流程如下图:

在这里插入图片描述


5.Minor GC、Major GC和Full GC

  • JVM进行GC时并不是每次对新生代、老年代和方法区一起回收。所以分为两个类型:

    • 部分收集:
      • Minor GC:只对新生代的GC
      • Major GC:只对老年代的GC
      • Mixed GC:对整个新生代以及部分老年代的GC
    • 整堆收集(Full GC):对整个堆和方法区进行GC
  • Minor GC的触发:

    • 年轻代空间(指Eden区,Survivor满时不触发GC)不足时
    • 该GC速度快
    • 该GC会导致STW(即暂停其他用户线程,等GC结束才恢复)
  • Major GC的触发:

    • 老年代空间不足时,会先尝试Minor GC,空间还是不足就触发Major GC
    • 该GC比Minor GC慢10倍,STW的时间也更长
  • Full GC的触发:

    • 调用System.gc()
    • 老年代空间不足
    • 方法区空间不足
    • 通过Minir GC进入老年代的对象大小大于老年代可用内存

6.为对象分配内存:TLAB

  • 为什么有TLAB(Thread Local Allocatuin Buffer)?

    • 由于堆是线程共享区域,所以并发情况下在堆中创建对象是线程不安全的(因为不同线程可能在同一个堆中某部分内存上创建不同的对象)
    • 加锁的话影响内存的分配速度
  • 什么是TLAB?

    • 在Eden区为每个线程分配一个私有的缓存区域TLAB
    • 每个线程在各自的TLAB中创建对象

    在这里插入图片描述

  • 对象分配内存的过程:

    在这里插入图片描述


最后

以上就是怕孤独日记本为你收集整理的JVM内存和垃圾回收-07.堆的全部内容,希望文章能够帮你解决JVM内存和垃圾回收-07.堆所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(49)

评论列表共有 0 条评论

立即
投稿
返回
顶部