Java 中的堆栈内存和堆空间介绍

1. 简介

为了以最佳方式运行应用程序,JVM 将内存分为堆栈内存和堆内存。每当我们声明新的变量和对象,调用新方法,声明字符串或执行类似的操作时,JVM都会从堆栈内存或堆空间为这些操作指定内存。

在本教程中,我们将研究这些内存模型。首先,我们将探讨它们的主要功能。然后,我们将了解它们如何存储在RAM中,以及在哪里使用它们。最后,我们将讨论它们之间的主要区别。

2. Java 中的堆栈内存

Java 中的堆栈内存用于静态内存分配和线程的执行。它包含特定于方法的基元值,以及对堆中方法引用的对象的引用。

对此内存的访问按后进先出 (LIFO) 顺序进行。每当我们调用一个新方法时,都会在堆栈顶部创建一个新块,其中包含特定于该方法的值,例如原始变量和对对象的引用。

当方法完成执行时,将刷新其相应的堆栈帧,流返回到调用方法,并且空间可用于下一个方法。

2.1. 堆栈内存的主要特性

堆栈内存的其他一些功能包括:

  • 它分别随着新方法的调用和返回而增长和收缩。
  • 堆栈中的变量仅在创建它们的方法运行时存在。
  • 当方法完成执行时,它会自动分配和取消分配。
  • 如果此内存已满,Java 会抛出java.lang.StackOverFlowError。
  • 与堆内存相比,访问此内存的速度更快。
  • 此内存是线程安全的,因为每个线程都在自己的堆栈中运行。

3. Java 中的堆空间

堆空间用于在运行时动态分配 Java 对象和 JRE 类。新对象始终在堆空间中创建,对这些对象的引用存储在堆栈内存中。

这些对象具有全局访问权限,我们可以从应用程序中的任何位置访问它们。

我们可以将这个内存模型分解为更小的部分,称为世代,它们是:

  1. 年轻一代 – 这是所有新对象被分配和老化的地方。当垃圾填满时,会发生次要垃圾回收。
  2. 旧一代或终身一代 - 这是存储长期存活的对象的地方。当对象存储在年轻一代中时,将设置对象的年龄阈值,当达到该阈值时,对象将移动到旧一代。
  3. 永久生成 – 这包括运行时类和应用程序方法的 JVM 元数据。

3.1. Java 堆内存的主要特性

堆空间的其他一些功能包括:

  • 它通过复杂的内存管理技术访问,包括年轻一代、老一代或终身一代以及永久一代。
  • 如果堆空间已满,Java 会抛出java.lang.OutOfMemoryError。
  • 访问此内存比堆栈内存慢
  • 与堆栈相比,此内存不会自动解除分配。它需要垃圾收集器来释放未使用的对象,以保持内存使用的效率。
  • 与堆栈不同,堆不是线程安全的,需要通过正确同步代码来保护。

4. 示例

基于我们目前所学到的知识,让我们分析一个简单的 Java 代码来评估如何管理内存:

代码语言:javascript代码运行次数:0运行复制
class Person {
    int id;
    String name;

    public Person(int id, String name) {
        this.id = id;
        this.name = name;
    }
}

public class PersonBuilder {
    private static Person buildPerson(int id, String name) {
        return new Person(id, name);
    }

    public static void main(String[] args) {
        int id = 23;
        String name = "John";
        Person person = null;
        person = buildPerson(id, name);
    }
}Copy

我们来一步一步来分析一下:

  1. 当我们输入main() 方法时,会在堆栈内存中创建一个空间来存储此方法的原语和引用。
    • 堆栈内存直接存储整数 id 的基元值
    • 类型为 Person的引用变量person也将在堆栈内存中创建,它将指向堆中的实际对象。
  2. main()调用参数化构造函数Person(int,String)将在前一个堆栈之上分配更多的内存。这将存储:
    • 堆栈内存中调用对象的this对象引用
    • 堆栈内存中的基元值id
    • 字符串参数名称的引用变量它将指向堆内存中字符串池中的实际字符串
  3. 主要方法是进一步调用buildPerson()static 方法,为此,进一步的分配将在前一个方法之上的堆栈内存中进行。这将再次以上述方式存储变量。
  4. 但是,堆内存将存储新创建的 Person 类型的对象person的所有实例变量

让我们在下图中看一下这个分配:

5. 总结

在结束本文之前,让我们快速总结一下堆栈内存和堆空间之间的差异:

参数

堆栈内存

堆空间

Application

堆栈在部分中使用,在线程执行期间一次一个

整个应用程序在运行时使用堆空间

Size

堆栈的大小限制取决于操作系统,通常小于堆

堆上没有大小限制

Storage

仅存储基元变量和对在堆空间中创建的对象的引用

所有新创建的对象都存储在此处

Order

它使用后进先出 (LIFO) 内存分配系统进行访问

此内存通过复杂的内存管理技术访问,包括年轻一代、老一代或终身一代以及永久一代。

Life

堆栈内存仅在当前方法运行时存在

只要应用程序运行,堆空间就存在

Efficiency

与堆相比,分配速度更快

与堆栈相比,分配速度较慢

Allocation/Deallocation

当调用和返回方法时,将分别自动分配和取消分配此内存

当新对象被创建时分配堆空间,当不再引用它们时,Gargabe Collector 会释放这些对象

6. 结论

堆栈和堆是 Java 分配内存的两种方式。在本文中,我们了解了它们的工作原理,以及何时使用它们来开发更好的 Java 程序。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。 原始发表:2023-02-15,如有侵权请联系 cloudcommunity@tencent 删除java存储对象教程内存