总览

最近有人问我在Java中使用堆内存的好处和智慧。 面临相同选择的其他人可能会对这些答案感兴趣。

堆外内存没什么特别的。 线程堆栈,应用程序代码,NIO缓冲区都在堆外。 实际上,在C和C ++中,您只有非托管内存,因为默认情况下它没有托管堆。 Java中托管内存或“堆”的使用是该语言的一个特殊功能。 注意:Java不是执行此操作的唯一语言。

新的Object()vs对象池vs Off堆内存

新的Object()

在Java 5.0之前,使用对象池非常流行。 创建对象仍然非常昂贵。 但是,从Java 5.0开始,对象分配和垃圾清理变得便宜得多,并且开发人员发现,通过删除对象池并仅在需要时创建新对象,它们可以提高性能,并简化代码。 在Java 5.0之前,几乎所有对象池(甚至是使用对象的对象池)都提供了改进,从Java 5.0来看,只有昂贵的对象才有意义,例如线程,套接字和数据库连接。

对象池

在低延迟空间中,显而易见的是,通过减少对CPU缓存的压力,回收可变对象可以提高性能。 这些对象必须具有简单的生命周期和简单的结构,但是使用它们可以看到性能和抖动方面的显着改善。

使用对象池的另一个有意义的领域是当使用许多重复的对象加载大量数据时。 随着内存使用量的显着减少以及GC必须管理的对象数量的减少,您看到了GC时间的减少和吞吐量的增加。

与使用同步HashMap相比,这些对象池的重量更轻,因此它们仍然可以提供帮助。

以这个StringInterner类为例。 您将所需文本的可循环使用的可变StringBuilder作为字符串传递给它,它将提供匹配的字符串。 传递String效率不高,因为您已经创建了对象。 StringBuilder可以回收。

注意:此结构具有一个有趣的属性,除了最低Java保证所提供的功能外,不需要任何其他线程安全功能(例如volatile或sync)。 也就是说,您可以正确地看到String中的final字段,并且只能读取一致的引用。

public class StringInterner {private final String[] interner;private final int mask;public StringInterner(int capacity) {int n = Maths.nextPower2(capacity, 128);interner = new String[n];mask = n - 1;}private static boolean isEqual(@Nullable CharSequence s, @NotNull CharSequence cs) {if (s == null) return false;if (s.length() != cs.length()) return false;for (int i = 0; i < cs.length(); i++)if (s.charAt(i) != cs.charAt(i))return false;return true;}@NotNullpublic String intern(@NotNull CharSequence cs) {long hash = 0;for (int i = 0; i < cs.length(); i++)hash = 57 * hash + cs.charAt(i);int h = (int) Maths.hash(hash) & mask;String s = interner[h];if (isEqual(s, cs))return s;String s2 = cs.toString();return interner[h] = s2;}
}

堆外内存使用率

使用堆外内存和使用对象池都有助于减少GC暂停,这是它们唯一的相似之处。 对象池适用于短暂的可变对象,创建对象的开销很大,而存在很多重复的永久对象则是不可变的。 中度活泼的可变对象或复杂的对象更有可能由GC处理。 但是,中型到长寿命的可变对象受堆内存解决的许多方式的困扰。

堆外内存提供;

  • 可扩展至大型内存,例如超过1 TB且大于主内存。
  • 名义上对GC暂停时间的影响。
  • 在进程之间共享,减少JVM之间的重复,并使分裂JVM更容易。
  • 持久性,可以更快地重新启动或回复测试中的生产数据。

在设计系统方面,堆外内存的使用为您提供了更多选择。 最重要的改进不是性能,而是确定性。

堆外和测试

高性能计算中的最大挑战之一是再现难以理解的错误,并能够证明已解决了这些错误。 通过以持久方式将所有输入事件和数据存储在堆外,您可以将关键系统变成一系列复杂的状态机。 (或者在简单的情况下,只有一个状态机)以这种方式,您可以在测试和生产之间获得可重现的行为和性能。

许多投资银行都使用此技术对一天中的任何事件可靠地重播系统,并确切地说明了该事件按原样处理的原因。 更重要的是,一旦你有了一个位置,你可以证明你有固定发生在生产,而不是发现问题,并希望这是问题的问题。

确定性行为与确定性行为一起出现。 在测试环境中,您可以按照实际的时间重播事件,并显示期望在生产中获得的延迟分布。 如果硬件不同,则无法重现某些系统抖动,但是从统计角度看,您可能会非常接近。 为了避免花一天时间重放一天的数据,您可以添加一个阈值。 例如,如果事件之间的时间超过10毫秒,则您可能只能等待10毫秒。 这样一来,您可以在不到一个小时的时间内按实际时间重播一天的事件,并查看您的更改是否改善了延迟分配。

通过降低级别,您不会失去“一次编译,随处运行”的某些功能吗?

在某种程度上,这是正确的,但远没有您想像的要多。 当您靠近处理器工作时,您将更加依赖处理器或OS的行为方式。 幸运的是,大多数系统使用AMD / Intel处理器,就其提供的低级别保证而言,甚至ARM处理器也变得更加兼容。 操作系统之间也存在差异,并且这些技术在Linux上比Windows上更有效。 但是,如果您在MacOSX或Windows上进行开发并使用Linux进行生产,则应该没有任何问题。 这就是我们在高频交易中所做的。

我们通过使用堆产生了哪些新问题?

没有什么是免费的,堆外情况就是这样。 堆外的最大问题是您的数据结构变得不太自然。 您或者需要一个可以直接映射到堆外的简单数据结构,或者需要一个序列化和反序列化的复杂数据结构以使其脱离堆。 显然使用序列化有其自身的麻烦和性能损失。 因此,使用序列化要比在堆对象上慢得多。

在金融世界中,最昂贵的数据结构是扁平且简单的,充满了原语,可以很好地在堆外映射,几乎没有开销。 但是,这并不适用于所有应用程序,您可以获得复杂的嵌套数据结构,例如图形,最终还必须在堆上缓存某些对象。

另一个问题是,JVM限制了您可以使用的系统数量。 您不必担心JVM会使系统过载太多。 有了堆外处理,就解除了一些限制,您可以使用比主内存大得多的数据结构,并且开始担心如果要这样做,您将拥有哪种磁盘子系统。 例如,您不希望分页至具有80 IOPS的HDD,而您可能希望拥有80,000 IOPS(每秒输入/输出操作)或更高(即快1000倍)的SSD。

OpenHFT如何提供帮助?

OpenHFT有许多库可以隐藏您实际上正在使用本机内存来存储数据的事实。 这些数据结构是持久的,几乎没有垃圾就可以使用。 这些用于不需少量收集即可全天运行的应用程序中

编年史队列 -持久的事件队列。 支持同一台机器上跨JVM的并发编写器,以及跨机器并发读取器。 微秒级的延迟和每秒数百万条消息的持续吞吐量。

编年史地图 –键值地图的本机或持久存储。 可以在同一台机器上的JVM之间共享,通过UDP或TCP复制和/或通过TCP远程访问。 微秒级延迟和持续的读/写速率,每台机器每秒可进行数百万次操作。

线程亲和性 –将关键线程绑定到隔离的内核或逻辑CPU,以最大程度地减少抖动。 可以将抖动降低1000倍。

使用哪个API?

如果您需要记录每个事件->编年史队列

如果您只需要最新结果作为唯一键->编年史地图

如果您关心20微秒抖动->线程亲和力

结论

堆外内存可能会带来挑战,但也会带来很多好处。 您可以在其中看到最大的收益,并与其他为实现可伸缩性而引入的解决方案进行比较。 与在堆缓存,消息传递解决方案或进程外数据库上使用分区/分片相比,堆外可能更简单,更快。 通过提高速度,您可能会发现不再需要为获得所需性能所需的一些技巧。 例如,堆外解决方案可以支持对OS的同步写入,而不必以异步方式执行写入操作,而存在数据丢失的风险。

但是,最大的收益就是启动时间,从而使您的生产系统重新启动的速度更快。 例如,映射到1 TB数据集中可能需要10毫秒,并且通过重播每个事件以使您每次都获得相同的行为,可以简化测试的可重复性。 这使您可以创建可以依靠的质量体系。

翻译自: https://www.javacodegeeks.com/2014/12/on-heap-vs-off-heap-memory-usage.html

堆上与堆外的内存使用情况相关推荐

  1. 如何分析堆外内存使用情况_堆上与堆外的内存使用情况

    如何分析堆外内存使用情况 总览 最近有人问我在Java中使用堆内存的好处和智慧. 面临相同选择的其他人可能会对这些答案感兴趣. 堆外内存没什么特别的. 线程堆栈,应用程序代码,NIO缓冲区都在堆外. ...

  2. java创建对象时分配内存方式,是堆上分配还是栈上分配?

    创建对象的内存是分配在堆上还是栈上面?大部分童鞋的回答是这样的:"肯定分配在堆内存的嘛,栈内存是属于子线程和基本数据类型专用的内存空间,怎么会分配到栈上面呢?",这个回答嘛,也对, ...

  3. 从RDA5981A/B/C编译后map文件和datasheet分析内存分配情况

    先查看RDA5981的datasheet, 如下图,RDA5981芯片内部有三个RAM区域, I_SRAM,D_SRAM,I_cache, 还可以外挂PSRAM, 内存映射图解释: 在编译目录BUIL ...

  4. java 查看堆外内存占用_如何监控和诊断JVM堆内和堆外内存使用?

    上一讲我介绍了 JVM 内存区域的划分,总结了相关的一些概念,今天我将结合 JVM 参数.工具等方面,进一步分析 JVM 内存结构,包括外部资料相对较少的堆外部分. 今天我要问你的问题是,如何监控和诊 ...

  5. Spark 内存管理堆内和堆外内存规划_大数据培训

    堆内和堆外内存规划 作为一个 JVM 进程,Executor 的内存管理建立在 JVM 的内存管理之上,Spark 对 JVM 的堆内(On-heap)空间进行了更为详细的分配,以充分利用内存.同时, ...

  6. Java 对象都是在堆上分配内存吗?

    为了防止歧义,可以换个说法:Java对象实例和数组元素都是在堆上分配内存的吗? 答:不一定.满足特定条件时,它们可以在(虚拟机)栈上分配内存. JVM内存结构很重要,多多复习 这和我们平时的理解可能有 ...

  7. 对象并不一定都是在堆上分配内存的

    JVM内存分配策略 关于JVM的内存结构及内存分配方式,不是本文的重点,这里只做简单回顾.以下是我们知道的一些常识: 1.根据Java虚拟机规范,Java虚拟机所管理的内存包括方法区.虚拟机栈.本地方 ...

  8. C++ 如何一次在堆上申请4G的内存?如何设计一个类只能在堆或者栈上创建对象?

    1.如何一次在堆上申请4G的内存? 因为32位的环境下虚拟地址空间的大小只有4g,而光内核空间就需要1g,所以不可能申请得到,只有在64位的环境下才可以实现,只需要把执行环境改为64x即可 #incl ...

  9. linux堆上的内存可执行吗,pwn的艺术浅谈(二):linux堆相关

    这是linux pwn系列的第二篇文章,前面一篇文章我们已经介绍了栈的基本结构和栈溢出的利用方式,堆漏洞的成因和利用方法与栈比起来更加复杂,为此,我们这篇文章以shellphish的how2heap为 ...

最新文章

  1. Linux系统文件以及目录介绍
  2. 1043:整数大小比较
  3. 【机器学习】支持向量机面试知识点小结
  4. 完成简单的四则运算(包含小括号)(栈)
  5. pythonnamedtuple定义类型_python - namedtuple和可选关键字参数的默认值
  6. 【PostgreSQL-9.6.3】进程及体系结构
  7. java截取文件名.后的字符串
  8. [RESTful web services读书笔记] 接口设计中维持XML和JSON表述的兼容性
  9. 广东省大学计算机应用基础考试试题,2021年广东省对口考试要做哪些题目?计算机应用基础(Windows7+office2010)周测月考单元卷...
  10. 计算机指令集编程教程,PLC编程语言入门,常用指令集汇总分享
  11. Linux logviewer的功能,基于终端的日志工具logview
  12. 【暑期每日一题】洛谷 P5708 【深基2.习2】三角形面积
  13. 电脑ping手机查看ARP抓包
  14. 《手把手教你构建自己的 Linux 系统》学习笔记(9)
  15. mysql数据库test密码_TestCenter常见问题
  16. pdf转换成ppt的方法
  17. 工业生产管理-数据采集初探
  18. Ubuntu16.04安装steam
  19. 没有 RunInstallerAttribute.Yes 的公共安装程序
  20. (ORACLE)PL/SQL 数据库概念

热门文章

  1. hibernate注解的测试
  2. 新闻发布项目——接口类(commentDao)
  3. 两路语音 两路计算机数据综合,脉冲编码调制解调实验摘要.doc
  4. 如何在Intellij IDEA中集成Gitlab
  5. matlab 定义一个有自变量的方程_常微分方程:(第四章) 高阶微分方程
  6. SpringBoot整合Shiro权限框架
  7. junit5和junit4_JUnit 5符合AssertJ
  8. java github_GitHub Research:超过50%的Java记录语句写错了
  9. 根据变量推断变量类型_Java A的新本地变量类型推断
  10. java 自定义运算符_Java中集合的自定义运算符