- 浏览: 176987 次
- 性别:
- 来自: 福州
文章分类
最新评论
-
vil:
请问CAS 单点登录加负载均衡后,httpClient tim ...
(转)CAS负载均衡配置 -
bcs2099:
男男女女[flash=200,200][/flash]
学Ext的好网站 -
bcs2099:
不过关
学Ext的好网站 -
qingteng1983:
请问,这个上传组件支持断点续传吗?
利用SWFUpload实现大附件上传 -
yangwn:
LZ,我想请教,hdfs://192.168.4.27:900 ...
编写JAVA代码读取Hadoop存储服务器文件
4 内存控制效率优化的启示
内存控制效率优化说起来简单做起来难,真正能做到优化,必须从点滴做起,并利用有效的手段加以应用。现在就看看对于控制内存方面都有哪些启示吧!
4.1 启示1:String和StringBuffer的不同之处
相信大家都知道String和StringBuffer之间是有区别的,但究竟它们之间的区别在哪里下面就在本小节中一探究竟,看看能给我们些什么启示。还是刚才那个程序,把它改一改,将本程序中的String进行无限次的累加,看看什么时候抛出内存超限的异常,程序如下所示。
public class MemoryTest{
public static void main(String args[]){
String s="abcdefghijklmnop";
System.out.print("当前虚拟机最大可用内存为:");
System.out.println(Runtime.getRuntime().maxMemory()/1024/1024+"M");
System.out.print("环前,虚拟机已占用内存:");
System.out.println(Runtime.getRuntime().totalMemory()/1024/1024+"M");
int count = 0;
while(true){
try{
s+=s;
count++;
}
catch(Error o){
System.out.println("环次数:"+count);
System.out.println("String实际字节数:"+s.length()/1024/1024+"M");
System.out.print("环后,已占用内存:");
System.out.println(Runtime.getRuntime().totalMemory()/1024/ 1024+"M");
System.out.println("Catch到的错误:"+o);
break;
}
}
}
}
程序运行后,果然不一会儿的功夫就报出了异常,如图 11-3所示。
图 11-3
可以注意到,在String的实际字节数只有8MB的情况下,环后已占内存数竟然已达到了63.56MB。这说明,String这个对象的实际占用内存数量与其自身的字节数不相符。于是,在环19次的时候就已报OutOfMemoryError的错误了。
因此,应该少用String这东西,特别是 String的“+=”操作。不仅来的String对象不能继续使用,而且又要产生多个新对象,会较高的占用内存,所以必须要改用StringBuffer来实现相应目的。下面是改用StringBuffer来做的测试。
public class MemoryTest{
public static void main(String args[]){
StringBuffer s=new StringBuffer("abcdefghijklmnop");
System.out.print("当前虚拟机最大可用内存为:");
System.out.println(Runtime.getRuntime().maxMemory()/1024/1024+"M");
System.out.print("环前,虚拟机已占用内存:");
System.out.println(Runtime.getRuntime().totalMemory()/1024/1024+"M");
int count = 0;
while(true){
try{
s.append(s);
count++;
}
catch(Error o){
System.out.println("环次数:"+count);
System.out.println("String实际字节数:"+s.length()/1024/1024+"M");
System.out.println("环后,已占用内存:");
System.out.println(Runtime.getRuntime().totalMemory()/1024/1024+"M");
System.out.println("Catch到的错误:"+o);
break;
}
}
}
}
将String改为StringBuffer以后,在运行时得到了如下结果,如图 11-4所示。
图 11-4
这次可以发现,当StringBuffer所占用的实际字节数为16M的时候才产生溢出,整整比上一个程序的String实际字节数8MB多了一倍。
4.2 启示2:用-Xmx参数来提高内存可控制量
前面介绍过-Xmx这个参数的用法,如果还是处理刚才的那个用StringBuffer的Java程序,则用-Xmx1024m来启动它,看看它的环次数有什么变化。
输入如下指令。
java –mx1024m MemoryTest
得到结果如图 11-5所示。
图 11-5
通过使用-Xmx参数将其可控内存量扩大至1024MB后,这个程序到了1GB的时候内存才超限,从而使内存的可控性提高了。但扩大内存使用量永远不是最终的解决方案,如果你的程序没有去更加地优化,早晚还是会超限的。
4.3 启示3:二维数组比一维数组占用更多内存空间
对于内存占用的问题,还有一个地方值得注意,就是二维数组的内存占用问题。
有时候我们一厢情愿地认为:
二维数组的占用内存空间多无非就是二维数组的实际数组元素数比一维数组多而已,那么二维数组的所占空间一定是实际申请的元素数而已。
事实上并不是这样的,对于一个二维数组而言,它所占用的内存空间要远远大于它开辟的数组元素数。下面来看一个一维数组程序的例子。
public class MemFor{
public static void main (String[] args) {
try{
int len=1024*1024*2; //设定环次数
byte [] abc=new byte[len];
for (int i=0;i<len;i++){
abc[i]=(byte)i;
}
System.out.print("已占用内存:");
System.out.println(
Runtime.getRuntime().totalMemory()/1024/1024+"M");
}
catch(Error e){
}
}
}
这个程序是开辟了1024×1024×2即2MB的数组元素的一维数组,运行这个程序得到的结果如图 11-6所示,程序运行结果提示“已占用内存:3MB”。
图 11-6
下面再将这个程序进行修改,改为一个二维数组,这个二维数组的元素数量也尽量地和上一个一维数组的元素数量保持一致。
public class MemFor{
public static void main (String[] args) {
try{
int len=1024*1024; //设定环次数
byte [][] abc=new byte[len][2];
for (int i=0;i<len;i++){
abc[i][0]=(byte)i;
abc[i][1]=(byte)i;
}
System.out.print("已占用内存:");
System.out.println(
Runtime.getRuntime().totalMemory()/1024/1024+"M");
}
catch(Error e){
}
}
}
当申请的元素数量未变,只是将二维数组的行数定为1024×1024、列数定为2,和刚才的那个一维数组1024×1024×2的数量完全一致,但得到的运算结果如图 11-7所示,竟然占用了29MB的空间。
图 11-7
姑且不管造成这种情况的因,只要知道一点就够了,那就是“二维数组占内存”。所以在编写程序的时候要注意,能不用二维数组的地方尽量用一维数组,要求内存占用小的地方尽量用一维数组。
4.4 启示4:用HashMap提高内存查询速度
有一本《大学计算机应用基础》中是这样描述内存的。
DRAM:即内存条。常说的内存并不是内部存储器,而是DRAM。
CPU的运行速度很快,而外部存储器的读取速度相对来说就很慢,如果CPU需要用到的数据总是从外部存储器中读取的,由于外部设备很慢,CPU可能用到的数据预先读到DRAM中,CPU产生的临时数据也暂时存放在DRAM中,这样的结果是大大地提高了CPU的利用率和计算机运行速度。
这是一个典型计算机基础教材针对内存的描述,也许作为计算机专业的程序员,对这段描述并不陌生。但也因为这段描述而对内存的处理速度有神话的理解,认为内存中的处理速度是非常快的。以使持有这种观点的程序员遇到一个巨型的内存查询环的较长时间时,就束手无策了。
看一下如下程序。
public class MemFor{
public static void main (String[] args) {
long start=System.currentTimeMillis(); //取得当前时间
int len=1024*1024*3; //设定环次数
int [][] abc=new int[len][2];
for (int i=0;i<len;i++){
abc[i][0]=i;
abc[i][1]=(i+1);
}
long get=System.currentTimeMillis(); //取得当前时间
//环将想要的数值取出来,本程序取数组的最后一个值
for (int i=0;i<len;i++){
if ((int)abc[i][0]==(1024*1024*3-1)){
System.out.println("取值结果:"+abc[i][1]);
}
}
long end=System.currentTimeMillis(); //取得当前时间
//输出测试结果
System.out.println("赋值环时间:"+(get-start)+"ms");
System.out.println("获取环时间:"+(end-get)+"ms");
System.out.print("Java可控内存:");
System.out.println(Runtime.getRuntime().maxMemory()/1024/1024+"M");
System.out.print("已占用内存:");
System.out.println(Runtime.getRuntime().totalMemory()/1024/1024+"M");
}
}
运行这个程序。
java -Xmx1024m MemFor
程序的运行结果如下。
取值结果:3145728。
赋值环时间:2464ms。
获取环时间:70ms。
Java可控内存:1016MB。
已占用内存:128MB。
可以发现,这个程序环了3145728次获得想要的结果,环获取数值的时间用了70毫秒。
你觉得快吗
是啊,70毫秒虽然小于1秒钟,但是如果你不得不在这个环外面再套一个环,即使外层嵌套的环只有100次,那么想想看是多少毫秒呢
回答:70毫秒×100=7000毫秒=7秒
如果,环1000次呢
70秒!
70秒的运行时间对于这个程序来说就是灾难了。
面对这个程序的运行时间,很多程序员已束手无策了,其实Java给程序员们提供了一个较快的查询方法——哈希表查询。
下面将这个程序用HashMap来改造一下,再看看运行结果。
import java.util.*;
public class HashMapTest{
public static void main (String[] args) {
HashMap has=new HashMap();
int len=1024*1024*3;
long start=System.currentTimeMillis();
for (int i=0;i<len;i++){
has.put(""+i,""+i);
}
long end=System.currentTimeMillis();
System.out.println("取值结果:"+has.get(""+(1024*1024*3-1)));
long end2=System.currentTimeMillis();
System.out.println("赋值环时间:"+(end-start)+"ms");
System.out.println("获取环时间:"+(end2-end)+"ms");
System.out.print("Java可控内存:");
System.out.println(Runtime.getRuntime().maxMemory()/1024/1024+"M");
System.out.print("已占用内存:");
System.out.println(Runtime.getRuntime().totalMemory()/1024/1024+"M");
}
}
运行这个程序。
java -Xmx1024m HashMapTest
程序的运行结果如下。
取值结果:3145727。
赋值环时间:16454ms。
获取环时间:0ms。
Java可控内存:1016MB。
已占用内存:566MB。
那么现在用HashMap来取值的时间竟然不到1ms,这时的程序的效率明显提高了。看来用哈希表进行内存中的数据搜索的速度确实很快。
在提高数据搜索速度的同时也要注意到,赋值时间的差异和内存占用的差异。
赋值环时间:
HashMap:16454ms。
普通数组:2464ms。
占用内存:
HashMap:566MB。
普通数组:128MB。
可以看出,HashMap在初始化以及内存占用方面都要高于普通数组。如果仅仅是为了数据存储,用普通数组是比较适合的,但是如果为了频繁查询的目的,HashMap是必然的选择。
4.5 启示5:用arrayCopy()提高数组截取速度
当需要处理一个大的数组应用时,往往需要对数组进行大面积的截取与复制操作,比如针对图形显示的应用时,单纯地通过对数组元素的处理操作有时捉襟见肘。
提高数组处理速度的一个很好的方法是使用System.arrayCopy(),它可以提高数组的截取速度。下面可以做一个对比试验。
例如用普通的数组赋值方法来处理,程序如下。
public class arraycopyTest1{
public static void main( String[] args ){
String temp="abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"
+"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"
+"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"
+"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"
+"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"
+"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"
+"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"
+"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"
+"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"
+"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz";
char[] oldArray=temp.toCharArray();
char[] newArray=null;
long start=0L;
newArray=new char[length];
//开始时间记录
start=System.currentTimeMillis();
for(int i=0;i<10000000;i++){
for(int j=0;j<length;j++){
newArray[j]=oldArray[begin+j];
}
}
//打印总用时间
System.out.println(System.currentTimeMillis()-start+”ms”);
}
}
程序运行结果如图 11-8所示。
图 11-8
那么下面再用arrayCopy()的方法试验一下。
public final class arraycopyTest2{
public static void main( String[] args ){
String temp="abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"
+"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"
+"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"
+"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"
+"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"
+"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"
+"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"
+"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"
+"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"
+"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz";
char[] oldArray=temp.toCharArray();
char[] newArray=null;
long start=0L;
newArray=new char[length];
//记录开始时间
start=System.currentTimeMillis();
for(int i=0;i<10000000;i++ ){
System.arraycopy(oldArray,100,newArray,0,120);
}
//打印总用时间
System.out.println((System.currentTimeMillis()-start)+”ms”);
}
}
程序运行结果如图 11-9所示。
图 11-9
两个程序的差距为3秒多,如果处理更大批量的数组,它们的差距还会更大。因此,可以在适当的情况下用这个方法来处理数组的问题。
有时候为了使用方便,可以在自己的tools包中重载一个arrayCopy方法,程序如下。
public static Object[] arrayCopy(int pos,Object[] srcObj){
return arrayCopy(pos,srcObj.length,srcObj);
}
public static Object[] arrayCopy(int pos,int dest,Object[] srcObject){
Object[] rv=null;
rv=new Object[dest-pos];
System.arraycopy(srcObject,pos,rv,0,rv.length);
return rv;
}
发表评论
-
CAS 3.x代理配置
2010-02-04 16:04 2695http://fallenlord.blogbus.com/l ... -
(转)CAS负载均衡配置
2010-01-28 09:38 8111http://fallenlord.blogbus.com ... -
获取汉字拼音首字母功能函数
2010-01-18 14:52 968如: System.out.println(HZPY. ... -
JAVA MVC构架详解
2009-12-22 15:04 997整理。。。 -
Web服务端组件事件开发与MVC开发
2009-12-19 11:14 1257本周一起跟.net开 ... -
记录下载中文附件出现乱码
2009-10-28 14:16 877环境:jsp页面,编码全部用UTF-8 在下载代码中写 ... -
web.xml 中 mime-mapping的使用
2009-10-08 09:58 6101mime-mapping 如果Web应用程序包含一 ... -
从Struts到Spring MVC的应用
2009-09-27 11:42 1485从 Struts 转到 Spring MVC 的应用 ... -
容易被搞晕的——堆和栈
2009-09-23 11:10 773容易被搞晕的——堆和 ... -
Tomcat的配置文件server.xml 中各个域的说明
2009-07-09 15:10 811Tomcat的配置文件server.xml 中各个域的说明 ... -
让CAS支持客户端自定义登陆页面——客户端篇
2009-06-19 17:08 3514客户端即指使用CAS中央认证服务器的应用程序,而不是指用户浏览 ... -
让CAS支持客户端自定义登陆页面——服务器篇
2009-06-19 16:40 2067修改需要基于几个基本原则: 不影响原有统一登陆界面功能 ... -
将页面输出进行压缩
2009-06-15 16:07 887压缩是解决数据冗余的一个好的方法,特别是在网络带宽 ... -
在spring mvc中用 jquery 出现的在IE,firefox乱码问题解决
2009-06-11 19:47 1708由于在开发中大量用GB2312出现此问题,解决方法可以有下方法 ... -
Hibernate中分页查询在SQL Server2005产生的SQL语句
2009-05-26 15:18 1637Hibernate 的分页代码 query.setFi ... -
不要重复 DAO!
2009-04-16 17:03 827http://www.ibm.com/developerwor ... -
jquery异步附件上传
2009-03-30 20:30 4233http://valums.com/wp-content/up ... -
JAVA中不错的处理日期工具单元
2009-02-10 10:54 977/**Revision Information: *@ver ... -
MyEclipse6.5注册码
2009-01-12 13:42 741Subscriber:QQ24785490 Subscrip ... -
Hibernate3.2官方中文参考手册
2009-01-09 16:37 1832如附件:
相关推荐
计算机行业产业互联网专题_工业篇2:Aspen,工业优化控制软件龙头启示
20201218-中信证券-计算机行业产业互联网专题~工业篇2:Aspen,工业优化控制软件龙头启示.pdf
德国大众公司的财务控制及其启示.doc
DOOM启示录 DOOM启示录 DOOM启示录 DOOM启示录 DOOM启示录 DOOM启示录 DOOM启示录 DOOM启示录 DOOM启示录 DOOM启示录 DOOM启示录
论文研究-控制理论发展过程的启示.pdf, 本文分三部分:第一部分分析了控制理论发展的历史,指出控制理论的任何重大进展均取决于当时社会生产力(包括先进技术)的需要和人类已有的知识(包括技术手段)的准备,并以此分析...
性能 优化时常用的工具优化小技巧 转储文件内容3:转储控制文件5月18日 Top SQL优化小技巧 查找Top SQL优化小技巧 索引设计的一些建议优化小技巧 什么是索引的集群因子优化小技巧 转储文件内容4:转储控制文件5月19...
能源效率政策:国内外对比与启示,查冬兰,,能源效率的提高除了依靠市场经济行为中技术的进步和经济结构的转变,还需要相应的政策来推进。各工业化国家和发展中国家纷纷制定
支线旅游机场的成本控制启示
国际深空轨迹优化竞赛及其启示,李俊峰,祝开建,结合本课题组连续三次参加国际深空轨迹优化竞赛的经验,依据竞赛主办单位公布的竞赛资料和相关国际学术研讨会的报告,本文分别介
美国政府内部控制的特征及启示.docx
基于雁群启示的粒子群优化算法.pdf
为确保顺和选煤厂工艺设备的先进性,对初步设计方案进行优化,详细叙述了优化后工艺设备、厂房与工业广场布置、产品装车方式及煤泥水处理系统情况,实现了中型无烟煤选煤厂布局合理、投资节约、工艺先进、产能扩充的...
急救医疗体系运行机制优化研究进展及启示.rar
急救医疗体系运行机制优化研究进展及启示.pdf
DOOM启示录 DOOM启示录 DOOM启示录
德国医疗模式对优化我国医患信任关系的启示.pdf
doom启示录.txt
Windows编程启示录.pdf Windows编程启示录.pdf
国家开发银行IT服务外包风险控制的经验及启示.pdf
介绍了大量关于现代启示录详细内容现代启示录详细内容现代启示录详细内容