• jvm 04-JVM堆内存划分


    JVM堆内存划分

    • 在JDK1.8之后,将最初的永久带内存空间取消了,该图为JDK1.8之前的内存空间组成
    • 取消永久代目的是为了将HotSpot于JRockit两个虚拟机标准联合为一个
    • 在整个JVM堆内存之中实际上将内存分为了三部分:
      • 新生带(年轻代):新对象和没达到一定年龄的对象都在年轻代
      • 老年代:被长时间使用的对象,老年代的内存空间应该要比年轻代更大
      • 元空间(JDK1.8之前叫永久代):像一些方法中的操作临时对象等,JDK1.8之前是占用JVM内存,JDK1.8之后直接使用物理内存

    年轻代

    • 年轻代属于JVM堆内存空间的一个组成部分
    • 所有使用关键字new的新实例化对象都会在Eden区进行保存
    • 在存活区保存的一定是已经在Eden区中存活好久的,并经历或多次Minor GC的活跃对象
    • 存活区一般有两块空间,这两块空间大小相等且两块中一定有一块是空的
    • 存活区两块空间的作用:
      • 一块存活区为了晋升
      • 一块存活区为了对象回收

    Minor GC算法

    • 年轻代中的Minor GC算法采用的事复制算法
    • 复制算法:从根集合扫描出存活对象,并将该对象复制到一块新的完全为使用的空间中
    **(红色为不存活对象所占用的内存空间;绿色为存活对象所占用的内存空间)**
    • 由于Eden区保存的对象可能大部分为临时对象,容易频繁发生Minor GC

    • 为此HotSpot虚拟机采用了两种技术加快空间的内存分配操作

    • Bump-The-Pointer

      • 该技术跟踪Eden区保存的最后一个对象,该对象一般会保存在Eden区的顶部
      • 当创建新对象时,需要检测最后保存的对象后面是否有足够的空间
      • 这样可以很快的判断Eden区是否还有剩余空间
      • 该技术可以极大提高内存分配速度
    • TLAB(Thread-Local Allocation Buffers)

      • 为了适应多线程环境,TLAB算法将Eden区分为多个数据块
      • 每个数据块分别使用Bump-The-Pointer技术进行对象保存与内存分配

    年轻代内存调整参数

    • -Xmn:设置年轻代堆内存大小,默认为物理内存的1/64
    • -Xss:设置每个线程栈大小,JDK 1.5之后默认为每个线程分配1M的栈大小
    • 减少此数值可以产生更多的线程对象,但是不能无限生成
    • -XX:SurvivorRatio:设置Eden与Survivor空间的大小比例,默认8:1:1
    • -XX:NewSize:设置年轻代内存区大小
    • -XX:NewRatio:设置年轻代与老年代的比率

    例如:改变存活区比率

    -Xmx10m -Xms10m -XX:SurvivorRatio=6 -XX:+PrintGCDetails
    

    输出结果:

    max_memory=9.5M
    total_memory=9.5M
    Heap
     PSYoungGen      total 2560K, used 606K [0x00000007bfd00000, 0x00000007c0000000, 0x00000007c0000000)
      eden space 2048K, 29% used [0x00000007bfd00000,0x00000007bfd97a38,0x00000007bff00000)
      from space 512K, 0% used [0x00000007bff80000,0x00000007bff80000,0x00000007c0000000)
      to   space 512K, 0% used [0x00000007bff00000,0x00000007bff00000,0x00000007bff80000)
     ParOldGen       total 7168K, used 0K [0x00000007bf600000, 0x00000007bfd00000, 0x00000007bfd00000)
      object space 7168K, 0% used [0x00000007bf600000,0x00000007bf600000,0x00000007bfd00000)
     Metaspace       used 2708K, capacity 4486K, committed 4864K, reserved 1056768K
      class space    used 293K, capacity 386K, committed 512K, reserved 1048576K
    

    老年代

    • 主要接收由年轻代发送来的对象
    • 经历过多次Minor GC后还保存下来的对象才会进入到老年代
    • 如果需要保存的对象超过了Eden区大小,那此对象也将直接保存到老年代中
    • 当老年代内存不足时将引发Full GC(Major GC)
    • 老年代采用两种算法结合模式实现GC处理:整理-压缩

    Full GC算法

    • 标记-清楚(Mark-Sweep)

      • 从根集合开始扫描,对存活的对象进行标记,标记完毕后,在扫描整个空间中未标记的对象,并进行回收
      • 优缺点:在空间中存活对象较多的情况下较为高效,但由于该算法为直接回收不存活对象所占用的内存,因此会造成内存碎片。
    • 标记-压缩(Mark-Compact)

      • 在回收不存活对象所占用的内存空间后,会将其他所有存活对象都往左端空闲的空间进行移动,并更新引用其对象指针。
      • 优缺点:在标记-清除的基础上还需要进行对象移动,成本相对较高,好处则是不产生内存碎片

    老年代内存调整参数

    • -XX:NewRatio:设置年轻代与老年代的比率
    • -XX:+UseAdaptiveSizePolicy:控制是否采用动态控制策略
    • 如果动态控制,则动态调整java堆中各个区域的大小以及进入老年代的条件
    • -XX:PretenureSizeThreshold:控制直接进入老年代对象的大小,大于该值的对象会直接分配在老年代中

    例如:设置老年代参数

    -Xmx10m -Xms10m -XX:PretenureSizeThreshold=512k -XX:+PrintGCDetails
    

    元空间

    • 元空间是在JDK 1.8之后新增的,功能上和永久代一样
    • 永久代使用的是JVM堆内存空间,元空间使用的是物理内存
    • 元空间直接受到本机的物理内存限制

    元空间内存调整参数

    • -XX:MetaspaceSize:设置元空间大小
    • -XX:MaxMetaspaceSize:这种元空间最大容量,默认没限制,直接受到本地物理内存限制
    • -XX:MinMetaspaceFreeRatio:执行GC之后,最小的剩余元空间百分比,减少为分配空间所导致垃圾收集
    • -XX:MaxMetaspaceFreeRatio:执行GC之后,最大的剩余元空间百分比,减少为释放空间所导致的垃圾收集

    例如:设置一些参数导致元空间出错

    -XX:MetaspaceSize=1m -XX:MaxMetaspaceSize=1m
    

    输出结果:

    Error occurred during initialization of VM
    OutOfMemoryError: Metaspace
    

    总结:

    • Java中的GC会有两种回收:
      • 年轻代的Minor GC -- 新对象创建时如果Eden区空间不足会触发Minor GC
      • 老年代的Full GC -- 如果老年代内存空间不足会触发Full GC,也是gc()方法执行
      • 如果所有空间都不足则抛出OutOfMemoryError
    • 如果整个java项目运行缓慢,为了避免堆内存的伸缩空间进行操作,可将初始化内存空间和整体堆内存空间大小设置为一样。(使用 -Xms、-Xmx)
  • 相关阅读:
    在.net中读写XML方法的总结
    C# 中的委托和事件
    周永亮 《我是职业人》
    .net序列化及反序列化
    .NET中的参数
    linux解压tar命令
    Linux date 命令使用技巧
    经典.net面试题目
    switch……case里的case能不能同时定义多个值呢?
    这里将是我学习进步的一个见证点
  • 原文地址:https://www.cnblogs.com/liangjingfu/p/9951938.html
Copyright © 2020-2023  润新知