Windows上的JVM – 分页和完整的GC

我们在Windows(Server 2012 R2)上运行基于Java的应用程序(TeamCity,在Tomcat上,在Java 1.7上)。 Java以最大6GB的堆大小运行,并且系统具有10GB的RAM。 它是机器上唯一运行的非操作系统服务。

对于稳定状态,一切都很好。 不幸的是,当Java偶尔做一个完整的GC时,我们发现Java堆的一部分已经被分页,需要重新分页,因此GC需要几分钟时间,而不是less于几秒钟。

一般的看法是不能完全禁用Windows页面文件,但是我想知道这是否是一个反例呢? (我们真的不希望这个世界暂停几分钟,而把它重新回到GC中!)

有没有更好的方法来确保JVM永远不会被分页到磁盘,因为高IO工作负载导致Windows驱逐一些有利于磁盘caching的堆?

谢谢,罗布

呃,Java。 🙁

首先我会给你简短的回答:是的,我认为这可能是一个没有页面文件运行的正当理由,当然知道你将不能再生成系统崩溃转储,并且你的机器会崩溃或如果内存不足,就会动摇到希望崩溃的地步。 但是,你只需要权衡自己的利弊。 从技术上讲,只要不将内存运行出来,Windows就可以正常运行,而不会出现内存不足的情况。例如,禁止旧式或devise不当的应用程序愚蠢地假定存在页面文件。

现在,答案很长。

首先,为什么分页发生? 操作系统使用页面replacealgorithm,每个内存页面都标有“年龄”,这是自上次访问内存页面以来的计数器。 长时间未被访问的内存页面最终被写入到磁盘中,以便其他可能更重要的数据可以在RAM中被给予空间。 如果一个进程更频繁地使用其分配的内存,那么这些页面被写出到页面文件的机会就越小。

应用程序开发人员可以调用VirtualLock Windows API函数,让Windows将页面locking到调用进程的工作集中。 它需要一定的操作系统权限才能成功地调用该函数,但是,如果你打得不好,就可以将页面locking到RAM中,否则可能会对整个系统产生不利影响。 但是这并不能帮助你,因为VirtualLock是你在开发应用程序时在代码中使用的东西。 这不是你可以调用别人的正在运行的过程。

但是对于Java来说,现在你正在运行一个虚拟机器,在实际的操作系统和内存pipe理器之上有自己的内部内存pipe理器,而这两者并不总是一起工作,就像你正在经历的那样。 locking一个进程(或者更准确地说,将一个进程分配的所有内存locking在物理内存中)是特定于操作系统的,因此Java并不是一个可以处理的东西,因为Java力求成为不可知论者,平台。

所以,我知道让Java将其分配locking到RAM中以便它们不被分页的唯一方法是使用JNI(Java Native Interface)和mlock() / mlockall()