“堆上”和“堆外”之间的区别


Answers:


169

堆上存储是指将在Java堆中存在的对象(也受GC约束)。另一方面,堆外存储引用(序列化的)对象,这些对象由EHCache管理,但是存储在堆外部(并且不受GC限制)。随着堆外存储继续在内存中进行管理,它比堆上存储稍慢,但仍比磁盘存储快。

问题中发布的链接中并没有非常清楚地涉及到管理和使用堆外存储的内部细节,因此明智的方法是查看用于管理磁盘外的Terracotta BigMemory的细节。商店。BigMemory(堆外存储)用于避免GC在数兆字节或千兆字节的堆上的开销。BigMemory通过直接的ByteBuffer使用JVM进程的内存地址空间,与其他本机Java对象不同,这些直接ByteBuffer不受GC影响。


18
+1提的进一步探索直接的ByteBuffers;)
最大

3
直接字节缓冲区可提供对非托管内存的访问,但它们本身受制于GC(与其指向的数据相反)。这很重要,因为GC将收集直接的ByteBuffer(ByteBuffer.allocateDirect类型,而不是MMap类型),并且在收集到它的Deallocater时将对其进行触发,从而也有效地收集了非托管内存。
Nitsan Wakart

与Unheap / DirectByteBuffers / ByteBuffers相比,使用Unsafe分配对象似乎具有更好的读写性能。ashkrit.blogspot.com/2013/07/...
乔ç

98

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

什么是堆卸载?

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

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

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

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

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

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

示例使用场景:

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

编辑:在某些情况下,可能会选择更复杂的垃圾收集算法,例如ConcurrentMarkAndSweep或G1以支持更大的堆(但这在16GB堆之外也有其限制)。还有一个具有改进的“无中断” GC(Azul)的商业JVM。


4
“分配大量的'非托管'内存,并使用它来将对象保存在那里”-您不能将对象分散保存。您可以存储基元,也可以将它们包装在所需的任何库中,但这些不是对象。您放下的数据没有对象标头,您无法对其进行同步,也无法将其与其他对象中的引用字段一起引用。
Nitsan Wakart

41

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

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


难道它不只是仍在堆中而是以序列化形式存在吗?
Pacerier

1
如何提高效率?
Pacerier

2
有很多方法。由于对象不再位于主Java堆上,因此它们不会浪费垃圾收集器的时间,它们不会使JVM的堆碎片化,并且可以为其他更多使用的对象释放空间。此外,由于它们已经序列化并且在不久的将来可能不需要,因此可以对其进行压缩,按需移动,甚至可以分页到磁盘。
亚当

1
在Hotspot中,GC暂停时间直接取决于堆大小。BigMemory通过利用RAM而不是堆来提供这种权衡,以使GC暂停到最小,并避免磁盘访问的IO成本。
Chander Shivdasani


1

JVM对堆外内存一无所知。Ehcache实现了磁盘上的缓存以及内存中的缓存。


1

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

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

我不使用ehcache,所以您可能不想信任我,但是那是我从他们的文档中收集的。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.