Java 混淆 Runtime.memory() 与 Windows' 物理内存使用历史图
Java confusion about Runtime.memory() vs. Windows' Physical Memory Usage History graph
我为自己创建了一个用于 Windows (Win7 x64) 的 Java 渲染应用程序,它在渲染过程中确实使用了大量内存(在一些大型项目中实际上是演出......我有我的 PC 和 6 核 CPU) 中有 8GB RAM,所以我必须在我的 .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 元素)
- 渲染处于活动状态时最大 4GB
- 渲染结束时回落到大约 80MB
我什至使用了 JAVA bin 文件夹中名为 Java VisualVM 的内存分析应用程序,以确保一切正常工作,没有任何内存泄漏......根据它的堆转储,它确实是像这样工作:
- 渲染结束后加载项目使用的内存约为80MB
- 未找到打开的进程
- 最大的文件大约有 3 个文件,每个 25MB +/-
但我真的很困惑,为什么当我在 Windows 任务管理器中查看内存使用情况的 Windows 图表时,内存使用情况几乎完全没有下降(也许只有一些MB 的):它应该下降数百 MB 如果不是数千(对于大型项目) - 它仅在我关闭我的应用程序后卸载。我真的等了 5 分钟、10 分钟、15 分钟……在那张图中仍然没有任何下降 window!
所以我想知道:我是否需要将一些特定的开关添加到我的 .bat 中以告知 JVM "please, do unload all the unused memory my app used during render" 或...?
Runtime.totalMemory()
returns JVM 已向操作系统请求的内存量。在该内存中,“已使用”和“未使用”的概念仅与 Java 应用程序相关,与操作系统无关。因此 Runtime.freeMemory()
告诉您总内存中有多少内存可用于新分配,而这些值之间的差异告诉您 Java 对象占用了多少内存,这些对象仍在使用或未收集还.
从操作系统的角度来看,totalMemory()
报告的值是应用程序请求的内存量,在简单系统中将被视为“正在使用”。因此,当 JVM 将内存返回给操作系统时,发生频率比垃圾收集低得多或根本不发生,具体取决于配置,totalMemory()
报告的数字会减少。
不幸的是,像 Windows 这样的操作系统并没有那么简单。它有自己的“已用内存”概念,不同于请求的内存和 Java 的“使用中”概念。内存按页面组织,Windows 将认为页面仅在应用程序实际写入时正在使用,因此它包含需要保留的内容。
此外,到目前为止讨论的所有内容都是“虚拟内存”,它映射到物理内存的方式是另一回事。 Windows 会尝试将虚拟内存的页面映射到物理内存中的页面,但其他进程的内存需求可能会减少进程使用的物理内存。这个事实已被恶作剧工具利用,承诺通过简单地请求大量内存来“清理内存”,导致 Windows 重新分配物理内存,然后释放内存,因此使用的物理内存数量看起来非常低,因为它已从 运行 进程中拿走,但当然,他们稍后会在继续时取回它,因此该操作只会降低性能。
要点是,如果不了解 运行 应用程序实际需要什么,任务管理器中的图表可能会变得毫无意义。 “分配(虚拟)内存”、“实际使用(虚拟)内存”和“当前使用(物理)内存”之间的关系不能用一个简单的图形来表达。
此外,虚拟机内的垃圾收集完成了使虚拟机内的内存可用于同一虚拟机内的新分配的主要任务,而不是将内存返还给操作系统。后者有时可能会发生,具体取决于配置,但一般不会。
所以这是 JDK v1.8 的最终解决方案。0_181(我的情况)
事实证明,我需要向我的 .bat 做广告的只是 -XX:+UseG1GC
开关,所以我的 java.bat 代码现在看起来像这样,它完全按照我的预期进行:将未使用的内存释放回系统:
@ECHO OFF
java -XX:+UseG1GC -Xmx8G -server -jar myapp %*
@if %errorlevel% neq 0 pause
"funny" 部分是我知道这个开关,但正如我在某处读到的那样,它默认用作默认 GC 收集器 - 即:无需手动设置 - 所以我完全忽略了该选项- 我只是没有检查它所说的部分来自 JDK 的特定版本 :-)。嗯...
非常感谢@apangin 的解决方案建议!
我为自己创建了一个用于 Windows (Win7 x64) 的 Java 渲染应用程序,它在渲染过程中确实使用了大量内存(在一些大型项目中实际上是演出......我有我的 PC 和 6 核 CPU) 中有 8GB RAM,所以我必须在我的 .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 元素)
- 渲染处于活动状态时最大 4GB
- 渲染结束时回落到大约 80MB
我什至使用了 JAVA bin 文件夹中名为 Java VisualVM 的内存分析应用程序,以确保一切正常工作,没有任何内存泄漏......根据它的堆转储,它确实是像这样工作:
- 渲染结束后加载项目使用的内存约为80MB
- 未找到打开的进程
- 最大的文件大约有 3 个文件,每个 25MB +/-
但我真的很困惑,为什么当我在 Windows 任务管理器中查看内存使用情况的 Windows 图表时,内存使用情况几乎完全没有下降(也许只有一些MB 的):它应该下降数百 MB 如果不是数千(对于大型项目) - 它仅在我关闭我的应用程序后卸载。我真的等了 5 分钟、10 分钟、15 分钟……在那张图中仍然没有任何下降 window!
所以我想知道:我是否需要将一些特定的开关添加到我的 .bat 中以告知 JVM "please, do unload all the unused memory my app used during render" 或...?
Runtime.totalMemory()
returns JVM 已向操作系统请求的内存量。在该内存中,“已使用”和“未使用”的概念仅与 Java 应用程序相关,与操作系统无关。因此 Runtime.freeMemory()
告诉您总内存中有多少内存可用于新分配,而这些值之间的差异告诉您 Java 对象占用了多少内存,这些对象仍在使用或未收集还.
从操作系统的角度来看,totalMemory()
报告的值是应用程序请求的内存量,在简单系统中将被视为“正在使用”。因此,当 JVM 将内存返回给操作系统时,发生频率比垃圾收集低得多或根本不发生,具体取决于配置,totalMemory()
报告的数字会减少。
不幸的是,像 Windows 这样的操作系统并没有那么简单。它有自己的“已用内存”概念,不同于请求的内存和 Java 的“使用中”概念。内存按页面组织,Windows 将认为页面仅在应用程序实际写入时正在使用,因此它包含需要保留的内容。
此外,到目前为止讨论的所有内容都是“虚拟内存”,它映射到物理内存的方式是另一回事。 Windows 会尝试将虚拟内存的页面映射到物理内存中的页面,但其他进程的内存需求可能会减少进程使用的物理内存。这个事实已被恶作剧工具利用,承诺通过简单地请求大量内存来“清理内存”,导致 Windows 重新分配物理内存,然后释放内存,因此使用的物理内存数量看起来非常低,因为它已从 运行 进程中拿走,但当然,他们稍后会在继续时取回它,因此该操作只会降低性能。
要点是,如果不了解 运行 应用程序实际需要什么,任务管理器中的图表可能会变得毫无意义。 “分配(虚拟)内存”、“实际使用(虚拟)内存”和“当前使用(物理)内存”之间的关系不能用一个简单的图形来表达。
此外,虚拟机内的垃圾收集完成了使虚拟机内的内存可用于同一虚拟机内的新分配的主要任务,而不是将内存返还给操作系统。后者有时可能会发生,具体取决于配置,但一般不会。
所以这是 JDK v1.8 的最终解决方案。0_181(我的情况)
事实证明,我需要向我的 .bat 做广告的只是 -XX:+UseG1GC
开关,所以我的 java.bat 代码现在看起来像这样,它完全按照我的预期进行:将未使用的内存释放回系统:
@ECHO OFF
java -XX:+UseG1GC -Xmx8G -server -jar myapp %*
@if %errorlevel% neq 0 pause
"funny" 部分是我知道这个开关,但正如我在某处读到的那样,它默认用作默认 GC 收集器 - 即:无需手动设置 - 所以我完全忽略了该选项- 我只是没有检查它所说的部分来自 JDK 的特定版本 :-)。嗯...
非常感谢@apangin 的解决方案建议!