Java Apps 如何超越堆

发布于:2023-01-16 ⋅ 阅读:(749) ⋅ 点赞:(0)

作为Java开发人员,我们对垃圾回收的概念并不陌生。我们的应用程序一直在生成垃圾,并且CMS,G1,Azul C4和其他类型的收集器会精心清理这些垃圾。基本上,我们的应用程序是为这个世界带来价值而生的,但是,没有什么是完美的 - 包括我们的应用程序在Java堆中留下了垃圾。

但是,故事并没有以Java堆结束。事实上,它只是从那里开始。让我们以一个基本的Java应用程序为例,该应用程序使用关系数据库(如PostgreSQL和固态硬盘(SSD))作为存储设备。从这里开始,我们将探讨我们的应用程序如何在 Java 运行时边界之外生成垃圾。

用死元组填充PostgreSQL

当 Java 应用程序对 PostgreSQL 数据库执行 DELETE 或 UPDATE 语句时,不会立即删除已删除的记录,也不会更新现有记录。相反,已删除的记录被标记为死元组,并将保留在存储中。实际上,更新的记录是PostgreSQL通过复制以前版本的记录并更新请求的列来插入的全新记录。该更新记录的以前版本被视为已删除,并且与 DELETE 操作一样,标记为死元组。

数据库引擎将已删除和更新的记录的旧版本保留在其存储中是有充分理由的。对于初学者来说,你的应用程序可以并行运行一堆针对PostgreSQL的事务。其中一些交易确实比其他交易更早开始。但是,如果事务删除了以前启动的一些事务可能仍然感兴趣的记录,则需要将该记录保存在数据库中(至少在所有较早开始的事务完成之前的时间点)。这就是PostgreSQL实现MVCC(多版本并发协议)的方式

很明显,PostgreSQL不能也不想永远保留死元组。这就是为什么数据库有自己的垃圾回收过程,称为清空。真空有两种类型-普通真空和完整真空。普通 VACUUM 与您的应用程序工作负载并行工作,不会阻止您的查询。这种类型的清空操作将失效元组占用的空间标记为可用,使其可用于应用稍后将添加到同一表中的新数据。普通的 VACUUM 不会将空间返回到操作系统,以便其他表或第三方应用程序可以重用它(除非在某些极端情况下,页面仅包含失效元组,并且页面位于表的末尾)。

 

(并发)真空示例

相比之下,完整的 VACUUM 确实会回收操作系统的可用空间,但它会阻止应用程序工作负载。你可以把它想象成Java的“停止世界”垃圾收集暂停。只有在PostgreSQL中,这样的暂停才能持续数小时(或数天)。因此,数据库管理员会尽最大努力防止完全 VACUUM 的发生。

让我在这里停下来,进入下一个层次 — SSD。如果您想更深入地了解吸尘,请查看这篇演示驱动的文章

在 SSD 中生成过时数据

如果您认为垃圾回收只是为了软件,那么...惊喜,惊喜!某些硬件设备还需要执行垃圾回收例程。SSD一直在进行垃圾回收!

每当您的Java应用程序删除或更新磁盘上的任何数据时 - 通过上面讨论的PostgreSQL或直接通过Java File API - 然后应用程序会在SSD上生成垃圾。

SSD将数据存储在页面中(通常大小在4KB到16KB之间),后者按块分组。虽然可以在页面级别写入或读取数据,但只能在块级别擦除过时(已删除)的数据。擦除需要比读取/写入操作更多的电压,并且很难在不影响相邻单元的情况下在页面级别定位该电压。

因此,如果您的Java应用程序更新了文件,那么实际上,更新的段将被写入到另一个块中的空页面。包含旧数据的段将被标记为过时,稍后将收集垃圾。首先,SSD中的垃圾回收器遍历具有陈旧数据的页面块,并将好的数据移动到其他块(类似于Java的G1收集器中的压缩阶段)。其次,收集器擦除仅剩下过时数据的块,并使这些块可用于将来的数据。

 

SSD 中的垃圾回收示例

好奇SSD制造商如何防止或最大限度地减少“停止世界”暂停的次数?有一个 SSD 过度配置的概念,即每个设备都附带一个额外的空间,而你的应用无法使用。该空间是一种安全缓冲区,允许应用继续写入或修改数据,同时垃圾回收器同时擦除过时数据。在此处阅读有关过度预配的详细信息

总结

因此,下次有人要求您解释Java垃圾回收的内部结构时,请继续通过扩展主题以包括数据库和硬件来给他们带来惊喜。

严肃地说,垃圾回收是一种广泛使用的技术,其使用远远超出了Java生态系统。如果实施得当,垃圾回收可以简化软件和硬件的体系结构,而不会影响性能。Java,PostgreSQL和SSD都是成功利用垃圾回收的产品的好例子,并且仍然是其类别中的顶级产品之一。

本文含有隐藏内容,请 开通VIP 后查看

网站公告

今日签到

点亮在社区的每一天
去签到