`

Java程序优化

    博客分类:
  • java
阅读更多

 

背景:

 

本人最近在做一个信令相关(电信行业的手机以及固话的通话记录)的项目,数据量非常大,之前也没碰到过这么大数据量,所以写程序的时候,没有意识到程序优化相关的知识,于是就出现了下面的程序。

 

程序一:

 

 

    /**
     * @description: 将字符串解析为List<SignalData>对象
     */
    public static List<SignalData> parseData(String data) {
        String[] listStr = data.split("\r\n");  //data中有多条记录,每条以\r\n换行,每条记录中存在着固定的五个字段的信息,每个字段以,隔开。要求去除数据,装入对象
        List<SignalData> result = new LinkedList<SignalData>();
        for (String dataStr : listStr) {
            String[] fieldArray = dataStr.split(",");
            if (fieldArray.length < 5) {
                continue;
            }
            SignalData item = new SignalData();    
            for (int i = 0; i < fieldArray.length; i++) {  //待优化
                if (i == 0) {
                    item.setAreaCode(fieldArray[i]);
                    continue;
                }
                if (i == 1) {
                    item.setCallingNum(fieldArray[i]);
                    continue;
                }
                if (i == 2) {
                    item.setCalledNum(fieldArray[i]);
                    continue;
                }
                if (i == 3) {
                    item.setCallType(fieldArray[i]);
                    continue;
                }
                if (i == 4) {
                    item.setSignallingTime(fieldArray[i]);
                    continue;
                }
            }
            result.add(item);
        }
        return result;
    }

 

 

上面的代码,猛一看,for循环体中还有continue,感觉像是优化的程序。 于是就放上去运行了,之后又看了看,突然灵光一闪,感觉不对劲,既然每个字段都有数据,我为什么不直接从数组中取呢,还要循环,真是自己作死啊,好,发现一处问题,这样的程序我都看不下去了,于是赶紧优化了下,于是出现下面的代码;

 

优化后:

 

 

    /**
     * @description: 将字符串解析为List<SignalData>对象
     */
    public static List<SignalData> parseData(String data) {
        String[] listStr = data.split("\r\n");
        List<SignalData> result = new LinkedList<SignalData>();
        
        for (String dataStr : listStr) {
            if(null==dataStr||dataStr.trim().equals("")) continue;
            String[] fieldArray = dataStr.split(",");
            if (fieldArray.length < 5) {
                continue;
            }
            SignalData item = new SignalData();
            item.setAreaCode(fieldArray[0]);  //优化之后
            item.setCallingNum(fieldArray[1]);
            item.setCalledNum(fieldArray[2]);
            item.setCallType(fieldArray[3]);
            item.setSignallingTime(fieldArray[4]);
            result.add(item);
        }
        return result;
    }

 

 

 

通过这个程序我大概算了一下,平均每天8G数据,每条数据50byte,  总共是 20 * 1024 *  1024  *  8 = 167772160 条记录, 而上面的每个循环体在取数据的时候,优化之前和优化之后,在判断数组下标相等的时候每条数据总共要花掉15次, 于是  20 * 1024 *  1024  *  8  *   15 = 2516582400 ,看着数字,我的内心是崩溃的,自己的失误,每天造成多了这么多次的运算,不仅程序变慢,还要要多浪费多少电,啊啊啊啊!

 

 

 

程序二:

 

 

优化前:

 

 

    @Override
    public void consume(ConsumerRecord<String, String> record) {
        try {
            //这里面是从kafka中pull数据,
            // pushPool 是一个静态的线程池对象,ParseAndPushTask是一个线程,record.value()就是上面的程序中的一批数据记录,
            pushPool.execute(new ParseAndPushTask(record.value()));  // 问题部分
        } catch (Exception e) {
            logger.error("error in PushBusinessTreate:", e);
        }
    }

 

 

 

这段程序,是从kafka中拉的数据,数据量就是上面的量,当写出这个程序,发布之后,直接OOM了。出现OOM赶紧停掉服务,想了一下,数据量这么大,每pull一条数据,都会生成一个线程,线程中的成员变量,也就是数据,至少在2kb的数据,也就是说,每条记录都要在堆内存中开辟2kb的数据,加上数据量大,不OOM都不正常了。。。。。

 

 

优化后:

 

 

    @Override
    public void consume(ConsumerRecord<String, String> record) {
        try {
		PushService.receiveDataFromProducer(record.value());   //这里使用了一个静态方法
		} catch (Exception e) {
			logger.error("error in PushBusinessTreate:", e);
		}
    }

 

 

修改之后,运行正常,这样,又节省了无数次的GC 啊 

 

 

程序三:

 

 

    public static List<AccountAndSubInfoVo> queryPushSubscriberData(Set<String> subscriptionIds) throws Exception {
        List<AccountAndSubInfoVo> result = new LinkedList<AccountAndSubInfoVo>();
        for (String subscriptionId : subscriptionIds) {
            String subscriberId = jedisCluster.get(Constants.SUBSCRIPTION + subscriptionId); // 获取订阅者ID
            if (subscriberId == null || "".equals(subscriberId)) {
                continue;
            }
            Map<String, String> map = jedisCluster.hgetAll(Constants.SUBSCRIBER + subscriberId);
            if (map != null) {
                AccountAndSubInfoVo vo = mapToBean(map, AccountAndSubInfoVo.class);  //待优化地方**********
                vo.setSubscriptionId(subscriptionId);
                vo.setSubscriberId(subscriberId);
                result.add(vo);
            }

        }

        return result;
    }
    
    public static <T> T mapToBean(Map<String, String> map, Class<T> obj) throws Exception {
        if (map == null) {
            return null;
        }
        Set<Entry<String, String>> sets = map.entrySet();
        T t = obj.newInstance();
        Method[] methods = obj.getDeclaredMethods();
        for (Entry<String, String> entry : sets) {
            String str = entry.getKey();
            String setMethod = "set" + str.substring(0, 1).toUpperCase() + str.substring(1);
            for (Method method : methods) {
                if (method.getName().equals(setMethod)) {
                    method.invoke(t, entry.getValue());
                }
            }
        }
        return t;
    }

 

 

 

 上面的情况大概是这样的,从redis中取出相关的数据(redis中的数据类型名称已经确定),取出形式为Map类型,然后呢,我需要把Map类型转换为对象类型,之前一般情况下都是写个反射,或者是调用一些API搞定,这次也是同样,但是看了看代码,觉得需要优化,因为反射技术本身就损耗性能,况且我是知道Map中的Key的,而且字段也不多,更有效的方法就是,去掉反射,用常规的setter方法,于是,出现下面的代码

 

优化后:

 

 

    public static List<AccountAndSubInfoVo> queryPushSubscriberData(Set<String> subscriptionIds) throws Exception {
        List<AccountAndSubInfoVo> result = new LinkedList<AccountAndSubInfoVo>();
        
        for (String subscriptionId : subscriptionIds) {
            String subscriberId = jedisCluster.get(Constants.SUBSCRIPTION + subscriptionId); // 获取订阅者ID
            if (subscriberId == null || "".equals(subscriberId)) {
                continue;
            }
            Map<String, String> map = jedisCluster.hgetAll(Constants.SUBSCRIBER + subscriberId);
            if (map != null) {
//                AccountAndSubInfoVo vo = mapToBean(map, AccountAndSubInfoVo.class); 优化程序,反射影响性能 ,
                AccountAndSubInfoVo vo = new AccountAndSubInfoVo();
                vo.setAppId(map.get(Constants.APP_ID));  //定义的常量字符串,下同
                vo.setAppSecret(map.get(Constants.APP_SECRET));
                vo.setPushUrl(map.get(Constants.PUSH_URL));
                vo.setSubscriptionId(subscriptionId);
                vo.setSubscriberId(subscriberId);
                result.add(vo);
            }

        }

        return result;
    }

 

 

 

 我想,这样也肯定会省掉很多时间。

 

 

总结:

 

 

通过上面的程序,让我深深的意识到了,平时在写完代码,review一把是多么的靠谱,不但会让程序更流畅一些,最重要的的是会省很多电,节省能源!以前写Web程序,数据量不大,这些问题也没有注意到,当量发生了变化之后,许多问题都会暴漏出来,大家在看到我的程序之后,都看看自己写的程序有没有类似的问题,有则改之无则加勉,这次特意写出来,分享给大家!

 

 

 

 

 下面分享一个很好的关于java程序优化的链接:

 

http://www.cnblogs.com/chinafine/articles/1787118.html

 

 

 

 

 

 

 

6
4
分享到:
评论
8 楼 henu_zhangyang 2016-03-11  
ruyi574812039 写道
for循环记录数过大,考虑多线程也好吧。。

7 楼 henu_zhangyang 2016-03-11  
java_workblog 写道
看完这个博客,里面回去查查自己的程序

O(∩_∩)O哈哈~
6 楼 henu_zhangyang 2016-03-11  
hq2999 写道
如果是用在移动设备上会更有价值

哪里都一样
5 楼 henu_zhangyang 2016-03-11  
bewithme 写道
太逗了,省电强迫症

要有环保意识
4 楼 ruyi574812039 2016-03-11  
for循环记录数过大,考虑多线程也好吧。。
3 楼 java_workblog 2016-03-10  
看完这个博客,里面回去查查自己的程序
2 楼 hq2999 2016-03-10  
如果是用在移动设备上会更有价值
1 楼 bewithme 2016-03-09  
太逗了,省电强迫症

相关推荐

    Java程序优化与数据竞争检测的研究.pdf

    Java程序优化与数据竞争检测的研究 本文主要研究Java程序优化与数据竞争检测的技术,旨在提高Java程序的性能和可靠性。Java语言由于具有一定的安全性和高效性,在不同平台都得到广泛应用。为了增加Java程序在实际...

    Java程序性能优化

    《Java程序性能优化:让你的Java程序更快、更稳定》以Java性能调优为主线,系统地阐述了与Java性能优化相关的知识与技巧。《Java程序性能优化:让你的Java程序更快、更稳定》共6章,先后从软件设计、软件编码、JVM调优...

    Java程序优化大全.pdf

    标题:“Java程序优化大全.pdf”描述:“Java程序优化大全.pdf”标签:“技术及资料”内容:Java程序优化方面的内容,包含了对Java程序中常见问题的优化策略和方法。具体包括了对集合类的使用优化,如Vector的使用;...

    Java程序优化大全[参照].pdf

    《Java程序优化大全》是一本深入探讨Java代码优化的指南,旨在帮助开发者提升程序性能和效率。书中涵盖了许多关键的优化策略,以下是其中四个主要的知识点: 1. 避免在循环条件中使用复杂表达式: 在编写Java程序...

    java程序性能优化

    java程序性能优化Java是目前应用最为广泛的软件开发平台,学习针对Java程序的优化方法有重要的现实意义。《Java程序性能优化:让你的Java程序更快、更稳定》以Java性能调优为主线,系统地阐述了与Java性能优化相关的...

    Java程序性能优化 让你的Java程序更快、更稳定附本书示例代码(清晰版)

    Java程序性能优化是每个开发人员都需要关注的重要领域,特别是在企业级应用中,高效稳定的Java程序能够显著提升用户体验,降低服务器资源消耗。这本书“Java程序性能优化 让你的Java程序更快、更稳定”提供了深入的...

    《Java程序性能优化》(葛一鸣)PDF版本下载.txt

    根据提供的文件信息,我们可以推断出这是一本关于Java程序性能优化的书籍,作者是葛一鸣,并提供了该书PDF版本的下载链接。虽然没有具体的书籍内容,但基于标题、描述以及通常这类书籍会涉及的主题,我们可以总结出...

    Java程序性能优化 让你的Java程序更快、更稳定

    Java程序性能优化是每个开发人员都需要关注的重要领域,它涵盖了多个方面,旨在提高代码执行效率,减少资源消耗,以及提升应用程序的稳定性和响应速度。在本文中,我们将深入探讨Java性能优化的关键点,帮助你的Java...

    JAVA代码优化工具

    Java代码优化是提升应用程序性能的关键步骤,尤其是在大型企业级应用或者高性能服务中。优化能够减少内存消耗,提高程序运行速度,降低CPU使用率,并改善整体的用户体验。在Java开发领域,有多种工具可以帮助开发者...

    浅议Java程序优化的几种方法与成效.pdf

    浅议Java程序优化的几种方法与成效.pdf

    《Java程序性能优化:让你的Java程序更快、更稳定》完整扫描PDF版网盘链接

    一个优秀的程序员,不仅要会编写程序,更要会编写高质量的程序,感受Java开发中的大智慧,让你的Java程序更优美 专注于Java应用程序的优化方法、技巧和思想,深入剖析软件设计层面、代码层面、JVM虚拟机层面的优化...

    Java程序性能优化 让你的Java程序更快、更稳定pdf文档视频资源

    Java程序性能优化是每个开发人员都需要关注的重要领域,特别是在企业级应用中,高效稳定的Java程序能够带来显著的业务优势。本资源包含一个PDF文档和相关的视频教程,旨在帮助你提升Java程序的速度和稳定性。 首先...

    java优化编程 java优化编程 java优化编程

    Java程序中的内存分为堆内存(用于存储对象实例)、栈内存(用于存储方法调用)和方法区(存储类信息)。优化内存使用意味着最小化内存泄漏,及时释放不再使用的对象,以及合理配置堆大小。 3. **垃圾回收**:Java...

    JAVA程序性能优化

    ### JAVA程序性能优化 在Java开发中,程序性能优化是一个重要的环节,它直接影响到应用程序的运行效率、用户体验以及系统的整体稳定性。本文将基于提供的标题、描述及部分内容,深入探讨几个关键性的性能优化策略。...

    4本高清中文版Java性能优化经典书籍

    通过这些工具和方法,开发者可以找出程序的瓶颈,从而针对性地优化代码。此外,书中还讲解了JDK自带的性能分析工具,如JConsole、VisualVM等,以及如何利用它们进行性能监控和诊断。 4. **《Java性能优化权威指南....

Global site tag (gtag.js) - Google Analytics