V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
• 请不要在回答技术问题时复制粘贴 AI 生成的内容
wangxin3
V2EX  ›  程序员

Java 线程池/阻塞队列疑问三连,请大佬解答!

  •  
  •   wangxin3 · 2022-12-14 17:03:40 +08:00 · 1892 次点击
    这是一个创建于 743 天前的主题,其中的信息可能已经有所发展或是发生改变。
    static ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1, 2, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(5000), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());
    
    public static void main(String[] args) {
        List<Map<String, String>> list = new ArrayList<>();
        // add 5000 次
        list.add(new HashMap<>(16));
    
        list.forEach(l -> CompletableFuture.runAsync(() -> {
            System.out.println(l.get("xxx"));
            System.out.println(l.get("yyy"));
            // 又臭又长的代码 耗时 1s
        }, threadPoolExecutor));
        
        // 问:此时阻塞队列中任务内存占用主要来源于哪里?
        //    是遍历 list 出来的每个元素对象的占用内存吗?
        //    业务逻辑代码的长短 /复杂度会影响内存占用吗?
    }
    
    第 1 条附言  ·  2022-12-15 09:34:56 +08:00
    其实我问这个问题的原因主要是,我们测试环境的每个 pod 内存给的太少,去年的时候大批量提交任务就 oom 重启了(当时怀疑提交任务太多了,当时单纯认为,任务提交到阻塞队列越多,内存占用越多),然后当时就想着加上 mq ,用 mq 来缓解阻塞队列的内存占用,但是现在回过头来再看这块,在想有没有必要把 mq 去掉,因为徒增了复杂度,提高了不稳定性。
    13 条回复    2022-12-15 19:58:44 +08:00
    bootvue
        1
    bootvue  
       2022-12-14 17:10:52 +08:00
    没有大佬 可以配合 jprofile 分析下
    guyeu
        2
    guyeu  
       2022-12-14 17:22:33 +08:00
    了解下引用和对象的概念呗
    wangxin3
        3
    wangxin3  
    OP
       2022-12-14 17:35:29 +08:00
    @guyeu #2 原文:“了解下引用和对象的概念呗”
    ======
    回复:想了想,list 对象在进入阻塞队列之前已经存在于内存中了,所以线程池中的内存占用主要是来源于对象的地址,以及方法的地址?不知道是不是这样理解
    wangxin3
        4
    wangxin3  
    OP
       2022-12-14 17:35:40 +08:00
    @bootvue #1 原文:“没有大佬 可以配合 jprofile 分析下”
    ======
    回复:get 新工具!
    7911364440
        5
    7911364440  
       2022-12-14 17:41:48 +08:00
    线程池的内存占用取决于线程的数量,线程的内存占用取决于 run()方法中申请的内存空间,其实就是看你那段 "又臭又长的代码 耗时 1s" 的逻辑
    wangxin3
        6
    wangxin3  
    OP
       2022-12-14 17:54:55 +08:00
    @7911364440 #5 原文:“线程池的内存占用取决于线程的数量,线程的内存占用取决于 run()方法中申请的内存空间,其实就是看你那段 "又臭又长的代码 耗时 1s" 的逻辑”
    ======
    回复:懂了,那按理来说线程池导致的 oom ,唯一的原因可能就是没有设置阻塞队列的长度?因为如果设置了长度,合理设置了 corePoolSize 、maximumPoolSize ,那么线程池多个线程在运行同一个方法时,因为单个方法的内存占用量是固定的,corePoolSize 是固定的,所以可以说线程池的内存占用量就是固定的。
    线程池内存占用量 = corePoolSize * 单个方法的内存占用量(阻塞队列还没满时)
    Yuesh1
        7
    Yuesh1  
       2022-12-14 18:37:41 +08:00
    突然想到,线程池本身也是一个对象,也需要内存占用,应该也有少量的内存消耗。
    我理解的线程池 oom ,在合理设置了参数后,应该就是阻塞队列的 maxSize = Integer.MAX_VALUE 所导致的
    期待其他大佬的见解
    optional
        8
    optional  
       2022-12-14 19:11:57 +08:00
    你这里每个 map 对象每个对象都被 list 引用了,没有释放的机会;
    另外()->{}会创建一个闭包对象。
    chendy
        9
    chendy  
       2022-12-14 19:58:29 +08:00
    > 此时阻塞队列中任务内存占用主要来源于哪里?

    来源于 '阻塞别列中任务占用的内存'

    > 是遍历 list 出来的每个元素对象的占用内存吗?

    是 '每个元素对象的的占用的内存'

    > 业务逻辑代码的长短 /复杂度会影响内存占用吗?

    会,越慢堆积任务越多,越复杂当然也多
    liprais
        10
    liprais  
       2022-12-14 20:30:23 +08:00 via iPhone
    不要打印到标准输出,那上面有个锁
    imv2er
        11
    imv2er  
       2022-12-14 20:52:10 +08:00
    我的能算标准答案么
    //此时阻塞队列中任务内存占用主要来源于哪里?
    map 中的 kv 元素数量
    // 是遍历 list 出来的每个元素对象的占用内存吗?
    是的
    // 业务逻辑代码的长短 /复杂度会影响内存占用吗
    会 ,因为这些逻辑代码中间几乎肯定会产生很多临时对象, 当然回收也很快。
    wangxin3
        12
    wangxin3  
    OP
       2022-12-15 09:45:36 +08:00
    @imv2er
    @chendy
    那么阻塞队列的阻塞的任务(没有运行的,正在排队的任务)内存占用量其实就是多线程任务的入参了吧。因为 corePoolSize 是固定的,阻塞队列是有设置大小的,所以就可以理解线程池的内存占用是固定的吧,或者说增长是微乎其微,因为只有基本类型和 String 的入参。
    imv2er
        13
    imv2er  
       2022-12-15 19:58:44 +08:00
    @wangxin3 你等固定任务数是队列大小 /长度 但是你控制不了 map 中又多少元素。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5466 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 27ms · UTC 08:29 · PVG 16:29 · LAX 00:29 · JFK 03:29
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.