有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

批处理文件Java关于运行时的混淆。memory()与Windows的物理内存使用历史记录图

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

@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,如果不是数千MB(对于所说的大型项目),它只有在我关闭应用程序后才会卸载。我真的等了5分钟,10分钟,15分钟。。。图形窗口中仍然没有任何下降

所以我想知道:有没有什么特别的开关我需要添加到我的。bat会告诉JVM“请卸载我的应用程序在渲染期间使用的所有未使用的内存”或


共 (2) 个答案

  1. # 1 楼答案

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

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

    不幸的是,像Windows这样的操作系统并不那么简单。它有自己的“已用内存”概念,这与请求内存和Java的“正在使用”概念都不同。内存是按页组织的,当应用程序实际写入时,Windows会考虑只使用一个页面,所以它包含需要保存的东西。p>

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

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

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

  2. # 2 楼答案

    这是JDK v1的最终解决方案。8.0_181(我的案例)

    事实证明,我所需要做的就是在我的网站上登广告。bat是-XX:+UseG1GC开关,所以我的java。bat代码现在终于看起来像这样了,它正做着我期望的事情:将未使用的内存释放回系统:

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

    “有趣的”部分是我知道这个开关,但当我在某处读到它默认用作默认GC收集器时——也就是说:不需要手动设置它——所以我完全忽略了这个选项——我只是没有检查它在特定版本的JDK中所说的部分:-)。嗯

    非常感谢@apangin提供解决方案建议