java优化占⽤内存的⼏种⽅法
java做的系统给⼈的印象是什么?占内存!说道这句话就会有N多⼈站出来为java辩护,并举出⼀堆的性能测试报告来证明这⼀点。其实从理论上来讲java做的系统并不⽐其他语⾔开发出来的系统更占⽤内存,那么为什么却有这么N多理由来证明它确实占内存呢?两个字,陋习。
(1)别⽤new Boolean()。
在很多场景中Boolean类型是必须的,⽐如JDBC中boolean类型的set与get都是通过Boolean封装传递的,⼤部分ORM也是⽤Boolean来封oolean类型的,⽐如:
ps.setBoolean("isClosed",new Boolean(true));
ps.setBoolean("isClosed",new Boolean(isClosed));
ps.setBoolean("isClosed",new Boolean(i==3));
通常这些系统中构造的Boolean实例的个数是相当多的,所以系统中充满了⼤量Boolean实例⼩对象,这是相当消耗内存的。Boolean类实际上只要两个实例就够了,⼀个true的实例,⼀个false的实例。
Boolean类提供两了个静态变量:
public static final Boolean TRUE = new Boolean(true);
public static final Boolean FALSE = new Boolean(false);
需要的时候只要取这两个变量就可以了,
⽐如:
ps.setBoolean("isClosed",Boolean.TRUE);
那么象2、3句那样要根据⼀个boolean变量来创建⼀个Boolean怎么办呢?可以使⽤Boolean提供的静态⽅法: Boolean.valueOf()
⽐如:
ps.setBoolean("isClosed",Boolean.valueOf(isClosed));
ps.setBoolean("isClosed",Boolean.valueOf(i==3));
因为valueOf的内部实现是:return (b ? TRUE : FALSE);
所以可以节省⼤量内存。相信如果Java规范直接把Boolean的构造函数规定成private,就再也不会出现这种情况了。
(2)别⽤new Integer。
和 Boolean类似,java开发中使⽤Integer封装int的场合也⾮常多,并且通常⽤int表⽰的数值通常都⾮常⼩。SUN SDK中对Integer的实例化进⾏了优化,Integer类缓存了-128到127这256个状态的Integer,如果使⽤ Integer.valueOf(int i),传⼊的int范围正好在此内,就返回静态实例。这样如果我们使⽤Integer.valueOf代替new Integer的话也将⼤⼤降低内存的占⽤。如果您的系统要在不同的SDK(⽐如IBM SDK)中使⽤的话,那么可以⾃⼰做了⼯具类封装⼀下,⽐如IntegerUtils.valueOf(),这样就可以在任何SDK中都可以使⽤这种特性。
(3)⽤StringBuffer代替字符串相加。这个我就不多讲了,因为已经被⼈讲过N次了。我只想将⼀个不是笑话的笑话,我在看国内某 “著
名”java开发的WEB系统的源码中,竟然发现其中⼤量的使⽤字符串相加,⼀个拼装SQL语句的⽅法中竟然最多构造了将近100个string实例。⽆语中!
(4)过滥使⽤哈希表,有⼀定开发经验的开发⼈员经常会使⽤hash表(hash表在JDK中的⼀个实现
就是HashMap)来缓存⼀些数据,从⽽提⾼系统的运⾏速度。⽐如使⽤HashMap缓存⼀些物料信息、⼈员信息等基础资料,这在提⾼系统速度的同时也加⼤了系统的内存占⽤,特别是当缓存的资料⽐较多的时候。其实我们可以使⽤操作系统中的缓存的概念来解决这个问题,也就是给被缓存的分配⼀个⼀定⼤⼩的缓存容器,按照⼀定的算法淘汰不需要继续缓存的对象,这样⼀⽅⾯会因为进⾏了对象缓存⽽提⾼了系统的运⾏效率,同时由于缓存容器不是⽆限制扩⼤,从⽽也减少了系统的内存占⽤。现在有很多开源的缓存实现项⽬,⽐如ehcache、oscache等,这些项⽬都实现了FIFO、MRU等常见的缓存算法。
(5)避免过深的类层次结构和过深的⽅法调⽤。因为这两者都是⾮常占⽤内存的(特别是⽅法调⽤更是堆栈空间的消耗⼤户)。
(6)变量只有在⽤到它的时候才定义和实例化。
(7)尽量避免使⽤static变量,类内私有常量可以⽤final来代替。
Java内存管理特点
Java⼀个最⼤的优点就是取消了指针,由垃圾收集器来⾃动管理内存的回收。程序员不需要通过调⽤函数来释放内存。
1、Java的内存管理就是对象的分配和释放问题。
在Java中,程序员需要通过关键字new为每个对象申请内存空间 (基本类型除外),所有的对象都在堆 (Heap)中分配空间。
对象的释放是由GC决定和执⾏的。
在Java中,内存的分配是由程序完成的,⽽内存的释放是有GC完成的,这种收⽀两条线的⽅法简化了程序员的⼯作。但也加重了JVM的⼯作。这也是Java程序运⾏速度较慢的原因之⼀。
GC释放空间⽅法:
监控每⼀个对象的运⾏状态,包括对象的申请、引⽤、被引⽤、赋值等。当该对象不再被引⽤时,释放对象。
2、内存管理结构
Java使⽤有向图的⽅式进⾏内存管理,对于程序的每⼀个时刻,我们都有⼀个有向图表⽰JVM的内存分配情况。
将对象考虑为有向图的顶点,将引⽤关系考虑为图的有向边,有向边从引⽤者指向被引对象。另外,每个线程对象可以作为⼀个图的起始顶点,例如⼤多程序从main进程开始执⾏,那么该图就是以main进程顶点开始的⼀棵根树。在这个有向图中,根顶点可达的对象都是有效对象,GC将不回收这些对象。如果某个对象 (连通⼦图)与这个根顶点不可达(注意,该图为有向图),那么我们认为这个(这些)对象不再被引⽤,可以被GC回收。
3、使⽤有向图⽅式管理内存的优缺点
Java使⽤有向图的⽅式进⾏内存管理,可以消除引⽤循环的问题,例如有三个对象,相互引⽤,只要它们和根进程不可达的,那么GC也是
可以回收它们的。
这种⽅式的优点是管理内存的精度很⾼,但是效率较低。
++:
另外⼀种常⽤的内存管理技术是使⽤计数器,例如COM模型采⽤计数器⽅式管理构件,它与有向图相⽐,精度⾏低(很难处理循环引⽤的问题),但执⾏效率很⾼。
★ Java的内存泄露
Java虽然由GC来回收内存,但也是存在泄露问题的,只是⽐C++⼩⼀点。
1、与C++的⽐较
c++所有对象的分配和回收都需要由⽤户来管理。即需要管理点,也需要管理边。若存在不可达的点,⽆法在回收分配给那个点的内存,导致内存泄露。存在⽆⽤的对象引⽤,⾃然也会导致内存泄露。
Java由GC来管理内存回收,GC将回收不可达的对象占⽤的内存空间。所以,Java需要考虑的内存泄露问题主要是那些被引⽤但⽆⽤的对象——即指要管理边就可以。被引⽤但⽆⽤的对象,程序引⽤了该对象,但后续不会再使⽤它。它占⽤的内存空间就浪费了。
如果存在对象的引⽤,这个对象就被定义为“活动的”,同时不会被释放。
2、Java内存泄露处理
处理Java的内存泄露问题:确认该对象不再会被使⽤。
典型的做法——
把对象数据成员设为null
从集合中移除该对象
注意,当局部变量不需要时,不需明显的设为null,因为⼀个⽅法执⾏完毕时,这些引⽤会⾃动被清理。
例⼦:
List myList=new ArrayList();
for (int i=1;i<100; i++)
{
Object o=new Object();
myList.add(o);
o=null;
}
//此时,所有的Object对象都没有被释放,因为变量myList引⽤这些对象。
当myList后来不再⽤到,将之设为null,释放所有它引⽤的对象。之后GC便会回收这些对象占⽤的内存。sql优化的几种方式
★对GC操作
对GC的操作并不⼀定能达到管理内存的效果。
GC对于程序员来说基本是透明的,不可见的。我们只有⼏个函数可以访问GC,例如运⾏GC的函数(),System.。
但是根据Java语⾔规范定义, ()函数不保证JVM的垃圾收集器⼀定会执⾏。因为,不同的JVM实现者可能使⽤不同的算法管理GC。通常,GC的线程的优先级别较低。
JVM 调⽤GC的策略有很多种,有的是内存使⽤到达⼀定程度时,GC才开始⼯作,也有定时执⾏的,有的是平缓执⾏GC,有的是中断式执⾏GC。但通常来说,我们不需要关⼼这些。除⾮在⼀些特定的场合,GC的执⾏影响应⽤程序的性能,例如对于基于Web的实时系统,如⽹络游戏等,⽤户不希望GC突然中断应⽤程序执⾏⽽进⾏垃圾回收,那么我们需要调整GC的参数,让GC能够通过平缓的⽅式释放内存,例如将垃圾回收分解为⼀系列的⼩步骤执⾏,Sun提供的 HotSpot JVM就⽀持这⼀特性。
★内存泄露检测
市场上已有⼏种专业检查Java内存泄漏的⼯具,它们的基本⼯作原理⼤同⼩异,都是通过监测Java程序运⾏时,所有对象的申请、释放等动作,将内存管理的所有信息进⾏统计、分析、可视化。开发⼈员将根据这些信息判断程序是否有内存泄漏问题。这些⼯具包括Optimizeit Profiler,JProbe Profiler,JinSight , Rational 公司的Purify等。
在运⾏过程中,我们可以随时观察内存的使⽤情况,通过这种⽅式,我们可以很快到那些长期不被释放,并且不再使⽤的对象。我们通过检查这些对象的⽣存周期,确认其是否为内存泄露。
★软引⽤
特点:只有当内存不够的时候才回收这类内存,同时⼜保证在Java抛出OutOfMemory异常之前,被设置为null。
保证最⼤限度的使⽤内存⽽不引起OutOfMemory异常。
在某些时候对软引⽤的使⽤会降低应⽤的运⾏效率与性能,例如:应⽤软引⽤的对象的初始化过程较为耗时,或者对象的状态在程序的运⾏过程中发⽣了变化,都会给重新创建对象与初始化对象带来不同程度的⿇烦。
⽤途:
可以⽤于实现⼀些常⽤资源的缓存,实现Cache的功能
处理⼀些占⽤内存⼤⽽且声明周期较长,但使⽤并不频繁的对象时应尽量应⽤该技术
★ java程序设计中有关内存管理的经验
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论