马老师jvm多线程oom习题

马老师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万多。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vDqdOknC-1595464889566)(67AE5525D12145F4B6A73F1FF6BA913F)]

查看heapdump,此时cardInfo对象已经存在了8万对,并且没有被回收。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fSROT7nS-1595464889567)(F0109F054448499FA049D806BAD9690A)]

查看具体对象age标记,可以看到所有的cardInfo对象都没有被回收。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XXToMhSZ-1595464889568)(9D03F556C4DF4DAE859EE060B307DCF6)]

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值