我想更好地了解我的应用程序,特别是它的内存占用。
我确实理解垃圾收集的概念,我知道堆中总会有一定数量的死对象,但是,我想最小化这个数量,以便用JConsole(或JVisualVM)监视向我提供了有关当前所需(未占用)空间的一些信息。
有没有办法在SunVM中配置现有的垃圾收集器(例如G1GC),以便(以响应性和运行时为代价)最小化堆中死对象的数量?
澄清
更清楚地了解我的目标:我的应用程序是非交互式的,因此在两次运行之间,随着时间的推移,内存占用量大致相同。我想确定所需的最小堆空间以及代码更改对该占用空间的影响。由于死对象,JConsole的输出在这里并没有真正帮助。我也想知道,我的峰值记忆是否确实在某个时间点是一个突出的峰值,或者它是否随着时间的推移而拉伸。这就是为什么在我到达OOME之前减少Xmx不是让我在那里的原因。
另外:我说的是在开发人员测试期间使用,而不是在生产中使用。在制作中,传播和表演当然比更现实的记忆足迹更重要。
答案 0 :(得分:5)
如果您想知道应用程序使用的内存总量,您必须在相当长的时间内监视它。应用程序随机时刻的堆样本:
jps -> output the pid of java process
jmap -dump:live,format=b,file=heap.bin [pid]
然后使用jhat
导航堆。还有其他工具可以做到。
执行此操作,您将立刻知道堆上的内容。
请记住,memory mapped files
之类的对象不会存储在堆中,而是存储在内存中。
到达OOM
时尝试添加此项,然后读取输出以查看哪些对象实际位于堆中:
-XX:-HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=./java_pid<pid>.hprof
为此,请启用GC log
,然后使用GCViewer等工具。
-XX:+PrintGCTimeStamps
-XX:+PrintGCDetails
-verbose:gc
-Xloggc:garbage_collector -> this set the file of the output
当你谈论堆时,我知道你在谈论tenured
空间。如果是这样,您需要知道对象的平均寿命。并遵循一些最佳实践,然后进行精细调整。另请注意,发出System.gc()
并不能保证GC
已执行。
事实是实例转到young generation (eden)
,当它已满时,执行minor GC
。仍可到达的对象将传递给名为survivor
的两个空格之一。一旦满了,该空间就会被转储到tenured
。完整后,full GC
执行并删除所有无法访问的实例。
您可以做的一件事是在方法中使用primitives
。这些不会放在堆中,因为它们的生命周期在线程堆栈中。
有用的参数是那些与ternured and young generation (eden)
的大小相关的参数。这些按比例工作。如果您要考虑许多GC
,请设置GC stop
的最长时间。
一些有趣的参数是:
-XX:MinFreeHeapRatio=
-XX:MaxHeapFreeRatio=
-XX:NewRatio=
-XX:SurvivorRatio
例如,设置-XX:NewRatio=3
意味着年轻一代与老一代的比例为1:3;换句话说,伊甸园和幸存者空间的总和将是堆的四分之一。
无论如何,我不知道为什么你需要这个要求。我通常只担心throughput
并且只关注throughput
错误时的那些参数。
答案 1 :(得分:0)
您可以在启动Java应用程序时传递-Xgenconfig
标志。您可以阅读更多相关信息here。
来自网站的引用:
genconfig开关可用于指定堆的大小 明确的年轻人和老年人。它也允许老一代 要指定的收集器。 (genconfig选项生成-Xms和 -Xmx选项不必要,但如果与genconfig一起使用选项-Xmx仍然被认为是一个 意味着限制旧一代堆大小。)
通过指定较小的堆大小,GC会更频繁地触发。我相信这正是你要找的。 p>
答案 2 :(得分:0)
虽然确实有一种方法可以将GC子系统配置为比默认值更具攻击性,但在所有可能性中,您都会意识到您正在努力将代码转向零垃圾。标准Java库几乎完全依赖于快速访问短期对象的每一步。例如字符串连接,增强的循环,自动装箱等等。
答案 3 :(得分:0)
是的,这是可能的,但这取决于您的应用程序在做什么。您只能通过试验和启用GC日志来找到此答案。
使用此功能,您将能够为您的应用找到旧版本的最小尺寸,然后您就可以开始为年轻一代尝试不同的尺寸。
请注意,如果年轻一代太小,可能会在旧一代中过早地提升对象,而不满足其生命周期要求。当您看到更多完整的GC集合时,您可以检测到这一点。