Java对Runtime.memory()和Windows的物理内存使用历史图表的困惑

12 浏览
0 Comments

Java对Runtime.memory()和Windows的物理内存使用历史图表的困惑

我为自己在Windows平台上(Win7 x64)创建了一个Java渲染应用程序,在渲染过程中它使用了大量的内存(在一些大型项目中可能达到几个G...我的PC上有8GB的RAM和6核心CPU),所以我不得不在启动我的Java应用程序的.bat文件中为它分配4GB甚至8GB的RAM,如下所示:\n

@ECHO OFF
java -Xmx8G -server -jar myapp %*
@if %errorlevel% neq 0 pause

\n当渲染过程结束时,它应该从物理内存中卸载所有不再需要的内容,并根据我的应用程序计算((r.totalMemory() - r.freeMemory()) / (1024.0 * 1024) + \"MB\"),它确实这样做了(在渲染线程结束后调用System.gc():如果没有这个操作,它报告的内存使用量没有下降-大约为4GB,如果有这个操作,它报告使用了约80MB的内存)。\n我的应用程序的RAM使用情况基本如下:\n

    \n

  • 没有加载任何内容时约为30MB,只有主要应用程序
  • \n

  • 加载了一些项目后约为80MB(无论大小如何,只会有几MB的区别,因为创建的GUI元素比简单的项目多)
  • \n

  • 渲染活动时高达4GB
  • \n

  • 渲染结束后回落到约80MB
  • \n

\n我甚至使用了来自JAVA bin文件夹的内存分析应用程序Java VisualVM,以确保一切都正常,没有内存泄漏...根据它的堆转储,情况确实如此:\n

    \n

  • 加载项目后渲染结束时使用的内存约为80MB
  • \n

  • 没有找到打开的进程
  • \n

  • 最大的文件大约为每个25MB的3个文件
  • \n

\n但是,当我查看Windows任务管理器中的内存使用图表时,我真的很困惑,几乎没有任何内存使用量下降(可能只有几MB):如果不是几百MB甚至几千MB(使用了大型项目),它应该下降-只有在我关闭我的应用程序之后才卸载。我真的等了5分钟,10分钟,15分钟...在那个图表窗口中仍然没有任何下降!\n所以我想知道:是否有一些特定的开关我需要在我的.bat中添加,以告诉JVM“请卸载我应用程序在渲染过程中使用的所有未使用的内存”或者...?

0
0 Comments

JDK v1.8.0_181版本的Java中,出现了一个关于Runtime.memory()与Windows的Physical Memory Usage History graph之间的混淆问题。解决这个问题的方法是在.bat文件中添加-XX:+UseG1GC参数。现在,java.bat的代码如下所示,它可以如我所期望的那样,将未使用的内存释放回系统:

 OFF
java -XX:+UseG1GC -Xmx8G -server -jar myapp %*
 %errorlevel% neq 0 pause

有趣的是,我知道这个参数,但在某个地方读到它是默认的GC收集器,所以不需要手动设置它,所以我完全忽略了这个选项。事实上,我没有检查到它是从特定版本的JDK开始生效的那部分。好吧...

非常感谢给出解决方案建议的人!

0
0 Comments

Java内存管理与Windows的物理内存使用历史图之间的混淆是由于以下原因导致的:Java虚拟机(JVM)的Runtime.totalMemory()返回的是JVM从操作系统请求的内存数量。在这段内存中,“已使用”和“未使用”只与Java应用程序相关,而与操作系统无关。因此,Runtime.freeMemory()告诉您在总内存中有多少内存可供新分配使用,而这两个值之间的差异告诉您有多少内存被Java对象占用,这些对象可能仍在使用中或尚未被回收。

从操作系统的角度来看,totalMemory()报告的值是应用程序请求的内存量,在简单的系统中被认为是“已使用”。当JVM将内存还给操作系统时(这发生的频率比垃圾回收要低得多,有时甚至根本不回收),totalMemory()报告的数字会减少。

不幸的是,像Windows这样的操作系统并不那么简单。它有自己的“已使用内存”概念,这与请求的内存和Java的“已使用”概念都不同。内存被组织成页面,只有当应用程序实际写入页面时,Windows才认为页面正在使用中,因此它包含需要保留的内容。

此外,前面讨论的一切都是“虚拟内存”,它映射到物理内存的方式是另一个复杂的问题。Windows会尽力保持虚拟内存页面与物理内存页面的映射,但其他进程对内存的需求可能会导致它减少进程正在使用的物理内存。这一事实被一些承诺“清理内存”的虚假工具利用,这些工具只需请求大量内存,使Windows重新分配物理内存,然后释放该内存,从而使使用的物理内存数量看起来令人印象深刻地降低,因为它已从正在运行的进程中取走,但当它们继续进行时,它们当然会再次获得它,因此该操作只会降低性能。

要点是,如果不了解运行应用程序所需的实际情况,任务管理器中的图表可能毫无意义。在“分配(虚拟)内存”、“实际使用(虚拟)内存”和“当前使用(物理)内存”之间的关系无法用单个简单的图表来表示。

此外,虚拟机内的垃圾回收的主要任务是使虚拟机内存可供同一虚拟机中的新分配使用,而不是将内存还给操作系统。根据配置,后者有时可能发生,但通常不会发生。

因此,如果我理解正确,JVM不会释放那些未使用的先前分配的内存给系统,它基本上告诉了我分配给我的Java应用程序的8GB内存中有多少内存被释放/使用,因此Windows不会将其识别为可用内存,因为它认为我分配的那8GB始终是正在使用的,直到我关闭我的应用程序-这个假设是否正确?

释放是可能的,在Java 12中有一个单独的JEP,据我回忆,它会在主要垃圾回收时释放内存,有一些设置可以控制这一点。

我说过,这取决于配置。首先,选择了哪种垃圾回收算法,然后配置了哪些返回内存给系统的阈值。以及运行时行为。例如,在JDK-6498735中提到:“这两个并行收集器在将堆缩小到“可接受”大小之前需要进行多次GC。这是设计要求。”参见Does the JVM give back free memory to the OS when no longer needed?

提到的JEP是JEP-346。它还指出G1确实返回内存,但不是立即返回,而是请求改进提前(“promptly”)返回。我试图强调的另一点是,对于您的系统来说,这些返回的内存并不重要,因为如果其他应用程序需要,Windows自己的内存管理会减少应用程序的物理内存。而当不需要时,该图表只是一个毫无意义的数字。

你能详细说明一些如何做到这一点吗?也许给我一个实际的例子,我可以尝试一下?你看,我还是Java新手,所以对你们来说看起来很简单的东西对我的实际知识来说是相当超过我的,没有一些例子的解释...

谢谢提供的链接,我会查看的。

那么,在您提供的链接中标记为正确的答案中提到使用-XX:MaxHeapFreeRatio-XX:MinHeapFreeRatio应该对我有用-我认为这是批处理文件的另一个开关,但我不知道该如何使用:我应该像这样直接写吗,还是这些值应该被替换为其他值?

它们的形式如下,例如:-XX:MinHeapFreeRatio=40 -XX:MaxHeapFreeRatio=70。这些值是总内存的百分比。它们告诉JVM,空闲内存应该在此范围内,否则它将扩展或缩小堆。

那么,我可以在我的批处理文件中这样写吗:java -XX:MinHeapFreeRatio=2 -XX:MaxHeapFreeRatio=70 -Xmx8G -server -jar myapp %*?这些开关放置的位置是否重要,如在-Xmx8G之前或之后?我是否正确理解了-XX:MinHeapFreeRatio设置在System.gc()之后需要保留的最小内存?我需要回退到我设置的100MB,我将其设置为-XX:MinHeapFreeRatio=2(Math.ceil(100MB /(8000MB/100)))?这将卸载先前由渲染器分配给这100MB的内存(在渲染结束后加载项目的应用程序)吗?

唯一需要注意的是它们必须在-jar myapp之前。对于所有这些JVM选项,顺序不重要。使用-XX:MinHeapFreeRatio=2,您使JVM在扩展堆方面更加勉强,但您必须减少XX:MaxHeapFreeRatio,使其在收缩方面更加积极。请注意,我刚刚在JDK 11中进行了测试,即使在您描述的数量级的空闲内存时,它也已经平滑地返回了内存,而不需要进行任何设置。

通过默认值,您指的是-XX:MinHeapFreeRatio=40 -XX:MaxHeapFreeRatio=70吗?因为如果是这样,我确实使用了完全相同的设置,就像java -XX:MinHeapFreeRatio=40 -XX:MaxHeapFreeRatio=70 -Xmx8G -server -jar myapp %*一样,但是物理内存图表仍然没有任何变化...它仍然显示内存仍然是满的,直到我关闭应用程序。

我是说,我没有为JDK 11 JVM指定任何内容。如我所说,可能有很多环境因素。您使用的确切Java版本是哪个?此外,我的Windows 10任务管理器可能显示与Windows 7不同的统计数据。

没关系,使用-XX:+UseG1GC开关已经解决了问题,我之前甚至没有尝试过,因为我在某个地方读到过不需要使用它,因为它已经成为JVM的默认垃圾收集器,但是我没有检查到它是从特定版本的JDK开始的那一部分:-)不管怎样,非常感谢你的帮助!

是的,这就是我提到Java版本的原因。使用JDK 11,G1GC是默认的...

0