我正在开发一个Swing应用程序,最近,我开始看到以下问题:
我必须在一个单独的窗口中显示一个非常大的工作报告。我关闭这个窗口,然后我重新打开相同的作业报告,我得到一个OutOfMemory Java堆空间错误。
JVM以-Xmx512m启动,当我打开作业报告时创建的所有对象在堆上占用大约300MB。假设没有内存泄漏,我希望第二次打开相同的作业报告时JVM不会抛出OOM。但是,在关闭第一个窗口后查看GC日志,我没有看到任何GC活动。
奇怪的是,在我关闭第一个窗口之后,如果我使用jmap(不带"live"选项)进行堆转储,仍然可以在堆转储中看到对象。
如果我使用dump:live选项运行jmap,会发生以下情况:
- 在获取第一个堆转储后,我仍然可以看到堆上的对象。
- 当我进行第二次堆转储时,它不再包含这些对象,我可以再次重新打开相同的作业报告,没有问题。所以,如果是内存泄漏,那么这些对象就不可能被收集,对吗?
我在Java 6(1.6.0_25和1.6.0_45,在Windows上)上测试了这个问题,它一直在复制。
运行jmap -heap打印:
"using thread-local object allocation.
Mark Sweep Compact GC
Heap Configuration:
MinHeapFreeRatio = 40
MaxHeapFreeRatio = 70
MaxHeapSize = 536870912 (512.0MB)
NewSize = 1048576 (1.0MB)
MaxNewSize = 4294901760 (4095.9375MB)
OldSize = 4194304 (4.0MB)
NewRatio = 2
SurvivorRatio = 8
PermSize = 12582912 (12.0MB)
MaxPermSize = 134217728 (128.0MB)
"
JVM通过以下选项启动:
" -Xms128m
-Xmx512m
-XX:MaxPermSize=128M
-verbose:gc
-XX:+PrintGCTimeStamps
-XX:+PrintGCDetails
-Xloggc:c:my_gc.log
-XX:+HeapDumpOnOutOfMemoryError"
所以,我的问题是:为什么当我使用活动选项(没有内存泄漏的迹象)进行堆转储时收集所有对象,但是,如果我不这样做,我就无法重新打开另一个(或相同的)作业报告,因为我得到一个OOM错误?
同样,我测试了另一个场景:
- 打开第一个作业报告窗口,然后关闭它。
- 创建了一个菜单项,当按下该菜单项时,将在无限循环中创建Long实例,直到JVM抛出一个OOM。我检查了在抛出OOM时生成的堆转储,堆中填充了99%的Long实例,并且堆上没有我的作业报告对象。
那么,我的问题是:为什么所有的对象收集当我采取带有活动选项的堆转储
jmap
live选项强制收集。
你的观察证实了这一点。
可以做的练习是重新打开窗口,发布一些GC活动,看看内存是否已被回收。