Java 对 Runtime.memory() 与 Windows 的物理内存使用历史图的混淆

标签 java batch-file memory garbage-collection jvm

我为自己创建了一个适用于 Windows (Win7 x64) 的 Java 渲染应用程序,它在渲染过程中确实使用了大量内存(在一些大型项目中实际上是演出……我的 PC 中有 8GB RAM 和 6 核 CPU ) 所以我必须在我的 .bat 文件中为它分配 4GB 甚至 8GB 的​​ RAM 来启动我的 java 应用程序,如下所示:

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

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

我的应用程序对 RAM 的使用基本上是这样的:

  • 大约 30MB 还没有加载任何东西,只有主应用
  • 当我加载一些项目时大约 80MB(无论多大,它都会有几 MB 的差异,因为与一些简单的相比,会创建更多的 GUI 元素)
  • 渲染处于 Activity 状态时高达 4GB
  • 渲染结束时回落到大约 80MB

我什至使用了来自名为 Java VisualVM 的 JAVA bin 文件夹中的内存分析应用程序,以确保一切正常工作,没有任何内存泄漏......根据它的堆转储,它确实是这样工作的:

  • 渲染结束后加载项目使用的内存约为80MB
  • 未找到打开的进程
  • 最大的文件大约有 3 个文件,每个 25MB +/-

但我真的很困惑,为什么当我在 Windows 任务管理器中查看内存使用情况的 Windows 图时,内存使用量几乎完全没有下降(可能只有几 MB):它应该下降数百MB,如果不是数千(对于大型项目) - 它仅在我关闭我的应用程序后卸载。我真的等了 5 分钟、10 分钟、15 分钟......在那个图形窗口中仍然没有任何下降!

所以我想知道:我是否需要向我的 .bat 添加一些特定的开关来告诉 JVM“请卸载我的应用程序在渲染期间使用的所有未使用的内存”或者......?

最佳答案

Runtime.totalMemory() 返回 JVM 已从操作系统请求的内存量。在该内存中,“已使用”和“未使用”的概念仅与 Java 应用程序相关,与操作系统无关。所以 Runtime.freeMemory() 告诉您总内存中有多少内存可用于新分配,而这些值之间的差异告诉您有多少内存被 Java 对象占用,或者仍在使用中或尚未收集。

从操作系统的角度来看,totalMemory() 报告的值是应用程序请求的内存量,在简单系统中将被视为“正在使用”。因此,当 JVM 将内存返还给操作系统时,发生频率比垃圾回收低得多或根本不发生,具体取决于配置,totalMemory() 报告的数字将会减少。

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

此外,到目前为止讨论的所有内容都是“虚拟内存”,它映射到物理内存的方式是另一件复杂的事情。 Windows 将尝试将虚拟内存的页面映射到物理内存中的页面,但其他进程的内存需求可能会减少进程使用的物理内存。这个事实已被恶作剧工具利用, promise 通过简单地请求大量内存来“清理内存”,导致 Windows 重新分配物理内存,然后释放内存,因此使用的物理内存数量看起来非常低,因为它有已从正在运行的进程中取出,但当然,他们稍后会在继续时取回它,因此该操作只会降低性能。

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

此外,虚拟机中的垃圾回收完成的主要任务是使虚拟机中的内存可用于同一虚拟机中的新分配,而不是将内存返还给操作系统。后者有时可能会发生,具体取决于配置,但一般不会发生。

关于Java 对 Runtime.memory() 与 Windows 的物理内存使用历史图的混淆,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57921646/

相关文章:

java - 在多线程中使用 titan graph 的最佳实践是什么?

java - 单选按钮选择问题

performance - 与 Hibernate 或 JDBC 相比,JPA 批处理的性能很糟糕

powershell - teamcity powershell-无法运行批处理文件

c - 重置 PeakWorkingSetSize

c++ - C++ 中的内存模型和单例

php - PHP 实际使用了多少内存?

java - 想要使用共享的 @BeforeClass/@AfterClass 和 Spring App Context 运行许多 jUnit 测试类

java - 在 ec2 中部署类似服务的 springboot 问题

c - 如何从 visual studio 2010 调用批处理文件