为什么这个程序会内存泄漏?

2020-08-03 18:04:01 +08:00
 LudwigWS

该程序后期会不断 GC,但是又无法回收多少内存,最终可能会 OOM

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * 从数据库中读取信用数据,套用模型,并把结果进行记录和传输
 */

public class T15_FullGC_Problem01 {

    private static class CardInfo {
        BigDecimal price = new BigDecimal(0.0);
        String name = "张三";
        int age = 5;
        Date birthdate = new Date();

        public void m() {}
    }

    private static ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(50,
            new ThreadPoolExecutor.DiscardOldestPolicy());

    public static void main(String[] args) throws Exception {
        executor.setMaximumPoolSize(50);

        for (;;){
            modelFit();
            Thread.sleep(100);
        }
    }

    private static void modelFit(){
        List<CardInfo> taskList = getAllCardInfo();
        taskList.forEach(info -> {
            // do something
            executor.scheduleWithFixedDelay(() -> {
                //do sth with info
                info.m();

            }, 2, 3, TimeUnit.SECONDS);
        });
    }

    private static List<CardInfo> getAllCardInfo(){
        List<CardInfo> taskList = new ArrayList<>();

        for (int i = 0; i < 100; i++) {
            CardInfo ci = new CardInfo();
            taskList.add(ci);
        }

        return taskList;
    }
}
3967 次点击
所在节点    Java
8 条回复
senninha
2020-08-03 18:30:35 +08:00
CardInfo 一个都回收不了,这不就内存泄漏了。
ChanKc
2020-08-03 19:09:32 +08:00
一直都在加 cardinfo 而且 executor 来不及执行所以越来越多?
LudwigWS
2020-08-03 19:22:37 +08:00
@senninha
请问一下为什么 CardInfo 回收不了?线程池满了以后旧的任务被抛弃了,按理说垃圾回收器不是能回收了么。
airfling
2020-08-03 19:36:12 +08:00
回收不了的,CardInfo 被 Runnable 隐式调用,ScheduledThreadPoolExecutor 你看的是核心线程是 50,但是任务队列应该是无线大的
yannxia
2020-08-03 19:46:11 +08:00
cardinfo 在 threadpool 的 queue 里面了,虽然都没有实际调用,但是的确是越来越多的。调整 scheduleWithFixedDelay 的速度 或者把 queue 改小。
asd123456cxz
2020-08-03 20:09:08 +08:00
ScheduledThreadPoolExecutor 使用的是 DelayedWorkQueue,它在提交队列到容量上限时会进行扩容,即不会触发 discard 。
仅看了下源码,没有验证,有问题请指正
senninha
2020-08-03 20:23:45 +08:00
@LudwigWS 大兄弟,一个 CardInfo 就 96B 了,一个任务加进队列还要包装几层,应该有 200B+。而默认的工作队列会膨胀到 Integer.MAX_VALUE,都跑不到 reject 内存就已经炸了啊。
LudwigWS
2020-08-03 21:01:08 +08:00
@ChanKc
@airfling
@yannxia
@asd123456cxz
@senninha
这个构造器默认最大队列容量是 Integer.MAX_VALUE,当提到这一点的时候我瞬间就明白了,哈哈哈。还是想当然了,把核心线程数当作拒绝临界点。所以也是这个问题比较隐蔽的原因。

我还以为是这里的内存泄漏涉及到垃圾回收器的回收算法,没想到只是使用上的问题。

感谢各位

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/695310

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX