有没有办法强制JVM使用交换,无论内存要求有多大?

2022-09-03 06:20:11

这是我的情况:我手头有一个需要大量内存的任务。我没有足够的RAM,无论我尝试了什么(Jrockit与/3gb开关等),我都不能给JVM足够的RAM,操作被终止,异常,告诉我我需要更多的堆空间。

有什么方法可以强制JVM使用操作系统的交换机制,这样它就不会耗尽内存?这是 Windows xp 32 位

这需要很长时间,但我不在乎,我只需要完成这个手术。

我已经没有了选项,我无法控制这里的任何变量。

这是一个必要的编辑,因为我几乎每个人都有相同的回应:)这不是我的代码。有人编写了一个将 xml 文件读取到存储库中的工具。该工具使用 EMF,并一次加载整个模型。我所能做的就是向它提供XML文件。在Windows或Linux等下运行的本机代码的情况下,操作系统使用虚拟内存/交换空间为其提供内存,而应用程序不知道它。我想知道是否有可能对JVM做同样的事情。在Windows 32位下,-Xmx可以达到一定的数量,但这还不够。目前,外出购买新硬件对我来说不是一个选择。所以我想知道是否有可能使JVM像原生进程一样工作。速度慢,但仍然有效。显然这是不可能的,我运气不好。我只需要知道我是否真的没有选择。


答案 1

显然,有一种方法可以解决Java堆的限制。它甚至用于名为BigMemory的商业产品中,该产品基本上允许您通过透明地交换到操作系统交换和/或磁盘(如果需要)来拥有几乎无限的内存。

这个想法是使用直接 s 来存储对象数据。由于直接字节缓冲区的内容存储在本机进程内存(而不是堆)中,因此您可以依靠操作系统交换机制来交换内存。我在这个网站上找到了这个(在页面上搜索“直接字节缓冲区”)。ByteBuffer

以下是你如何实现它(java-pseudo-code'ish):

class NativeMemoryCache{
  private Map<Object, ByteBuffer> data = new HashMap<...>();

  public void put(Object key, Serializable object){
    byte[] bytes = serialize(object);
    //allocate native memory to store our object
    ByteBuffer buf = ByteBuffer.allocateDirect(bytes.length);
    buf.put(bytes);
    buf.flip();
    data.put(key, buf);
  }

  public Object get(Object key){
    ByteBuffer buf = data.get(key).duplicate();
    byte[] bytes = new byte[buf.remaining()];
    buf.get(bytes);
    return deserialize(bytes);
  }

  private byte[] serialize(Object obj){ ... }
  private Object deserialize(byte[] bytes){ ... }
}

希望你明白这个想法。您只需要实现序列化(也可以使用zip压缩对象。如果您只有几个大对象,特别是那些包含字符串等可压缩数据的对象,这将是有效的)。

当然,对象,哈希映射和s将在堆中,但这不应该占用太多内存。NativeMemoryCachedatakey


答案 2

正如其他答案所指出的,您可以使用交换机为JVM提供更多RAM。-Xmx

但是,您可以达到多高是有限制的。在32位系统上,如果JVM支持,这可能是2GiB,可能是3或4 GiB。对于Sun JVM,根据Java -Xmx,系统上的最大内存,32位Windows上的限制为1500MiB。

出于基本的体系结构原因,进程不能(没有特殊技术)获得超过4 GiB的内存(包括它可能使用的任何交换空间),这就是存在值限制的原因。-Xmx

如果您已尝试了最大可能值,但仍收到 OOM 错误,则您唯一的选择是:

  • 修复应用程序,使其需要更少的RAM

  • 将其移至 64 位操作系统,并进一步增加-Xmx

编辑:

请注意,4 GiB 限制是 CPU 体系结构的限制,因此它适用于任何进程,无论是否为 Java。因此,即使是本机分配技巧也无法在这里帮助您。解决这个问题的唯一方法是使用多个进程,但这需要从根本上重写应用程序,这可能与修复应用程序以使用更少的RAM一样复杂。因此,上面的两个选项是您唯一(明智的)选项。

编辑 2:

要解决问题的新部分:

我想知道是否有可能使JVM像本机进程一样工作。

这是一个误解。在这方面,JVM确实像本机进程一样工作:它使用的堆位于JVM从操作系统分配的内存中;对于操作系统来说,这只是分配的内存,如果它感觉像它一样,操作系统将像任何其他内存一样将其交换出来 - 这没有什么特别之处。

堆不能无限增长的原因不是它不能大于物理RAM(它可以,我至少在Linux / x86上尝试过),而是每个操作系统进程(JVM是)不能获得超过4GiB RAM。因此,在32位系统上,您永远不会有超过4GiB的堆。在实践中,它可能要少得多,因为堆内存不能是碎片的(例如,参见Windows XP上的Java最大内存),但4GiB是一个硬的,不可避免的限制。