我在最近的项目中遇到了一个奇怪的内存泄漏问题。我有一个'主'代码,它将消息发送到'处理程序'代码。通信时,处理程序代码执行固定任务,将结果写入磁盘并退出。通过调用另一个类中的特定函数来执行固定任务。
在执行此功能期间,堆大小会增加(预期)。但是,在完成代码后,结果本身不应占用大量空间。人们会期望完成对处理程序的调用会导致内存占用量减少。但是,“top”表示处理程序仍然消耗大量内存(RES)。
线程退出是否应该确保线程消耗的堆被回收?我尝试在函数执行时发出System.gc()调用,但是,它不会强制进行垃圾回收。
答案 0 :(得分:4)
Hotspot JVM中的垃圾收集器只是“不情愿地”将内存返回给操作系统。假设应用程序可能会在回收的空间中创建更多对象。如果GC要返回内存,那么它将需要再次请求它, churn 将导致额外的系统调用,等等。
但是,如果JVM注意到堆不必要地大,那么它将减小堆大小,这最终可能导致JVM返回一些内存。调整大小机制记录在此Oracle Heap Tuning Parameters页面上。
还应该注意,线程的堆栈没有在Java堆中分配。而是它存在于非堆内存段中,该段通常在线程启动时从OS请求,并在线程终止时释放。
答案 1 :(得分:2)
线程退出是否应该确保线程消耗的堆被回收?我尝试在函数执行时发出System.gc()调用,但是,它不会强制进行垃圾回收。
尝试添加一些JVM标志来记录GC活动:
-verbose:gc
-XX:+PrintGCTimeStamps
-XX:+PrintGCDetails
我想你会发现这个空间实际上正在被收回。 top
报告的内存占用空间包括为Java堆保留的空间,无论它是否已用于分配对象。您无法可靠地使用这些数字来监控GC活动。
线程终止不一定会触发垃圾回收。你也不应该期望这个过程的足迹在垃圾收集时缩小。 JVM将根据需要增加其托管堆的大小,但不一定会在第一次机会(或可能永远)缩小它。如果程序的内存使用率达到这些级别一次,那么JVM没有理由相信它不会再发生,因此除非系统内存压力非常高,否则JVM不太可能立即将该内存释放回操作系统(如果有的话)。