gpt4 book ai didi

java - 使用普通对象指针(OOP)在32位模式下但在64GB RAM上运行Java应用程序

转载 作者:行者123 更新时间:2023-11-30 10:01:39 25 4
gpt4 key购买 nike

我有一台具有64Gb RAM的计算机,并且我有一些小的Java应用程序需要多达4Gb RAM。

当Java应用程序在32位平台上运行时,它只能使用4GB,但它使用直接对象地址。当Java应用程序在更大的RAM上但小于32Gb的内存上运行时,它将使用带有8字节填充的压缩oop(COOPS),这会增加一些开销,但仍然可以。
但是,当在64Gb堆上运行Java应用程序时,它将使用完整的64位地址,这将消耗两倍的内存来存储地址。

所以我的问题是:我可以在32位模式下运行Java应用程序,以便它不能使用超过4Gb的内存,但可以更有效地使用内存。我可以运行多个这样的应用程序来使用整个64Gb地址空间吗?
也许我可以以某种方式使用Docker来限制Java进程的内存,但是我不确定该进程是否将以简单的32位模式运行,因为无论如何它都需要访问一些高于32Gb的内存地址。

也许Docker可以将64Gb地址空间虚拟化为简单的32位地址。

当我看到这个解决方案时:我们启动了一个Java应用程序,并将其一部分堆从32GB扩展到36Gb。指定的地址空间为4Gb,因此这里仅32bit指针(4字节整数)就足够了,因此在所有字段内部将仅使用常规32指针。但是32位指针可用于0到4Gb的地址,而应用程序的堆从32Gb开始。因此,内部JVM通过添加32Gb只是将32位指针转换为64位。
这类似于COOPS,但我们总是只在应用程序堆开始的地方添加地址偏移量,而不是8字节填充。
但是,在处理可以使用整个64Gb地址空间的本机代码时,这又带来了另一个问题。

UPD
根据JVM Anatomy Quark #23: Compressed References,应用程序将使用基于非零模式的模式,该模式最多可使用32Gb并进行3位移位。可以,因为指针仍然是32位,但实际上,如果应用程序的MaxRAM是4Gb,则可以跳过3位移位。
但这似乎不需要优化,因为无论如何它将使用0xc(%r12,%r11,8),%eax形式的同一条指令,而不是8,它将是0。

为了进行实验,我们可以将java -version本身与具有VM选项的其他任何Java应用程序一起运行以设置其内存限制。
例如,我们可以设置-XX:MaxRAM=2147483648-Xmx2G,即将最大内存限制为2 Gibibyte。

要查看真正的COOPS模式,我们可以添加-Xlog:gc+heap+coops=info选项:

$ java -XX:MaxRAM=2147483648 -XX:MaxRAMPercentage=100 -Xlog:gc+heap+coops=info -version
[0.011s][info][gc,heap,coops] Heap address: 0x0000000080000000, size: 2048 MB, Compressed Oops mode: 32-bit
openjdk version "12.0.2" 2019-07-16
OpenJDK Runtime Environment Zulu12.3+11-CA (build 12.0.2+3)
OpenJDK 64-Bit Server VM Zulu12.3+11-CA (build 12.0.2+3, mixed mode, sharing)

在这里我们可以看到COOPS已启用并且使用32位模式,即所有指针的长度均为4字节。同时记下 Heap address: 0x0000000080000000,其中十六进制地址 0000000080000000对应于2147483648,即2Gb。此值在源中称为“堆基数”或 HeapBaseMinAddress最终标志或 NarrowOopHeapBaseMin,默认值为2Gb,但可以通过标志进行更改。

这意味着虽然32位足以寻址4Gb,但是第一个2Gb将由 HeapBaseMinAddress保留,因此对于32位模式,只剩下2Gb。
如果您尝试将 -XX:MaxRAM=2147483649设置为比2Gb(2147483648)高一个字节,则将禁用32位模式,而将使用“基于零,Oop移位量:3”模式:
$ java -XX:MaxRAM=2147483649 -XX:MaxRAMPercentage=100 -Xlog:gc+heap+coops=info -version
[0.010s][info][gc,heap,coops] Heap address: 0x000000077fe00000, size: 2050 MB, Compressed Oops mode: Zero based, Oop shift amount: 3

我在这里不清楚的是堆地址 0x000000077fe00000是32210157568,即29,998046875 Gb,而在我进行测试的当前计算机上,我只有16Gb或RAM。

同时,如果我们指定 HeapBaseMinAddress标志,它将用作堆地址:
$ java -XX:MaxRAM=2147483648 -XX:MaxRAMPercentage=100 -Xlog:gc+heap+coops=info -XX:HeapBaseMinAddress=8g -version
[0.008s][info][gc,heap,coops] Heap address: 0x0000000200000000, size: 2048 MB, Compressed Oops mode: Zero based, Oop shift amount: 3

此处 0x0000000200000000对应于8589934592,即8Gb。

我还写了一个简单的应用程序,它作为守护程序运行,并运行了它的两个实例,并且都以32位模式启动,并且都共享相同的堆地址:
$ java -XX:MaxRAM=2147483648 -XX:MaxRAMPercentage=100  -XX:+UnlockDiagnosticVMOptions -Xlog:gc+heap+coops=info  DaemonThreadTest
[0.011s][info][gc,heap,coops] Heap address: 0x0000000080000000, size: 2048 MB, Compressed Oops mode: 32-bit

我不知道这是怎么回事,因为我认为仅当应用程序可以占用整个地址空间的第3和第4吉字节时,COOPS 32位模式才有可能,但是如果第一个守护程序已经使用了它,那么第二个应该使用其他可用内存前4Gb空间。
我在这里很困惑。

无论如何,我将继续测试并在具有64Gb RAM的计算机上的下一个学习中学习。
谢谢您的回复。

可以从 universe.hpp中看到所有可用的模式
  // For UseCompressedOops
// Narrow Oop encoding mode:
// 0 - Use 32-bits oops without encoding when
// NarrowOopHeapBaseMin + heap_size < 4Gb
// 1 - Use zero based compressed oops with encoding when
// NarrowOopHeapBaseMin + heap_size < 32Gb
// 2 - Use compressed oops with disjoint heap base if
// base is 32G-aligned and base > 0. This allows certain
// optimizations in encoding/decoding.
// Disjoint: Bits used in base are disjoint from bits used
// for oops ==> oop = (cOop << 3) | base. One can disjoint
// the bits of an oop into base and compressed oop.
// 3 - Use compressed oops with heap base + encoding.
enum NARROW_OOP_MODE {
UnscaledNarrowOop = 0,
ZeroBasedNarrowOop = 1,
DisjointBaseNarrowOop = 2,
HeapBasedNarrowOop = 3,
AnyNarrowOopMode = 4
};

Non-zero disjoint mode

最佳答案

如果要在64位操作系统上以32位模式运行Java应用程序,则需要32位JVM。

32位JVM将在64位OS上运行,并且可能会比32位OS占用更多的4GB地址空间。但是,出于架构原因,您可能无法使用完整的地址空间。

So I can't run 16 apps in 32-bit mode on 64Gb RAM, I can run only one such app. Or I can?



是的你可以!

您可以运行尽可能多的物理内存。 JVM是一个进程。它不与任何其他进程共享虚拟地址空间。

对于旧版本的Oracle Java,您可以获得32位JVM。在Java 9中,Oracle不再提供32位JVM的发行版,但是32位平台的代码仍在代码库中。

第三方Java供应商可提供某些平台的32位JVM。例如Azul具有适用于Windows 2008r2或更高版本的x86-32发行版。您只需要进行一些搜索。

根据您的更新,您似乎打算让多个4GB JVM在单个64GB虚拟地址空间中运行。我认为您这样做有充分的技术理由,尽管我无法想象这是什么。 (而且您还没有分享这个信息,所以我不会低估这个X-Y问题的可能性。)

对于该要求,可以采用基于压缩OOP的方法。但是,我认为这不是那么简单。
  • 如果处理器处于32位模式,则此方法不起作用。因此,任何本机库都必须是64位,并且必须实现以与这种不寻常的工作方式配合使用。
  • 如果处理器处于64位模式,则每个JVM的本机代码部分将能够寻址其他JVM的内存。我认为没有防止这种情况的实用方法。因此,通常会导致一个JVM崩溃的错误现在可能崩溃/破坏所有内容。
  • OOPs机制仅适用于对Java对象的引用。 JVM还出于各种目的提供并使用非托管本机堆。确保JVM的指针停留在其分配的4GB虚拟地址空间内将是具有挑战性的。即使确保JVM不使用超出允许范围的内存,也将具有挑战性。


  • 最后,令我惊讶的是,您可能正在尝试解决在Docker容器中运行时Java的旧版本所遇到的问题。如果是这样,您应该检查一下: "Docker Support in Java 10"

    现在(Java 10+),您应该能够将64GB的RAM分配到16个Docker容器中,每个容器都运行具有(大约)4GB堆+压缩OOP的JVM。

    关于java - 使用普通对象指针(OOP)在32位模式下但在64GB RAM上运行Java应用程序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57328061/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com