背景:
本人最近在做一个信令相关(电信行业的手机以及固话的通话记录)的项目,数据量非常大,之前也没碰到过这么大数据量,所以写程序的时候,没有意识到程序优化相关的知识,于是就出现了下面的程序。
程序一:
/** * @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
相关推荐
Java程序优化与数据竞争检测的研究 本文主要研究Java程序优化与数据竞争检测的技术,旨在提高Java程序的性能和可靠性。Java语言由于具有一定的安全性和高效性,在不同平台都得到广泛应用。为了增加Java程序在实际...
《Java程序性能优化:让你的Java程序更快、更稳定》以Java性能调优为主线,系统地阐述了与Java性能优化相关的知识与技巧。《Java程序性能优化:让你的Java程序更快、更稳定》共6章,先后从软件设计、软件编码、JVM调优...
标题:“Java程序优化大全.pdf”描述:“Java程序优化大全.pdf”标签:“技术及资料”内容:Java程序优化方面的内容,包含了对Java程序中常见问题的优化策略和方法。具体包括了对集合类的使用优化,如Vector的使用;...
《Java程序优化大全》是一本深入探讨Java代码优化的指南,旨在帮助开发者提升程序性能和效率。书中涵盖了许多关键的优化策略,以下是其中四个主要的知识点: 1. 避免在循环条件中使用复杂表达式: 在编写Java程序...
java程序性能优化Java是目前应用最为广泛的软件开发平台,学习针对Java程序的优化方法有重要的现实意义。《Java程序性能优化:让你的Java程序更快、更稳定》以Java性能调优为主线,系统地阐述了与Java性能优化相关的...
Java程序性能优化是每个开发人员都需要关注的重要领域,特别是在企业级应用中,高效稳定的Java程序能够显著提升用户体验,降低服务器资源消耗。这本书“Java程序性能优化 让你的Java程序更快、更稳定”提供了深入的...
根据提供的文件信息,我们可以推断出这是一本关于Java程序性能优化的书籍,作者是葛一鸣,并提供了该书PDF版本的下载链接。虽然没有具体的书籍内容,但基于标题、描述以及通常这类书籍会涉及的主题,我们可以总结出...
Java程序性能优化是每个开发人员都需要关注的重要领域,它涵盖了多个方面,旨在提高代码执行效率,减少资源消耗,以及提升应用程序的稳定性和响应速度。在本文中,我们将深入探讨Java性能优化的关键点,帮助你的Java...
Java代码优化是提升应用程序性能的关键步骤,尤其是在大型企业级应用或者高性能服务中。优化能够减少内存消耗,提高程序运行速度,降低CPU使用率,并改善整体的用户体验。在Java开发领域,有多种工具可以帮助开发者...
浅议Java程序优化的几种方法与成效.pdf
一个优秀的程序员,不仅要会编写程序,更要会编写高质量的程序,感受Java开发中的大智慧,让你的Java程序更优美 专注于Java应用程序的优化方法、技巧和思想,深入剖析软件设计层面、代码层面、JVM虚拟机层面的优化...
Java程序性能优化是每个开发人员都需要关注的重要领域,特别是在企业级应用中,高效稳定的Java程序能够带来显著的业务优势。本资源包含一个PDF文档和相关的视频教程,旨在帮助你提升Java程序的速度和稳定性。 首先...
Java程序中的内存分为堆内存(用于存储对象实例)、栈内存(用于存储方法调用)和方法区(存储类信息)。优化内存使用意味着最小化内存泄漏,及时释放不再使用的对象,以及合理配置堆大小。 3. **垃圾回收**:Java...
### JAVA程序性能优化 在Java开发中,程序性能优化是一个重要的环节,它直接影响到应用程序的运行效率、用户体验以及系统的整体稳定性。本文将基于提供的标题、描述及部分内容,深入探讨几个关键性的性能优化策略。...
通过这些工具和方法,开发者可以找出程序的瓶颈,从而针对性地优化代码。此外,书中还讲解了JDK自带的性能分析工具,如JConsole、VisualVM等,以及如何利用它们进行性能监控和诊断。 4. **《Java性能优化权威指南....