JVM之堆和方法区

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

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 堆的内存溢出

使用如下代码:

public class a {
    public static void main(String[] args)  {
        List<String> list=new ArrayList<>();
        String a="hello";
        while (true){
            a=a+a;
            list.add(a);
        }
    }
}


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



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

执行下面的代码:

public class a {
    public static void main(String[] args) throws InterruptedException {
        Thread.sleep(30000);
        byte[] bytes = new byte[1024 * 1024 * 50];
        System.out.println("-------");
       Thread.sleep(30000);
        bytes=null;
        System.out.println("-------");
        Thread.sleep(30000);
    }
}


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



2. 方法区

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


2.1 方法区的结构

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


类的元数据


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


静态变量


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


常量池


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


方法代码


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


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

相关文章
|
2月前
|
存储 算法 Java
惊!Java程序员必看:JVM调优揭秘,堆溢出、栈溢出如何巧妙化解?
【8月更文挑战第29天】在Java领域,JVM是代码运行的基础,但需适当调优以发挥最佳性能。本文探讨了JVM中常见的堆溢出和栈溢出问题及其解决方法。堆溢出发生在堆空间不足时,可通过增加堆空间、优化代码及释放对象解决;栈溢出则因递归调用过深或线程过多引起,调整栈大小、优化算法和使用线程池可有效应对。通过合理配置和调优JVM,可确保Java应用稳定高效运行。
113 4
|
2月前
|
存储 Java Linux
32 位和 64 位 JVM 的最大堆大小是多少?
【8月更文挑战第22天】
115 0
|
2月前
|
存储 安全 Java
JVM常见面试题(二):JVM是什么、由哪些部分组成、运行流程,JDK、JRE、JVM关系;程序计数器,堆,虚拟机栈,堆栈的区别是什么,方法区,直接内存
JVM常见面试题(二):JVM是什么、由哪些部分组成、运行流程是什么,JDK、JRE、JVM的联系与区别;什么是程序计数器,堆,虚拟机栈,栈内存溢出,堆栈的区别是什么,方法区,直接内存
JVM常见面试题(二):JVM是什么、由哪些部分组成、运行流程,JDK、JRE、JVM关系;程序计数器,堆,虚拟机栈,堆栈的区别是什么,方法区,直接内存
|
2月前
|
存储 缓存 Java
JVM中的方法区
这篇文章介绍了JVM中方法区的概念和作用,包括它所存储的内容(类型信息、常量、静态变量、编译后的代码缓存),常量池与运行时常量池的功能,以及方法区与栈、堆的交互关系。
JVM中的方法区
|
2月前
|
存储 Java
JVM中的堆
这篇文章详细介绍了JVM中的堆内存,包括堆的核心概念、内存细分、堆空间大小设置以及Java 7和8版本堆内存逻辑上的不同划分。
JVM中的堆
|
3月前
|
Java
Jinfo 查看 jvm 配置及使用 Jstat 查看堆内存使用与垃圾回收
Jinfo 查看 jvm 配置及使用 Jstat 查看堆内存使用与垃圾回收
56 5
|
3月前
|
监控 Java
JVM内存问题之使用jstat命令查看GC堆百分比占比情况,应该使用哪个选项
JVM内存问题之使用jstat命令查看GC堆百分比占比情况,应该使用哪个选项
|
3月前
|
存储 消息中间件 监控
JVM内存问题之ARMS监控显示堆内存和我设置的不同如何解决
JVM内存问题之ARMS监控显示堆内存和我设置的不同如何解决
|
3月前
|
Java
JVM内存问题之JVM堆内存是由哪几个区域组成的
JVM内存问题之JVM堆内存是由哪几个区域组成的
|
3月前
|
存储 Java 对象存储
Java虚拟机(JVM)中的栈(Stack)和堆(Heap)
在Java虚拟机(JVM)中,栈(Stack)和堆(Heap)是存储数据的两个关键区域。它们在内存管理中扮演着非常重要的角色,但各自的用途和特点有所不同。
42 0