【问题标题】:Difference between "on-heap" and "off-heap"“堆上”和“堆外”之间的区别
【发布时间】:2011-08-30 19:14:04
【问题描述】:

Ehcache talks about 堆内和堆外内存。有什么区别?使用哪些 JVM 参数来配置它们?

【问题讨论】:

标签: java memory heap-memory ehcache off-heap


【解决方案1】:

堆上存储是指将出现在 Java 堆中的对象(也受 GC 影响)。另一方面,堆外存储是指由 EHCache 管理的(序列化)对象,但存储在堆外(也不受 GC 影响)。由于堆外存储继续在内存中进行管理,因此它比堆上存储稍慢,但仍比磁盘存储快。

问题中发布的链接中涉及到堆外存储管理和使用的内部细节不是很明显,因此查看用于管理的Terracotta BigMemory 的详细信息是明智的磁盘外存储。 BigMemory(堆外存储)用于避免 GC 在几兆字节或千兆字节大的堆上的开销。 BigMemory 使用 JVM 进程的内存地址空间,通过 direct ByteBuffers 与其他原生 Java 对象不同,不受 GC 影响。

【讨论】:

  • Direct ByteBuffers 提供对非托管内存的访问,但它们本身受制于 GC(与它们指向的数据相反)。这很重要,因为直接的 ByteBuffer(ByteBuffer.allocateDirect 类型,而不是 MMap 类型)将被 GC 收集,当它被收集时,它的 Deallocater 将被触发,从而有效地收集非托管内存。
  • 使用 Unsafe 分配对象看起来比 Onheap/DirectByteBuffers/ByteBuffers 的读写性能要好得多。 ashkrit.blogspot.com/2013/07/…
【解决方案2】:

来自http://code.google.com/p/fast-serialization/wiki/QuickStartHeapOff

什么是堆卸载?

通常,您分配的所有非临时对象都由 java 的垃圾收集器管理。尽管虚拟机在垃圾收集方面做得不错,但在某些时候虚拟机必须执行所谓的“Full GC”。完整 GC 涉及扫描完整分配的堆,这意味着 GC 暂停/减速与应用程序堆大小成正比。所以不要相信任何告诉你“内存很便宜”的人。在 java 中,内存消耗会损害性能。此外,如果堆大小 > 1 Gb,您可能会出现明显的暂停。如果您有任何近乎实时的事情正在进行,这可能会很糟糕,在集群或网格中,java 进程可能会无响应并从集群中删除。

然而,当今的服务器应用程序(通常建立在臃肿的框架之上 ;-))很容易需要远远超过 4Gb 的堆。

解决这些内存需求的一个方法是将对象的一部分“卸载”到非 Java 堆(直接从操作系统分配)。幸运的是,java.nio 提供了类来直接分配/读取和写入“非托管”内存块(甚至是内存映射文件)。

因此可以分配大量“非托管”内存并使用它来保存对象。为了将任意对象保存到非托管内存中,最可行的解决方案是使用序列化。这意味着应用程序将对象序列化到堆外内存中,稍后可以使用反序列化读取对象。

Java VM 管理的堆大小可以保持较小,因此 GC 暂停以毫秒为单位,每个人都很高兴,工作完成。

很明显,这种堆外缓冲区的性能主要取决于序列化实现的性能。好消息:由于某种原因,FST 序列化非常快:-)。

示例使用场景:

  • 服务器应用程序中的会话缓存。使用内存映射文件来存储千兆字节的(非活动)用户会话。一旦用户登录到您的应用程序,您就可以快速访问与用户相关的数据,而无需处理数据库。
  • 缓存计算结果(查询、html 页面等)(仅适用于计算比反序列化结果对象的 c 慢的情况)。
  • 使用内存映射文件实现非常简单快速的持久性

编辑:对于某些情况,可能会选择更复杂的垃圾收集算法,例如 ConcurrentMarkAndSweep 或 G1 来支持更大的堆(但这也有超过 16GB 堆的限制)。还有一个具有改进的“无暂停”GC (Azul) 的商业 JVM。

【讨论】:

  • “分配大量'非托管'内存并使用它来保存对象” - 您无法在堆外保存对象。您可以存储原语,您可以将它们包装在您喜欢的任何库中,但这些不是对象。你放在堆外的数据没有对象头,你不能同步它,你不能用其他对象中的引用字段来引用它。
【解决方案3】:

堆是内存中动态分配的对象所在的位置。如果您使用了new,那么它就在堆上。这与堆栈空间相反,堆栈空间是函数堆栈所在的位置。如果您有一个局部变量,那么该引用就在堆栈上。 Java 的堆是垃圾回收的对象,对象可以直接使用。

EHCache 的堆外存储将您的常规对象从堆中取出,将其序列化,并将其作为字节存储在 EHCache 管理的内存块中。这就像将它存储到磁盘但它仍然在 RAM 中。对象在这种状态下不能直接使用,它们必须首先反序列化。也不受垃圾收集的影响。

【讨论】:

  • 不就是还在堆里,而是作为序列化的形式吗?
  • 如何提高效率?
  • 有很多方法。由于对象不再位于主 Java 堆上,因此它们不会浪费垃圾收集器的时间,它们不会对 JVM 堆进行分段,它们会为其他更常用的对象释放空间。此外,由于它们是序列化的,并且在不久的将来可能不需要它们,因此可以根据需要对其进行压缩、移动,甚至换出到磁盘。
  • 在 Hotspot 中,GC 暂停时间直接取决于堆大小。 BigMemory 通过利用 RAM 而不是堆来提供这种权衡,以将 GC 暂停保持在最低限度并避免磁盘访问的 IO 成本。
  • @Adam 感谢您的回答,当您说“将其存储为字节”时,这到底是什么意思?我实际上在stackoverflow.com/questions/63320051/… 中提出了这个问题,但没有得到答案,你有什么提示吗?谢谢。
【解决方案4】:

简而言之

pic credits


详细图片

pic credits

【讨论】:

  • 关闭堆内存是否由 -xmx 控制?蓝色的是 Old Gen 还是堆外的?
  • 没有。它是堆中未使用的空间,当堆中创建许多对象时会被填充。
【解决方案5】:

不是 100%;但是,听起来堆是一个对象或一组已分配空间(在 RAM 上),它内置于 Java 本身或更可能来自 ehcache 本身的代码功能中,并且堆外 RAM 有自己的系统好吧;然而,这听起来慢了一个数量级,因为它没有那么有条理,这意味着它可能不使用堆(意味着一组长的 ram 空间),而是使用不同的地址空间,这可能会使其效率略低。

当然,下一层是硬盘空间本身。

我不使用 ehcache,所以你可能不想相信我,但这是我从他们的文档中收集到的。

【讨论】:

    【解决方案6】:

    JVM 对堆外内存一无所知。 Ehcache 实现了磁盘缓存和内存缓存。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-03-18
      • 2011-03-29
      • 2016-05-28
      • 2013-09-30
      • 2015-12-22
      • 2020-08-24
      • 2018-07-04
      相关资源
      最近更新 更多