V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
LudwigWS
V2EX  ›  Java

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

  •  
  •   LudwigWS · 2020-08-03 18:04:01 +08:00 · 4002 次点击
    这是一个创建于 1602 天前的主题,其中的信息可能已经有所发展或是发生改变。

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

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

    感谢各位
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2589 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 143ms · UTC 11:01 · PVG 19:01 · LAX 03:01 · JFK 06:01
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.