• JVM之堆和方法区


    目录

    1.堆

    1.1 堆的结构

    1.1.1 新生代(Young Generation)

    1.1.2 年老代(Old Generation)

    1.1.3 永久代/元空间(Permanent Generation/Metaspace)

     1.2 堆的内存溢出

    1.3 堆内存诊断

    1.3.1 jmap

    1.3.2 jvisualvm

    2. 方法区

    2.1 方法区的结构


    1.堆

    JVM堆是Java程序运行时内存管理的核心,它主要用于存储对象实例和数组。堆内存的特点是动态分配和回收,它允许对象的创建和销毁,同时也需要注意内存泄漏和性能问题。

    1.1 堆的结构

    JVM堆通常被分为三个主要部分:

    1.1.1 新生代(Young Generation)

    新生代用于存储刚刚被创建的对象。它被分为三个区域:Eden空间和两个Survivor空间(通常称为From和To空间)。新创建的对象首先被分配到Eden空间,经过一次垃圾回收后,仍然存活的对象会被移到Survivor空间。多次循环后,仍然存活的对象会被移到年老代。

    1.1.2 年老代(Old Generation)

    年老代用于存储生命周期较长的对象。在新生代中经过多次垃圾回收后,仍然存活的对象会被晋升到年老代。年老代中的对象一般需要经历更多的垃圾回收周期才会被回收。

    1.1.3 永久代/元空间(Permanent Generation/Metaspace)

    在早期的JVM版本中,永久代用于存储类的元数据、方法信息以及静态变量。然而,由于永久代容易导致内存泄漏和溢出问题,1.8后JVM引入了元空间来代替。元空间的元数据存储在本地内存中,不再受到固定大小的限制。

    jdk1.8以及之后:在堆内存中,逻辑上存在,物理上不存在(元空间使用的是本地内存),如下图:

    什么是永久代和元空间?

    方法区是一种规范,不同的虚拟机厂商可以基于规范做出不同的实现,永久代和元空间就是出于不同jdk版本的实现。
    方法区就像是一个接口,永久代与元空间分别是两个不同的实现类。
    只不过永久代是这个接口最初的实现类,后来这个接口一直进行变更,直到最后彻底废弃这个实现类,由新实现类—元空间进行替代。

     1.2 堆的内存溢出

    使用如下代码:

    1. public class a {
    2. public static void main(String[] args) {
    3. List list=new ArrayList<>();
    4. String a="hello";
    5. while (true){
    6. a=a+a;
    7. list.add(a);
    8. }
    9. }
    10. }

    执行以上代码后就会发生堆内存溢出如下图:

    Java堆的大小可以通过命令行参数来配置,主要参数包括:

    • -Xms:设置堆的初始大小。
    • -Xmx:设置堆的最大大小。

    通常,将这两个参数设置为相同的值可以减少堆的动态调整,提高性能。例如:

    java -Xms512m -Xmx512m -jar YourApp.jar
    

    这将设置堆的初始大小和最大大小都为512兆字节。

    1.3 堆内存诊断

    1.3.1 jmap

    首先jps找到java运行的进程,然后jmap -heap 进程id就可以查看堆内存了,如下图:

    1.3.2 jvisualvm

    执行下面的代码:

    1. public class a {
    2. public static void main(String[] args) throws InterruptedException {
    3. Thread.sleep(30000);
    4. byte[] bytes = new byte[1024 * 1024 * 50];
    5. System.out.println("-------");
    6. Thread.sleep(30000);
    7. bytes=null;
    8. System.out.println("-------");
    9. Thread.sleep(30000);
    10. }
    11. }

    然后执行jvisualvm会得出如下图:当bytes=null后进行垃圾回收后,内存占用直接减少50M。如下图:

    2. 方法区

    方法区(Method Area)是JVM的另一个重要内存区域,它主要用于存储类的元数据、静态变量、常量池以及方法代码。

    2.1 方法区的结构

    方法区包含以下主要部分:

    类的元数据

    方法区存储了每个类的元数据,包括类名、父类、接口、字段、方法等信息。这些信息在程序运行时起到重要作用,例如方法的调用和字段的访问。

     静态变量

    静态变量属于类,不属于对象的实例。这些变量在类加载时初始化,存在于方法区中。

    常量池

    常量池包含了类中使用的常量,例如字符串、数字、类名等。它为类的运行时常量提供了存储空间。

    方法代码

    方法区存储了类中的方法代码,包括字节码指令和方法的字节码表示。这些代码在方法被调用时执行。

    在早期的JVM版本中,方法区被实现为永久代。然而,由于永久代的内存泄漏和性能问题,JVM在较新的版本中引入了元空间来替代永久代。元空间的好处是不再受限于固定大小,避免了永久代引起的一些问题。如下图:

  • 相关阅读:
    分享从零开始学习网络设备配置--任务3.7 使用动态路由RIPv2实现网络连通
    数据中台开源解决方案(一)
    【操作系统】1.3.2 中断和异常
    铭控传感亮相2023国际物联网展,聚焦“多场景物联感知方案”应用
    math_周期/三角函数系/傅里叶级数展开/正负交错符号的形式
    k8s--基础--18.6--存储卷--类型--PVC实践
    第一章三层交换应用
    【第一章:Java开发岗:基础篇】
    数据结构 循环队列
    操作系统作业第8章和第9章(个人重点题目笔记)
  • 原文地址:https://blog.csdn.net/qq_43649937/article/details/132626704