0

在我最近的项目中,我面临一个奇怪的内存泄漏问题。我有一个“主”代码,它向“处理程序”代码发送消息。通信时,处理程序代码执行固定任务,将结果写入磁盘,然后退出。固定任务是通过调用另一个类中的特定函数来执行的。

在执行此函数期间,堆大小会增加(预期)。但是,在代码完成后,结果本身不应占用太多空间。人们会期望对处理程序的调用完成会导致较小的内存占用。但是,“顶部”表明处理程序仍然消耗大量内存 (RES)。

线程退出不应该确保线程消耗的堆被回收吗?我尝试在函数执行后进行 System.gc() 调用,但是,它不会强制进行垃圾收集。

4

2 回答 2

4

Hotspot JVM 中的垃圾收集器只会“不情愿”地将内存归还给操作系统。假设是应用程序可能会在回收的空间中创建更多对象。如果 GC 要归还内存,则需要再次请求它,并且流失会导致额外的系统调用等。

但是,如果 JVM 注意到堆不必要的大,它将减小堆大小,这最终可能导致 JVM 返还一些内存。调整大小机制记录在此 Oracle堆调整参数页面上。


还应该注意,线程的堆栈不是在 Java 堆中分配的。相反,它位于非堆内存段中,通常在线程启动时从操作系统请求,并在线程终止时释放。

于 2013-10-29T13:54:14.610 回答
2

线程退出不应该确保线程消耗的堆被回收吗?我尝试在函数执行后进行 System.gc() 调用,但是,它不会强制进行垃圾收集。

尝试添加一些 JVM 标志来记录 GC 活动:

-verbose:gc
-XX:+PrintGCTimeStamps
-XX:+PrintGCDetails

我想你会看到空间实际上正在被回收。报告的内存占用top包括为 Java 堆保留的空间,无论它是否已用于分配对象。您不能可靠地使用这些数字来监控 GC 活动。

线程终止不一定会触发垃圾回收。您也不应该期望进程的足迹会在垃圾收集时缩小。JVM 将根据需要增加其托管堆的大小,但它不一定会在第一次机会(或可能永远)时缩小它。如果您的程序的内存使用量曾经达到过这些水平,那么 JVM 没有理由相信它不会再次发生,因此除非系统内存压力非常高,否则 JVM 不太可能立即将该内存释放回操作系统(如果曾经)。

于 2013-10-29T13:53:38.780 回答