马老师jvm多线程oom习题
习题代码
/**
* 从数据库中读取信用数据,套用模型,并把结果进行记录和传输
*/
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;
}
}
scheduleAtFixedRate 方法介绍
参考 https://juejin.im/post/5ae75604f265da0ba56753cd
ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit)
创建并执行一个在给定初始延迟后首次启用的定期操作,随后,在每一次执行终止和下一次执行开始之间都存在给定的延迟。
代码分析
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);
});
}
- 在第一次调用
modelFit
方法时,taskList包含50个对象。 - 遍历第一个对象card1时,会先暂停2秒,然后
executor
会先从线程池中,取出一个线程,来执行info.m();
执行完后会暂停3秒钟。三秒钟之后,会再从线程池中拿出一个线程,再次执行info.()
。 - 在遍历第二个对象card2时,会重复上述流程。但是这是,
executor
依然持有着card1对象。 - 当遍历完成后,
executor
会持有50个card对象。 - 第二次调用
modelFit
方法时,会重复上述流程,并且依旧保留第一次调用时所持有的对象。导致所有生成的cardInfo对象都不会被回收。
代码测试
为了方便观察,代码做以下修改:
- 给
CardInfo
对象,添加age标记。 - 主线程只进行一次
modelFit
调用,然后休眠。 - 减小list集合大小,设置为5.
public class T15_FullGC_Problem01_2 {
static AtomicInteger integer = new AtomicInteger(0);
static AtomicInteger integer2 = new AtomicInteger(0);
private static class CardInfo {
BigDecimal price = new BigDecimal(0.0);
String name = "张三";
int age = 5;
Date birthdate = new Date();
public CardInfo(int age) {
this.age = age;
}
public void m() {
System.out.println("当前线程是"+Thread.currentThread().getName() +",对应crad的" + " age是" + age + "---");
}
}
private static ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(50,
new ThreadPoolExecutor.DiscardOldestPolicy());
public static void main(String[] args) throws Exception {
executor.setMaximumPoolSize(50);
for (; ; ) {
// 只进行一次modelFit调用
modelFit();
Thread.sleep(1000_000);
}
}
private static void modelFit() {
List<CardInfo> taskList = getAllCardInfo();
for (CardInfo info : taskList) {
executor.scheduleWithFixedDelay(() -> {
info.m();
}, 2, 3, TimeUnit.SECONDS);
}
}
private static List<CardInfo> getAllCardInfo() {
List<CardInfo> taskList = new ArrayList<>();
int andIncrement = integer.getAndIncrement();
System.out.println("------------------创建了 " + andIncrement + " 次list-----------------");
// 修改list大小为5
for (int i = 0; i < 5; i++) {
CardInfo ci = new CardInfo(integer2.getAndIncrement());
taskList.add(ci);
}
return taskList;
}
}
查看log日志
可以看到age=1的card对象,每隔3秒钟就会被执行一次。说明card对象一直被执行并且没有被释放。
因此,当modelFit
被不断调用的时候,card对象就会不断被创建,并且越来越多。
visualVM分析
public class T15_FullGC_Problem01 {
static AtomicInteger integer = new AtomicInteger(0);
static AtomicInteger integer2 = new AtomicInteger(0);
private static class CardInfo {
BigDecimal price = new BigDecimal(0.0);
String name = "张三";
int age = 5;
Date birthdate = new Date();
public CardInfo(int age) {
this.age = age;
}
public void m() {
System.out.println("crad的" + Thread.currentThread().getName() + "age是" + age + "---");
}
}
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();
for (CardInfo info : taskList) {
executor.scheduleWithFixedDelay(() -> {
info.m();
}, 2, 3, TimeUnit.SECONDS);
}
}
private static List<CardInfo> getAllCardInfo() {
List<CardInfo> taskList = new ArrayList<>();
int andIncrement = integer.getAndIncrement();
System.out.println("------------------创建了 " + andIncrement + " 次list-----------------");
for (int i = 0; i < 100; i++) {
CardInfo ci = new CardInfo(integer2.getAndIncrement());
taskList.add(ci);
}
return taskList;
}
}
在创建900多次list之后,card对象已经创建了8万多。
查看heapdump,此时cardInfo对象已经存在了8万对,并且没有被回收。
查看具体对象age标记,可以看到所有的cardInfo对象都没有被回收。