我们有一个基于 Java 的应用程序(TeamCity,基于 Tomcat,基于 Java 1.7),运行在 Windows(Server 2012 R2)上。Java 运行时的最大堆大小为 6GB,系统有 10GB 的 RAM。它是机器上运行的唯一非操作系统服务。
对于稳定状态,一切都很好。不幸的是,当 Java 偶尔执行完整 GC 时,我们发现 Java 堆的一部分已被调出,需要重新调入,因此 GC 需要几分钟而不是几秒钟。
一般情况下,我们不应该完全禁用 Windows 页面文件,但我想知道这是否是一个反例?(我们真的不希望世界暂停几分钟,让它重新进入 GC 页面!)
考虑到高 IO 工作负载导致 Windows 驱逐部分堆以使用磁盘缓存,有没有更好的方法来确保 JVM 永远不会被分页到磁盘?
谢谢,罗布
答案1
呃,Java。:(
首先,我简短地回答一下:是的,我认为这是不使用页面文件运行的正当理由,当然,如果内存耗尽,您将无法再生成系统崩溃转储,并且您的计算机将崩溃或不稳定到您希望它崩溃的程度。但是,您必须自己权衡利弊。从技术上讲,只要您不耗尽内存,Windows 就可以在没有页面文件的情况下正常运行……例如,除非旧的或设计不良的应用程序愚蠢地假设页面文件的存在。
现在,来谈谈长答案。
首先,为什么要进行分页?操作系统使用页面替换算法,其中每个内存页面都标有“年龄”,这是自上次访问该内存页面以来的计数器。长时间未访问的内存页面最终会被写入磁盘,以便其他可能更重要的数据可以在 RAM 中留出空间。如果某个进程更频繁地主动使用其分配的内存,则这些页面被写入页面文件的机会就会减少。
应用程序开发人员可以调用VirtualLock
Windows API 函数,要求 Windows 将页面锁定到调用进程的工作集中。但是,需要一定的操作系统权限才能成功调用该函数,因为如果您不小心,将页面锁定到 RAM 中可能会对整个系统产生不利影响。但这对您没有帮助,因为这VirtualLock
是您在开发应用程序时会在代码中用到的东西。您不能在其他人的运行进程上调用它。
但是使用 Java,现在您正在实际操作系统及其内存管理器之上运行一个具有自己的内部内存管理器的虚拟机,而这两个虚拟机并不总是能很好地协同工作,正如您所经历的那样。将进程(或更准确地说,将进程分配的所有内存锁定)锁定到物理内存中是操作系统特定的,因此,这不是 Java 可以处理的事情,因为 Java 致力于实现不可知性和跨平台性。
因此,我所知道的让 Java 将其分配锁定到 RAM 中以便它们不会被分页的唯一方法是使用 JNI(Java 本机接口)和mlock()
/ mlockall()
。