首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >线程池(I)

线程池(I)

原创
作者头像
顾老师的猫
修改2024-10-31 07:41:25
修改2024-10-31 07:41:25
1820
举报

现网故事

背景介绍

现象:内存不断升高,负载不断重启。

  • 定性:这是什么样的问题?内存泄漏问题?

java程序,跑在linux虚拟机上,监控发现内存不断升高,重启后,又不断升高,直到再次重启。这种属于什么现象,要如何处理?

  • 内存泄漏问题与内存溢出问题区别是什么?

规避:面多了加水,水多了加面。

  • 加内存,调高堆内存 Xms和Xmx。后面2个分别是什么意思?

建议 初始堆内存大小(-Xms)和最大堆内存大小(-Xmx)设置为相同的值,避免动态调整堆大小带来的性能开销。

这里的性能开销

  1. 要消耗计算资源和时间。要重新分配内存空间;
  2. GC的频率和停顿时间;
  3. 应用程序不稳定;
  4. 频繁地调整对OS造成影响,影响性能;
  5. 如果处理内存泄漏问题
  6. 这问题放到问小微助手会得到什么答复?

java程序,跑在linux虚拟机上,监控发现内存不断升高,重启后,又不断升高,直到再次重启。这种属于什么现象,要如何处理?

  • 出现这种问题如何在节点上搞下文件
  • JVisualVM怎么用来分析文件的?

不断创建线程池,完了这些线程池又不会被JVM给GC

GC如何判断哪些对象应该被回收?哪些对象不应该被回收?

为什么在这个场景,不会被回收?

参考https://www.cnblogs.com/thisiswhy/p/16798811.html

  • 局部变量new线程池不会被回收吗?

看一段代码 这段代码打出来

  • 开头Test代码打出来,换个方法名GC回收的原理

主要看对象的可达性

  • 可达性算法

从GCRoot对象(GC根节点对象) 出发,沿着引用有向图上的路径,一个对象一个对象开始遍历,能到达就标个颜色(灰),遍历完的也标个颜色(黑),最后遍历完,没标颜色就是不可达对象,不再会使用到,就要被回收。

  • 哪些可以作为GCRoots对象?
  • 找Runnable getTask的源码
  • 什么是workerQueue
  • 如何验证呢

freesion.com/article/49741215019/

  • 找一下解释的原文贴进来

这个线程是live thread吧

证明 线程 -> executorService是可达的

  • 打补丁,经常一个java文件build(编译)出来好几个class文件,为什么?

因为非静态内部类持有外部类的引用。

再看下Worker类在哪?

Worker类声明在了ThreadPoolExecutor类的内部

Worker类返回的这个线程对象,是持有 threadPoolExecutor 对象引用的。完了它是一个live thread吧,可以作为GC Root吧,GC Root -> threadPoolExecutor 可达。

  • 怎么分析呢?说明下Profile插件用法
  • 补一下静态内部类代码

要调用父类的方法必须new一个外部类对象才行

什么道理:没有充分理由就别去创建非静态内部类,为什么?

  • 《Effective Java(第三版)》中的第 24 条:静态内部类优于非静态内部类

因为每new一次非静态内部类,就会有指向父类对象的引用。如果外部类有很大的对象时,这是不合理的,你又用不到他,非得去持它的对象干嘛,闲的没事,找OOM吗?

下面其实不需要

  • 虚拟机栈

栈帧中局部变量表:存方法参数和方法中定义的局部变量。生命周期是随方法调用结束栈帧销毁而销毁;方法区(又称静态区,存类的静态属性、常态、方法代码)

方法区中类静态属性引用的对象;

方法区中常量(常量池)引用的对象:public static final String/int/long;本地方法栈(转为执行本地方法服务)

本地方法栈中JNI(java与其他语言交互的机制)引用的对象:通过JNI的接口从堆传递到本地方法栈供本地方法使用,被本地方法使用的对象。

本地方法:被Java程序通过JNI调用的非Java语言编写的方法。关键字:native;

Java8 内存模型

线程池最佳实践

参考资料:https://developer.aliyun.com/article/1343284

https://blog.csdn.net/zxyhj/article/details/135915205

https://tech.meituan.com/2020/04/02/java-pooling-pratice-in-meituan.html

正确声明线程池

《华为编程规范》

阿里:https://mp.weixin.qq.com/s/icrrxEsbABBvEU0Gym7D5Q

  • 补充下华为编程规范如何要求声明线程池的
  • 补充阿里编程规范对线程池的要求
  • 补充下为什么要这么做?

线程池命名

  • 这块应该如何做?

不同类型业务用不同线程池

父子任务的坑:https://heapdump.cn/article/646639

  • 写出代码,问能不能合入?
  • 扒一下这个案例,记得画图!

业务背景说下

一个广告的扣费服务

用户:用户——>看到广告——>点击广告

广告主:点击数量——>产生费用——>扣除费用

代码语言:java
复制
// 线程池声明
bizThreadPool = new newFixedThreadPool(25);

// 执行扣费任务
public Result<Integer> executeDeduct(ChargeInputDTO chargeInput) {
  ChargeTask chargeTask = new ChargeTask(chargeInput);
  bizThreadPool.execute(() -> chargeTaskBill.execute(chargeTask));
  return Result.success();
}

// 扣费任务
public class ChargeTaskBill implements Runnable {
  public void execute(ChargeTask chargeTask) {
     // 参数校验
     verifyInputParam(chargeTask);
     // 反作弊校验
     executeUserSpam(SpamHelper.userConfigs);
     // 执行扣费
     handlePay(chargeTask);
     // 其他业务动作
     ...
  }
}

// 反作弊校验
public void executeUserSpam(List<SpamUserConfigDO> configs) {
  if (CollectionUtils.isEmpty(configs)) {
    return;
  }
  try {
    // 反作弊校验 n 个
    CountDownLatch latch = new CountDownLatch(configs.size());
    for (SpamUserConfigDO config : configs) {
      UserSpamTask task = new UserSpamTask(config,latch);
      bizThreadPool.execute(task);
    }
    //阻塞主线程,等n个反作弊校验方法执行完
    latch.await();
  } catch (Exception ex) {
    logger.error("", ex);
  }
}
  • CountDownLatch 搭配线程池是怎么用的
  • 线程池任务添加原理?

队列有哪些?

  • 造成死锁的原因有哪些?
  • 有没有进一步的优化空间?
  • 可用性:秒杀中订单扣费是用的MQ异步吗?

有关闭线程池吗?

案例:线程池创建放在逻辑判断里未关闭

  • 写出代码,问能不能合入?
  • 写出案例

线程池任务耗时吗?

  • 《future超时的坑》 写出代码,问能不能合入?
  • 写出案例,会导致什么后果?

重复创建线程池了吗?

重复创建线程池了吗?

创建线程池有没有放到局部变量里!

正确配置线程池参数

如何设置线程池参数? https://mp.weixin.qq.com/s/YbyC3qQfUm4B_QQ03GFiNw

  • 参数应该如何设置呢?

监控线程池状态

假如我们有个线程池,叫xx

正在运行中,各种数据是多少怎么弄?在页面上展示出来

  • 用userwebsite代码写个demo,url访问调用这些方法的结果;

总结-《线程池自检清单》

  • 画出这张自检清单

动态线程池

  • 创建2个线程池,用Map来装,用url请求来处理。

线程池动态调整:https://mp.weixin.qq.com/s/FJQ5MhB1kMp8lP1NA6q4Vg

提问

  • 如果让你设计一个动态线程池,要支持各种配置中心,不能引入外部jar包,你会怎么设计?

提问:https://mp.weixin.qq.com/s?__biz=Mzg3NjU3NTkwMQ==&mid=2247505057&idx=1&sn=621ebc409b589478e2e05388e079d8c0

https://www.cnblogs.com/thisiswhy/p/18150414

DynamicTp:https://dynamictp.cn/guide/introduction/background.html

https://github.com/dromara/dynamic-tp

Java内存模型

堆里面会存放哪些?

  1. 对象:new创建的所有对象;
  2. 数组对象;
  3. 类实例对象:类的实力变量(非静态成员变量);

堆转储文件能看到哪些内容?

堆转储文件 heap dump,是JVM在运行时的快照,记录Java堆中对象的状态信息。

用来诊断和分析内存问题,如内存泄漏、内存溢出。

里面包含有

  1. 所有对象
  2. 所有类
  3. GC Roots
  4. 线程堆栈和本地变量;

阻塞队列

雷军-墨菲定律

雷军演讲提到过得墨菲定律

凡事只要有可能出错,那就一定会出错

如何设置线程池参数

其实关注3个参数就行

corePoolSize、maximumPoolSize、workQueue(队列长度)

IO 密集型任务

I/O密集型的场景在开发中比较常见,比如像 MySQL数据库读写、文件的读写、网络通信等任务。

IO密集型通常设置为 2n+1(n 为 CPU 核数;)

CPU密集型

比如加解密,压缩、计算等一系列需要大量耗费 CPU 资源的任务。建议设置为n+1,避免多线程环境下CPU资源带来上下文频繁切换的开销。

有些脱离实际

  1. 因为机器上肯定不止你一个应用,肯定不止你一个线程池!
  2. 不同时间段,流量肯定不一样;

所以最好的解决方式——动态化配置

为什么可以支持呢?因为JDK线程池提供了这样的方法

动态更新的工作原理是什么?

动态设置的注意点有哪些?

allowCoreThreadTimeOut 在哪设置?

如何动态指定队列长度?

把 LinkedBlockingQueue 粘贴一份出来

executor.getQueue();

所以,我们可以对线程池进行多维度的监控,比如其中的一个维度就是队列使用度的监控。

当队列使用度超过 80% 的时候就发送预警短信,提醒相应的负责人提高警惕,可以到对应的管理后台页面进行线程池参数的调整,防止出现任务被拒绝的情况。

代码语言:java
复制
public void setCapacity(int capacity) {
    final int oldCapacity = this.capacity;
    this.capacity = capacity;
    final int size = count.get();
    if (capacity > size && size >= oldCapacity) {
        signalNotFull();
    }

非常小概率处异常。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 现网故事
    • 背景介绍
    • 线程池最佳实践
      • 正确声明线程池
      • 线程池命名
      • 不同类型业务用不同线程池
      • 有关闭线程池吗?
      • 线程池任务耗时吗?
      • 重复创建线程池了吗?
      • 正确配置线程池参数
      • 监控线程池状态
    • 总结-《线程池自检清单》
    • 动态线程池
    • 提问
  • Java内存模型
    • 阻塞队列
  • 雷军-墨菲定律
  • 如何设置线程池参数
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档