32位JVM提交>3G虚拟内存
我们有一个在 64 位 RHEL5 下运行的 32 位 JVM,它有足够的内存(32G)。由于不同的原因,这个过程需要相当大的托管堆和永久生成空间——目前,它使用以下虚拟机参数运行:
-Xmx2200M -XX:MaxPermSize=128M -XX:+CMSClassUnloadingEnabled
我最近开始看到 JVM 崩溃,因为它似乎耗尽了本机内存(它无法创建本机线程,或者无法分配本机内存等)。这些崩溃与托管堆的状态没有(直接)相关,因为当这些崩溃发生时,托管堆已满约 50-70%。 我知道为托管进程保留的内存接近 2.5 G,留给 JVM 本身的内存不超过 0.5G,但是 - 我不明白为什么 0.5 对于 JVM 来说还不够,即使不断进行 GC - 真正的问题是:当我使用 jconsole 连接到进程时,它会说(当前)
Committed virtual memory:
3,211,180 kbytes
哪个大于 3G。我可以想象,出于某种原因,JVM 认为它已经 内存为 3,211,180 KB (3.06G),但当尝试超过 3G 时,内存分配失败。
任何想法 a) 为什么会发生这种情况 b) 如何避免这种情况
谢谢。 伴侣
we have a 32 bit JVM running under 64 bit RHEL5 on a box which has plenty of memory (32G). For different reasons, this process requires a pretty large managed heap and permgen space -- currently, it runs with the following VM arguments:
-Xmx2200M -XX:MaxPermSize=128M -XX:+CMSClassUnloadingEnabled
I have started seeing JVM crashes recently because it - seemingly - ran out of native memory (it could not create native threads, or failed to allocate native memory, etc.). These crashes were not (directly) related to the state of the managed heap, as when those crashes happened the managed heap was ~50-70% full.
I know that the memory reserved for the managed process is close to 2.5 G which leaves not more than 0.5G for the JVM itself, BUT
- I don't understand why 0.5 isn't enough for the JVM, even if there is constant GCing going on
- the real question is this: when I connect to the process using jconsole, then it says that (currently)
Committed virtual memory:
3,211,180 kbytes
Which is more than 3G. I can imagine that for some reason JVM thinks that it has
3,211,180 kbytes (3.06G) of memory is but when it tries to go over 3G the memory allocation fails.
Any ideas on
a) why does this happen
b) how is it possible to avoid this
Thanks.
Mate
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
典型 VM 中存在大量开销,这些开销未计入 VM 核算中,因为它本质上是由进程的本机元素窃取的 - 例如,用于执行系统库本机级代码的 .so 文件中的映射不包含在内。计入基本 VM 核算中。您的典型共享库映射到内存的顶部 GB,因此如果您尝试将内存分配到该区域,您将被拒绝,因为它会溢出共享库的内存区域 - 大多数操作系统上的内存分配是由当您要求更多内存时会出现的简单栏。当您请求内存并且该栏与其他用途发生冲突时,它就会失败。接下来的大部分细节都是关于这个的。
您需要避免在 32 位进程中需要如此多的内存。这是根本性的挑战。获得一个 64 位虚拟机是微不足道的,它允许您使用比其他方式可访问的更多的内存 - 在这种情况下它只是简单可用。
如果您使用的是 32 位进程,则很可能会遇到 32 位进程的有效地址空间限制。对于 Windows,最大约为 3GB - 超过此值的任何内容都会保留给 I/O 空间和内核。您可以移动它,但它可能会破坏为 32 位操作系统设计的应用程序/驱动程序。
对于 Linux,每个进程最终会获得约 3GB 的可用可寻址 RAM,其余的则由内核等内容使用并映射到共享库中。该限制被称为“地址空间限制”,我认为它可以调整。
如何避免呢?好吧,在大多数情况下,你不能,这是 32 位地址空间的物理限制,以及将内核/IO 与 32 位操作系统的进程位于同一地址空间的需要。
对于 64 位操作系统,您可以使用(大部分)所有 64 位地址空间,这远远超出了您需要使用的范围。
There is a lot of overhead in a typical VM that is not counted in the VM accounting because it is essentially stolen by the native elements of the process - e.g. mapping in .so files that are used for performing native level code for system libraries are not counted in the base VM accounting. your typical shared library is mapped in at the top GB of memory, so if you try to allocate memory into this region you will be denied, because it would overrun with the shared libraries' memory region - memory allocation on most OS's is performed by a simple bar that is raised when you ask for more memory. When you ask for memory and the bar conflicts with other uses, then it simply fails. Most of the details that follow are about this.
You need to avoid needing so much memory in a 32bit process. This is the fundamental challenge. It is trivial to get a 64bit VM that will allow you to make use of so much more memory than would be otherwise accessible - it is just simply usable in this situation.
If you are using a 32bit process, there is a high probability that you are encountering the effective address space limit of the 32bit process. For windows, this is a maximum of about 3GB - anything above this is reserved for I/O space and the kernel. You can move this, but it has a tendency to break applications/drivers that are designed for the 32bit OS.
For Linux, you end up with ~3GB of usable addressable RAM per process, the rest is used up by things like the kernel and mapped in shared libraries. The limit is referred to as the 'address space limit', and I presume it can be tuned.
How to avoid it? Well, for the most part, you can't, it's a physical limitation of the 32bit address space and the needs of having the kernel/IO in the same address space as the process for a 32bit OS.
With 64 bit OS's you have (most of) all of the 64 bit address space to play around with, which is extensively more than you need to use.
当您启动 JVM 时,它会立即为其分配最大大小。使用了多少内存并不重要。您的应用程序可以寻址大约 3 GB,其中大约 2.3 GB 已分配给堆和永久代。其余部分可用于共享库(通常约为 200 MB)和线程堆栈。
当解决方案相对简单时(使用 64 位 JVM),担心为什么不能使用完整的 3 GB 地址并不是很有用,我假设您没有任何仅在 32 位中可用的共享库-少量。但是,如果您确实有其他共享库,它们可能会轻松使用数百 MB。
When you start a JVM it allocates it maximum size immediately. How much of that memory is used doesn't really matter. Your application can address about 3 GB of which about 2.3 GB you have allocated to heap and perm gen. The rest is available for shared libraries (typically around 200 MB) and thread stacks.
Worrying about why you can't use the full 3 GB of address isn't very useful when the solution is relatively trivial (use a 64-bit JVM) I am assuming you don't have any shared libraries which are only available in 32-bit. However if you do have additional shared libraries they can easily be using 100s of MB.