”工欲善其事,必先利其器。“—孔子《论语.录灵公》
首页 > 编程 > GraalVM 本机映像中的内存管理

GraalVM 本机映像中的内存管理

发布于2024-09-12
浏览:983

内存管理是计算机软件开发的重要组成部分,负责应用程序中内存的有效分配、利用和释放。其重要性在于增强软件性能,保证系统稳定性。

垃圾收集

垃圾收集 (GC) 在 Java 和 Go 等当代编程语言中至关重要。它自动检测并回收未使用的内存,从而减轻开发人员手动管理内存的需要。 GC 的概念最初出现在 20 世纪 50 年代末的 LISP 编程语言中,标志着自动内存管理的引入。

自动化内存管理的主要优势包括:

  • 防止内存泄漏,高效内存利用。
  • 简化开发流程,增强程序稳定性。

了解内存中“垃圾”的本质并识别可回收空间至关重要。在接下来的章节中,我们将从探索垃圾收集的基本原理开始。

引用计数算法 [George E. Collins 1966]

引用计数算法在对象标头中分配一个字段来跟踪其引用计数。此计数随着每个新引用而增加,并在删除引用时减少。当计数达到零时,该对象就有资格进行垃圾回收。

考虑以下代码:

首先创建一个由d引用的值为demo的字符串(图1)。

String d = new String("demo");

Memory Management in GraalVM Native Image

图 1 – 创建字符串后

然后,将 d 设置为 null。 demo的引用计数为零。在引用计数算法中,需要回收demo的内存(图2)。

d =null; // Reference count of 'demo' becomes zero, prompting garbage collection.

Memory Management in GraalVM Native Image

图 2 – 当引用无效时

引用计数算法在程序执行期间运行,避免了Stop-The-World事件,该事件会暂时停止程序以进行垃圾收集。然而,它的主要缺点是无法处理循环引用(图 3)。

例如:

public class CircularReferenceDemo {

  public CircularReferenceDemo reference;
  private String name;

  public CircularReferenceDemo(String name) {
    this.name = name;
  }

  public void setReference(CircularReferenceDemo ref) {
    this.reference = ref;
  }

  public static void main(String[] args) {
    CircularReferenceDemo objA = new CircularReferenceDemo("Ref_A");
    CircularReferenceDemo objB = new CircularReferenceDemo("Ref_B");

    objA.setReference(objB);
    objB.setReference(objA);

    objA = null;
    objB = null;
  }
}

这里,尽管使外部引用无效,但 objA 和 objB 之间的相互引用阻止了它们的垃圾回收。

Memory Management in GraalVM Native Image

图 3 – 循环引用

我们可以看到这两个对象都无法再访问了。然而,它们是互相引用的,因此它们的引用计数永远不会为零。因此,GC 收集器永远不会被通知使用引用计数算法对它们进行垃圾收集。

该算法实际上是通过使用 std::shared_ptr 在 C 中实现的。 std::shared_ptr 旨在管理动态分配对象的生命周期,在创建或销毁指向对象的指针时自动增加和减少引用计数。该智能指针是 C 标准库的一部分,提供强大的内存管理功能,可显着降低与手动内存处理相关的风险。每当复制 std::shared_ptr 时,托管对象的内部引用计数就会增加,反映新的引用。相反,当 std::shared_ptr 被破坏、超出范围或重新分配给不同的对象时,引用计数就会减少。当引用计数为零时,分配的内存会自动回收并销毁对象,
通过确保没有必要时不会保留分配的对象,有效防止内存泄漏。

可达性分析算法[1978]

可达性分析算法从 GC 根开始,遍历对象图。无法从这些根到达的对象被视为不可恢复,并成为收集的目标。

如下图所示,蓝色圆圈内的对象应该保持存活,灰色圆圈内的对象可以回收(图4)。

Memory Management in GraalVM Native Image

图 4 – 内存泄漏

该方法有效解决了引用计数算法固有的循环引用问题。从 GC 根无法到达的对象将被分类进行收集。

通常,被视为 GC 根的 Java 对象包括:

  • 当前方法范围内的局部变量。
  • 活动 Java 线程。
  • 类中的静态字段。
  • 来自本机代码的 JNI 引用。

GraalVM 原生镜像概述

GraalVM 提供了一个提前 (AOT) 编译器,它将 Java 应用程序转换为独立的可执行二进制文件,称为 GraalVM Native Images。这些二进制文件由 Oracle 实验室开发
封装应用程序和库类以及 GC 等运行时组件,允许在没有 Java 运行时环境 (JRE) 的情况下进行操作。

该过程涉及静态分析以确定可访问的组件、通过执行块进行初始化,以及通过创建应用程序状态快照以供后续机器代码翻译来完成。

Substrate VM 的基础知识

Substrate VM 是 GraalVM 套件的一个组成部分,由 Oracle 实验室精心策划。它是一个增强的 JVM,不仅支持提前 (AOT) 编译,而且还有助于执行 Java 以外的语言,例如 JavaScript、Python、Ruby,甚至是 C 和 C 等本机语言。 Substrate VM 的核心是一个复杂的框架,允许 GraalVM 将 Java 应用程序编译成独立的本机二进制文件。这些二进制文件不依赖于传统的 Java 虚拟机 (JVM) 来执行,这简化了部署和
操作流程。

Substrate VM 的主要功能之一是其专门的垃圾收集器,它针对需要低延迟和最小内存占用的应用程序进行了微调。该垃圾收集器擅长处理与本机映像不同的独特内存布局和操作模型,这与在标准 JVM 上运行的传统 Java 应用程序有很大不同。 Substrate VM 本机映像中缺少即时 (JIT) 编译器是一种战略选择,有助于最小化可执行文件的总体大小。这是因为它消除了包含 JIT 编译器和相关元数据的必要性,这些元数据的大小和复杂性都很大。

此外,虽然 GraalVM 是使用 Java 开发的,但这会带来一定的限制,特别是在本机内存访问方面。此类限制主要是出于安全考虑以及保持跨平台兼容性的需要。然而,访问本机内存对于优化垃圾收集操作至关重要。为了解决这个问题,Substrate VM 采用了一套专门的接口来促进与本机内存的安全高效的交互。这些接口是更广泛的 GraalVM 架构的一部分,使 Substrate VM 能够以类似于 C 等较低级语言的方式有效管理内存,同时保留 Java 的安全性和可管理性。

在实践中,这些功能使 Substrate VM 成为一种极其通用的工具,可以增强使用 GraalVM 编译的应用程序的功能和效率。通过允许开发人员
Substrate VM 利用更广泛的编程语言并将其编译为高效的本机二进制文件,突破了传统 Java 开发环境所能实现的界限。这使其成为需要高性能、减少资源消耗和多种语言支持的现代软件开发项目的宝贵资产。

Substrate VM 值得注意的元素包括:

  • 通过用于原始内存操作的指针接口指针和用于处理字大小值的 WordBase 接口 WordBase 等接口简化内存访问。

  • 将堆划分为包含不可变对象的预初始化段和用于动态对象分配的运行时段(图 5)。

Memory Management in GraalVM Native Image

图 5 – 本机映像中的内存管理

在运行时,Substrate VM 中所谓的镜像堆包含在镜像构建过程中创建的对象。堆的这一部分是用可执行二进制文件的数据部分中的数据预先初始化的,并且可以在应用程序启动时轻松访问。驻留在图像堆中的对象被认为是不朽的;因此,这些对象中的引用被
视为根指针 垃圾收集器。但是,GC 仅扫描部分映像堆中的根指针,特别是那些未标记为只读的指针。

在构建过程中,指定为只读的对象被放置在映像堆的特定只读部分中。由于这些对象永远不会保存对运行时分配的对象的引用,因此它们不包含根指针,从而允许 GC 在扫描期间绕过它们。同样,仅由原始数据或原始类型数组组成的对象也缺少根指针。此属性进一步简化了垃圾收集过程,因为这些对象可以从 GC 扫描中省略。

相反,Java 堆被指定用于保存在运行时动态创建的普通对象。堆的这一部分会定期进行垃圾收集,以回收不再使用的对象占用的空间。它被构造为具有老化机制的分代堆,随着时间的推移促进高效的内存管理。

预初始化、永久映像堆和动态管理的 Java 堆之间的这种划分使 Substrate VM 能够优化内存使用和垃圾收集效率,满足应用程序内存需求的静态和动态方面。

堆块

在 Substrate VM 的堆模型中,内存被系统地组织成称为堆块的结构。这些块的默认大小通常为 1024KB,形成一个连续的虚拟内存段,仅分配给对象存储。这些块的组织结构是一个链表,其中尾部块代表最近添加的段。这样的模型
促进高效的内存分配和对象管理。

这些堆块进一步分为两种类型:对齐和未对齐。对齐的堆块能够连续保存多个对象。这种对齐允许更简单地映射
对象到各自的父堆块,使内存管理更加直观和高效。在需要对象提升的场景中 - 通常是在垃圾收集期间和
内存优化——对象从其在父堆块中的原始位置移动到位于指定的“旧目标空间”中的目标堆块。此迁移是分代堆管理策略的一部分,该策略通过将年轻对象与旧对象分离来帮助优化垃圾收集过程,从而减少 GC 周期期间的开销。

本机映像中的垃圾收集器

GraalVM Native Image支持针对不同需求定制的各种GC:

  • 串行 GC: 适用于单线程应用程序的默认低占用空间收集器。

  • G1 垃圾收集器: 专为具有大堆大小的多线程应用程序而设计,增强了生成管理的灵活性。

  • Epsilon GC: 一种简约的收集器,处理分配但缺乏回收,最适用于可预测堆利用率的短期应用程序。

结论

总而言之,Substrate VM 通过结合专门的垃圾收集和结构化堆管理等先进技术,有效地优化了 GraalVM 中的内存管理。这些功能(包括堆块以及用于图像和 Java 堆的单独内存段)可简化垃圾收集并提高应用程序性能。由于 Substrate VM 支持多种编程语言并将其编译为高效的本机二进制文件,因此它展示了现代 JVM 框架如何超越传统边界,以提高不同应用程序环境中的执行效率和鲁棒性。这种方法为虚拟机技术和应用程序部署的未来发展设定了高标准。

版本声明 本文转载于:https://dev.to/yanev/memory-management-in-graalvm-native-image-4nbe?1如有侵犯,请联系[email protected]删除
最新教程 更多>
  • 获取下一行学习如何处理文件描述符和系统 I/O 的项目
    获取下一行学习如何处理文件描述符和系统 I/O 的项目
    在 C 编程领域,有效管理输入、输出和内存是基础。为了帮助您掌握这些关键概念,您将在 get_next_line 项目中编写一个使用文件描述符逐行读取文件的函数。每次调用该函数都会从文件中读取下一行,从而允许您一次一行处理整个文件内容。 了解系统中的文件描述符和 I/O ...
    编程 发布于2024-11-07
  • 为什么Go的main函数有死循环?
    为什么Go的main函数有死循环?
    Go 运行时:主函数中无限循环之谜Go 运行时的核心位于 src/runtime/proc.go,其中有一个令人费解的功能:主函数末尾有一个无限的 for 循环。人们可能想知道为什么运行时中存在这样一个看似毫无意义的构造。目的:检测致命错误深入研究代码,很明显循环服务于错误处理的关键目的。当发生致命...
    编程 发布于2024-11-07
  • iostream 与 iostream.h:在现代 C++ 中应该使用哪个?
    iostream 与 iostream.h:在现代 C++ 中应该使用哪个?
    iostream 和 iostream.h 之间的区别在 C 中,程序员可能会遇到两个具有类似目的的术语:iostream 和 iostream.h 。本指南旨在阐明两者之间的根本区别。iostream.h:已弃用的旧版本iostream.h 是 C 库中的一个头文件,它提供一组输入/输出函数。对于...
    编程 发布于2024-11-07
  • VLONE Clothing:重新定义都市时尚的街头服饰品牌
    VLONE Clothing:重新定义都市时尚的街头服饰品牌
    VLONE 是少数几个在快速变化的市场中取得超越街头服饰行业所能想象的成就的品牌之一。 VLONE 由 A$AP Mob 集体的电影制片人之一 A$AP Bari 创立,现已发展成为一个小众项目,有时甚至成为都市时尚界的国际知名品牌。 VLONE 凭借大胆的图案、深厚的文化联系和限量版发售,在时尚界...
    编程 发布于2024-11-07
  • 如何使用PDO查询单行中的单列?
    如何使用PDO查询单行中的单列?
    使用 PDO 查询单行中的单列处理针对单行中特定列的 SQL 查询时,通常需要检索直接取值,无需循环。要使用 PDO 完成此操作,fetchColumn() 方法就派上用场了。fetchColumn() 的语法为:$col_value = $stmt->fetchColumn([column_...
    编程 发布于2024-11-07
  • 我是如何构建 PeerSplit:一款免费的点对点费用分摊应用程序 — 从构思到发布仅需数周时间
    我是如何构建 PeerSplit:一款免费的点对点费用分摊应用程序 — 从构思到发布仅需数周时间
    我构建了 PeerSplit——一个免费的、点对点的 Splitwise 替代品——从想法到发布仅用了两周时间! PeerSplit 是一款本地优先的应用程序,用于分配团体费用。它可以离线工作,100% 免费且私密,不需要注册或任何个人数据。 以下是我如何构建它以及我在此过程中学到的一切。 ...
    编程 发布于2024-11-07
  • 如何在 PHP 中解析子域名的根域名?
    如何在 PHP 中解析子域名的根域名?
    在 PHP 中从子域解析域名在 PHP 中,从子域中提取根域名是一项常见任务。当您需要识别与子域关联的主网站时,这非常有用。为了实现这一目标,让我们探索一个解决方案。提供的代码片段利用 parse_url 函数将 URL 分解为其组件,包括域名。随后,它使用正则表达式来隔离根域,而忽略子域。以下示例...
    编程 发布于2024-11-07
  • 使用 Socket.io 构建实时应用程序
    使用 Socket.io 构建实时应用程序
    介绍 Socket.io 是一个 JavaScript 库,允许 Web 客户端和服务器之间进行实时通信。它支持创建交互式动态应用程序,例如聊天室、多人游戏和直播。凭借其易于使用的 API 和跨平台兼容性,Socket.io 已成为构建实时应用程序的流行选择。在本文中,我们将探讨 ...
    编程 发布于2024-11-07
  • 重写 `hashCode()` 和 `equals()` 如何影响 HashMap 性能?
    重写 `hashCode()` 和 `equals()` 如何影响 HashMap 性能?
    了解 equals 和 hashCode 在 HashMap 中的工作原理Java 中的 HashMap 使用 hashCode() 和 equals() 方法的组合来有效地存储和检索键值对。当添加新的键值对时,首先计算键的hashCode()方法,以确定该条目将被放置在哪个哈希桶中。然后使用 eq...
    编程 发布于2024-11-07
  • 使用 Google Apps 脚本和 Leaflet.js 构建交互式 XY 图像图
    使用 Google Apps 脚本和 Leaflet.js 构建交互式 XY 图像图
    Google Maps has a ton of features for plotting points on a map, but what if you want to plot points on an image? These XY Image Plot maps are commonly...
    编程 发布于2024-11-07
  • 理解 React 中的状态变量:原因和方法
    理解 React 中的状态变量:原因和方法
    在深入研究状态变量之前,让我们先来分析一下 React 组件的工作原理! 什么是 React 组件? 在 React 中,组件是一段可重用的代码,代表用户界面 (UI) 的一部分。它可以像 HTML 按钮一样简单,也可以像完整的页面一样复杂。 React 中的状态...
    编程 发布于2024-11-07
  • Miva 的日子:第 4 天
    Miva 的日子:第 4 天
    这是 100 天 Miva 编码挑战的第四天。我跳过了第三天的报告,因为我被困在我的网页设计项目中,需要改变节奏。这就是为什么我今天决定深入研究 JavaScript。 JavaScript JavaScript 就像系统和网站的行为组件。它为网站增加了交互性和响应能力,使其成为网页设计和开发的关...
    编程 发布于2024-11-07
  • TailGrids React:+ Tailwind CSS React UI 组件
    TailGrids React:+ Tailwind CSS React UI 组件
    我们很高兴推出 TailGrids React,这是您的新首选工具包,可用于轻松构建令人惊叹的响应式 Web 界面。 TailGrids React 提供了超过 600 免费和高级 React UI 组件、块、部分和模板的大量集合 - 所有这些都是用 Tailwind CSS 精心制作的。 无论您...
    编程 发布于2024-11-07
  • 如何用列表值反转字典?
    如何用列表值反转字典?
    使用列表值反转字典:解决方案在本文中,我们探讨了使用列表值反转字典的挑战。给定一个索引字典,其中键是文件名,值是这些文件中出现的单词列表,我们的目标是创建一个倒排字典,其中单词是键,值是文件名列表。提供的反转函数 invert_dict,不适用于以列表值作为键的字典,因为它会失败并显示“TypeEr...
    编程 发布于2024-11-07
  • 现代 Web 开发框架:比较流行的框架及其用例
    现代 Web 开发框架:比较流行的框架及其用例
    在快速发展的 Web 开发领域,选择正确的框架可以显着影响项目的成功。本文深入研究了一些最流行的 Web 开发框架,比较了它们的优势和理想用例,以帮助开发人员做出明智的决策。 反应 概述 React 由 Facebook 开发和维护,是一个用于构建用户界面的 Java...
    编程 发布于2024-11-07

免责声明: 提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发到邮箱:[email protected] 我们会第一时间内为您处理。

Copyright© 2022 湘ICP备2022001581号-3