11.堆

  1. 堆针对一个JVM进程来说是唯一的,一个进程只有一个JVM,但是一个进程包含多个线程,他们共享同一个堆空间

  2. JVM实例只存在一个堆内存,堆也是java内存管理的核心区域

  3. Java堆去在JVM启动的时候即可被创建,其空间大小已经确定了,是JVM管理的最大一块内存空间

    1. 堆内存大小是可以调节的
  4. 堆可以处于物理上不连续的内存空间中,但逻辑上他们被视为连续的

  5. 所有线程共享堆空间,堆里面也有划分的线程私有的缓冲区 Thread Local Allocation Buffer TLAB

    1. -Xms10m 最小堆内存
    2. -Xmx 最大堆内存

在这里插入图片描述

  1. 几乎所有对象实例都在堆中分配内存,还有一些在栈上分配,数组和对象可能永远不会存储在栈上,因为栈帧中保留引用,这个引用指向了对象或者数组在堆中的位置

  2. The heap is the run-time data area from which memory for all class instances and arrays is allcated 所有对象实例及数组都应该当在运行时分配到堆上

  3. 方法结束之后堆中的对象不会马上移除,仅在触发GC的时候才会移除

  4. 在GC回收的时候因为STW (stop the world)的影响,用户线程会暂定执行,所以减少GC运行的次数和时间可以有效优化程序

  5. 堆是GC执行垃圾回收的终点区域

    image-20200706201904057

  6. 堆内存划分

    1. JDK7以前堆内存逻辑上分为三部分
      • Young Genration Space 新生代 New/Young 又细分为Eden伊甸园区和Survivor0 Survivor1区
      • Tenure Genration Space 老年代 Old/Tenure
      • Permanent Space 永久代 Perm
    2. JDK8之后内存逻辑上也分为三部分主要跟JDK7以前的区别在于永久代变成了元空间
      • Young Genration Space 新生代 New/Young 又细分为Eden伊甸园区和Survivor0 Survivor1区
      • Tenure Genration Space 老年代 Old/Tenure
      • Meta Space 元空间 Meta
        在这里插入图片描述
  7. 设置堆大小与OOM

    • java堆区用于存储java对象实例,堆的大小在JVM启动时候已经设定好了
      • -Xms 设置起始堆空间大小 memory start 或者 -XX:InitialHeapSize
      • -Xmx 设置堆最大内存空间 memory max 或者 -XX:maxHeapSize
    • 一旦堆区中的内存大小超过了-Xmx指定的内存大小就会抛出OOM异常
    • 通常在生产中-Xms和-Xmx两个参数设置成相同,为了能够在GC在清理完堆区之后不需要重新分割计算堆区大小从而提高性能
  8. 默认值

    • 初始内存大小默认为系统内存/64

    • 最大内存大小默认为系统内存/4

    • public class HeapTest2 {
          public static void main(String[] args) {
              // 返回Java虚拟机中的堆内存总量
              long initialMemory = Runtime.getRuntime().totalMemory() / 1024 / 1024;
              // 返回Java虚拟机试图使用的最大堆内存
              long maxMemory = Runtime.getRuntime().maxMemory() / 1024 / 1024;
              System.out.println("-Xms:" + initialMemory + "M");
              System.out.println("-Xmx:" + maxMemory + "M");
          }
      }
      
  9. 使用jstat -gc 进程ID 查看内存分配情况

在这里插入图片描述

- S0C  S1C Survivor 0/1 Capacity 幸存者区总容量
- S0U S1U Survivor 0/1 Used 已使用的幸存者区
- EC Eden Capacity
- EU Eden Used
- OC Old Capacity
- OU Old Used
- MC Meta Capacity
- MU Meta Used
  1. JVM参数 -XX:+PrintGCDetails

在这里插入图片描述

  1. 可以使用JVisualVM 中的抽样器查看哪些资源内存占用高

  2. 新生代和老年代

    • 一类生命周期较短的瞬时对象,对象的创建和消亡都非常迅速通,常存放在新生代,也可能因为在Eden满执行GC的时候此时TO也满了,会直接放在O区
    • 另一类对象生命周期非常长,极端情况下与JVM生命周期一致,通常放在O区
  3. 新生代又可细分为Eden空间和Survivor0和Survior1 也称from to区
    在这里插入图片描述

  4. 新生代中可调节的参数

    1. 默认-XX:NewRatio=2 表示新生代占1 老年代占2 新生代占整个堆的1/3
    2. 可修改-XX:NewRatio=4 表示新生代占1 老年代占4 新生代栈整个堆的1/5
    3. 如果发现整个项目中,生命周期长的对象偏多,就可以通过调整老年代大小
  5. 在HotSpot中,Eden空间和另外两个Survivor的默认比例是8:1:1,也可以通过-XX:SurviorRatio调整空间比例,但是实际在JVisualVm中查看其实是6:1:1 需要显示指定–XX:SurviorRatio=8

  6. 几乎所有java对象都在Eden区被new出来,绝大多数java对象在新生代销毁.有些大对象在Eden中无法存储会直接进入老年代

  7. 80%的对象都是朝生夕死,也可以使用-Xmn来设置新生代的最大内存大小,一般不用设置,如果设置了具体大小,则使用具体大小-XX:NewRatio参数失效

  8. 新生代中的对象存在一个生命周期计数器的概念,默认生命周期计数器达到15的时候会进入老年代,或者当有一半以上对象的生命周期计数器都大于2也会进入老年代 也可以通过-XX:MaxTenuringThreshold=N进行设置

图解对象分配的过程

  • 对一个新对象的内存分配是一个非常复杂严谨的事,JVM设计者不仅需要考虑内存如何分配,分配到哪里的问题,也需要考虑与内存分配密切相关的内存回收算法,还需要考虑GC在执行完成之后是否会在内存空间产生内存碎片

    1. new的对象先进入Eden 如果大于Eden空间直接进入Old
    2. 当Eden空间满的时候,程序还需要创建对象,触发GC(MinorGC),会对Eden和S1/S0进行垃圾回收,不被其他对象引用的对象销毁
    3. 触发 GC之后 Eden和幸存者FROM区的剩余对象 移动到 幸存者TO区
    4. 如果FROM区中的生命周期计数器大于15将对象放入OLD区
    5. 当OLD区内存不足,再次触发GC(MajorGC)会被OLD NEW区进行垃圾回收
    6. 如果进行MajorGC之后依然无法存储对象 产生OOM
  • 图解过程

    • 我们创建的对象,一般都是存放在Eden区的,当我们Eden区满了后,就会触发GC操作,一般被称为 YGC / Minor GC操作

    • image-20200707084714886

    • 当我们进行一次垃圾收集后,红色的将会被回收,而绿色的还会被占用着,存放在S0(Survivor From)区。同时我们给每个对象设置了一个年龄计数器,一次回收后就是1。

      同时Eden区继续存放对象,当Eden区再次存满的时候,又会触发一个MinorGC操作,此时GC将会把 Eden和Survivor From中的对象 进行一次收集,把存活的对象放到 Survivor To区,同时让年龄 + 1

      image-20200707085232646

    • 我们继续不断的进行对象生成 和 垃圾回收,当Survivor中的对象的年龄达到15的时候,将会触发一次 Promotion晋升的操作,也就是将年轻代中的对象 晋升到 老年代中image-20200707085737207

      • 只有在Eden满的时候才会触发MinorGC Survivor满的时候不会触发GC

      • 当Survivor满的时候会直接放入Old

对象分配特殊情况

image-20200707091058346

常用调优工具

  • java自带的命令 jinfo jstat Jmap等
  • Eclipse: Memory Analyzer Tool
  • Jconsole
  • Visual VM
  • Jprofiler
  • Java Flight Recorder
  • GCViewer
  • GCEasy
  • 垃圾回收

总结

  • 针对幸存者S0 和S1 的总结: 复制中以后有交换 谁空谁是TO
  • 关于垃圾回收,频繁出现在新生代,很少出现在老年代,几乎不出现在永久代和元空间
  • 新生代采用复制算法目的是为了减少内存碎片

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值