现象:内存不断升高,负载不断重启。
java程序,跑在linux虚拟机上,监控发现内存不断升高,重启后,又不断升高,直到再次重启。这种属于什么现象,要如何处理?
规避:面多了加水,水多了加面。
建议 初始堆内存大小(-Xms)和最大堆内存大小(-Xmx)设置为相同的值,避免动态调整堆大小带来的性能开销。
这里的性能开销指
java程序,跑在linux虚拟机上,监控发现内存不断升高,重启后,又不断升高,直到再次重启。这种属于什么现象,要如何处理?
不断创建线程池,完了这些线程池又不会被JVM给GC
GC如何判断哪些对象应该被回收?哪些对象不应该被回收?
为什么在这个场景,不会被回收?
参考https://www.cnblogs.com/thisiswhy/p/16798811.html
看一段代码 这段代码打出来
主要看对象的可达性
从GCRoot对象(GC根节点对象) 出发,沿着引用有向图上的路径,一个对象一个对象开始遍历,能到达就标个颜色(灰),遍历完的也标个颜色(黑),最后遍历完,没标颜色就是不可达对象,不再会使用到,就要被回收。
freesion.com/article/49741215019/
这个线程是live thread吧
证明 线程 -> executorService是可达的
因为非静态内部类持有外部类的引用。
再看下Worker类在哪?
Worker类声明在了ThreadPoolExecutor类的内部
Worker类返回的这个线程对象,是持有 threadPoolExecutor 对象引用的。完了它是一个live thread吧,可以作为GC Root吧,GC Root -> threadPoolExecutor 可达。
要调用父类的方法必须new一个外部类对象才行
什么道理:没有充分理由就别去创建非静态内部类,为什么?
因为每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
业务背景说下
一个广告的扣费服务
用户:用户——>看到广告——>点击广告
广告主:点击数量——>产生费用——>扣除费用
// 线程池声明
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);
}
}
队列有哪些?
案例:线程池创建放在逻辑判断里未关闭
重复创建线程池了吗?
创建线程池有没有放到局部变量里!
如何设置线程池参数? https://mp.weixin.qq.com/s/YbyC3qQfUm4B_QQ03GFiNw
假如我们有个线程池,叫xx
正在运行中,各种数据是多少怎么弄?在页面上展示出来
线程池动态调整:https://mp.weixin.qq.com/s/FJQ5MhB1kMp8lP1NA6q4Vg
https://www.cnblogs.com/thisiswhy/p/18150414
DynamicTp:https://dynamictp.cn/guide/introduction/background.html
https://github.com/dromara/dynamic-tp
堆里面会存放哪些?
堆转储文件能看到哪些内容?
堆转储文件 heap dump,是JVM在运行时的快照,记录Java堆中对象的状态信息。
用来诊断和分析内存问题,如内存泄漏、内存溢出。
里面包含有
雷军演讲提到过得墨菲定律
凡事只要有可能出错,那就一定会出错
其实关注3个参数就行
corePoolSize、maximumPoolSize、workQueue(队列长度)
IO 密集型任务
I/O密集型的场景在开发中比较常见,比如像 MySQL数据库读写、文件的读写、网络通信等任务。
IO密集型通常设置为 2n+1(n 为 CPU 核数;)
CPU密集型
比如加解密,压缩、计算等一系列需要大量耗费 CPU 资源的任务。建议设置为n+1,避免多线程环境下CPU资源带来上下文频繁切换的开销。
有些脱离实际
所以最好的解决方式——动态化配置
为什么可以支持呢?因为JDK线程池提供了这样的方法
动态更新的工作原理是什么?
动态设置的注意点有哪些?
allowCoreThreadTimeOut 在哪设置?
如何动态指定队列长度?
把 LinkedBlockingQueue 粘贴一份出来
executor.getQueue();
所以,我们可以对线程池进行多维度的监控,比如其中的一个维度就是队列使用度的监控。
当队列使用度超过 80% 的时候就发送预警短信,提醒相应的负责人提高警惕,可以到对应的管理后台页面进行线程池参数的调整,防止出现任务被拒绝的情况。
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 删除。