有没有办法在不使用时降低 Java 堆?
我目前正在开发一个 Java 应用程序,并致力于优化其内存使用。据我所知,我正在遵循正确的垃圾收集指南。然而,我的堆似乎达到了最大大小,尽管不需要它。
当计算机无人使用时,我的程序每小时运行一次资源密集型任务。该任务使用了相当大的内存块,但在任务完成后立即释放了所有内存。 NetBeans 分析器显示内存使用情况如下所示:
我真的很想提供所有堆不使用时将空间返回给操作系统。当程序至少在接下来的一个小时内不会做任何事情时,我没有理由霸占这一切。
这可能吗?谢谢。
I'm working on a Java application at the moment and working to optimize its memory usage. I'm following the guidelines for proper garbage collection as far as I am aware. However, it seems that my heap seems to sit at its maximum size, even though it is not needed.
My program runs a resource intensive task once an hour, when the computer is not in use by a person. This task uses a decent chunk of memory, but then frees it all immediately after the task completes. The NetBeans profiler reveals that memory usage looks like this:
I'd really like to give all of that heap space back to the OS when not in use. There is no reason for me to hog it all while the program won't even be doing anything for at least another hour.
Is this possible? Thanks.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
您也许可以使用
-XX:MaxHeapFreeRatio
- 这是 GC 收缩之前空闲堆的最大百分比(默认为 70)。也许将其设置得低一点(40 或 50?),然后使用 System.gc() 可能会花一些时间来获得所需的行为?然而,没有办法强制这种情况发生,您可以尝试并鼓励 JVM 这样做,但您不能在您想要的时候就将内存拉走。虽然上面的方法可能会缩小堆,但内存不一定会直接返回给操作系统(尽管在最近的 JVM 实现中确实如此。)
You could perhaps play around with
-XX:MaxHeapFreeRatio
- this is the maximum percentage (default 70) of the heap that is free before the GC shrinks it. Perhaps setting it a bit lower (40 or 50?) and then usingSystem.gc()
might go some lengths to get you the desired behaviour?There's no way to force this to happen however, you can try and encourage the JVM to do so but you can't just yank memory away as and when you want to. And while the above may shrink the heap, that memory won't necessarily be handed straight back to the OS (though in recent implementations of the JVM it does.)
简短版本:是的,可以。
长版本:
Java/JVM 如何管理内存
对于大多数应用程序,JVM 默认值都可以。看起来 JVM 期望应用程序仅运行有限的时间。因此它似乎不会自行释放内存。
为了帮助 JVM 决定如何以及何时执行垃圾收集,应提供以下参数:
-Xms
指定最小堆大小–Xmx
指定最大堆size对于服务器应用程序添加:
-server
这对我来说还不够。我想要更多的控制权!
如果上述参数不够,您可以影响 JVM 关于垃圾收集的行为。
首先,您可以使用 System.gc() 告诉虚拟机您何时认为垃圾回收有意义。其次,您可以指定 JVM 应使用哪种垃圾收集器:
不同类型的垃圾收集器:
串行 GC
命令行参数:
-XX:+UseSerialGC
停止您的应用程序并执行 GC。
并行GC
命令行参数:
-XX:+UseParallelGC -XX:ParallelGCThreads=value
与您的应用程序并行运行次要集合。减少主要集合所需的时间,但使用另一个线程。
并行压缩GC
命令行参数:
-XX:+UseParallelOldGC
与您的应用程序并行运行主要集合。使用更多CPU资源,减少内存使用。
CMS GC
命令行参数:
-XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:+CMSParallelRemarkEnabled -XX:CMSInitiatingOccupancyFraction=value -XX:+UseCMSInitiatingOccupancyOnly
执行较小的收集,并且比串行 GC 更频繁,从而限制应用程序的中断/停止。
G1
命令行参数:
-XX:+UnlockExperimentalVMOptions -XX:+UseG1GC
实验性(至少在 Java 1.6 中):尝试确保应用程序的停止时间不会超过 1 秒。
#例子
未经任何优化的 Play Framework Web 应用程序的内存使用情况:
正如您所看到的,它使用了相当多的堆空间,并且使用的空间会定期释放。
在这种情况下,仅使用参数进行优化是无效的。有一些计划任务使用了相当多的内存。在本例中,通过在内存密集型操作后结合使用
CMS GC
和System.gc()
实现了最佳性能。结果,WebApp 的内存使用量从 1.8 GB 减少到大约 400-500 MB。您可以在此处看到 VisualVM 的另一个屏幕截图,其中显示了 JVM 如何释放内存并实际返回到操作系统:
注意:我在代码中使用 VisualVM 的“执行 GC”按钮来执行 GC,而不是
System.gc()
,因为消耗内存的计划任务仅在特定时间启动并且用 VisualVM 捕获有点困难。进一步阅读
Short version: Yes you can.
Long version:
How Java/JVM manages memory
For most applications the JVM defaults are okay. It looks like the JVM expects applications to run only a limited period of time. Therefore it does not seem to free memory on it's own.
In order to help the JVM to decide how and when to perform garbage collection, the following parameters should be supplied:
-Xms
Specifies the minimal heap size–Xmx
Specifies the maximal heap sizeFor server applications add:
-server
That's not enough for me. I want more control!
In case the above mentioned parameters are not enough, you can influence the behavior of the JVM regarding garbage collection.
First you can use
System.gc()
to tell the VM when you believe garbage collection would make sense. And second you can specify which of the garbage collectors the JVM should use:Different Kinds of Garbage collectors:
Serial GC
Command line parameter:
-XX:+UseSerialGC
Stops your application and performs GC.
Parallel GC
Command line parameter:
-XX:+UseParallelGC -XX:ParallelGCThreads=value
Runs minor collections in parallel with your application. Reduces time needed for major collections, but uses another thread.
Parallel Compacting GC
Command line parameter:
-XX:+UseParallelOldGC
Runs major collections in parallel with your application. Uses more CPU resources, reduces memory usage.
CMS GC
Command line parameter:
-XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:+CMSParallelRemarkEnabled -XX:CMSInitiatingOccupancyFraction=value -XX:+UseCMSInitiatingOccupancyOnly
Performs smaller collections, and more often than Serial GC, thus limiting the breaks/stops of the application.
G1
Command line parameter:
-XX:+UnlockExperimentalVMOptions -XX:+UseG1GC
Experimental (at least in Java 1.6): Tries to make sure the application is never stopped for more than 1s.
#Example
Memory usage of a Play Framework web application without any optimizations:
As you can see, it uses quite a lot of heap space, and the used space is regularly freed.
In this case the optimizations with parameters only were not effective. There were some scheduled tasks which used rather a lot of memory. In this case the best performance was achieved by using the
CMS GC
combined withSystem.gc()
after the memory intensive operations. As a result the memory usage of the WebApp was reduced from 1.8 GB to around 400-500 MB.You can see here another screenshot from the VisualVM which shows how memory is freed by the JVM and actually returned to the OS:
Note: I used the "Perform GC"-button of the VisualVM to perform the GC rather than
System.gc()
in my code, as the scheduled tasks which consume the memory are only launched at specific times and somewhat harder to capture with VisualVM.Further Reading
Java 12 使用 G1GC 支持此功能。
https://openjdk.java.net/jeps/346
Java 13 使用 zgc 支持此功能
http://openjdk.java.net/jeps/351
Java 12 supports this feature using G1GC.
https://openjdk.java.net/jeps/346
Java 13 supports this feature using zgc
http://openjdk.java.net/jeps/351
一种可能性是让您的后台 java 应用程序每小时启动一个外部 jvm 实例来运行您的任务。这样,任务之间就只有原始的 jvm 应用程序在运行。
One possibility is to have your background java application launch an external jvm instance each hour to run your task. That way only your original jvm application is running between tasks.
如果您的应用程序在不活动期间处于静止状态,操作系统可能会为您换出这些页面,从而减轻它们对物理内存的压力。
http://www.linuxvox。 com/2009/10/what-is-the-linux-kernel-parameter-vm-swappiness/
If your app is quiescent during periods of inactivity, it's possible the OS will swap out those pages for you, mitigating their pressure on physical memory.
http://www.linuxvox.com/2009/10/what-is-the-linux-kernel-parameter-vm-swappiness/
Java 最好的秘密:-Xincgc
它确实会影响性能,但并不总是那么大。有时确实如此,取决于您在做什么。
增量垃圾收集器很好地将内存交还给系统!
Java best kept secret: -Xincgc
It does impact performance but not always that much. Sometimes it does, depends on what you're doing.
The incremental garbage collector hands memory back to the system quite well !
正如自四年前撰写以来许多人所指出的那样,如果为 JVM 提供正确的 GC 设置,则可以将内存返还给操作系统。
As noted by several people since this was written four years ago, you can give memory back to the OS if you give the proper GC settings to the JVM.