`

高性能JAVA开发之内存管理[转]

阅读更多
一、JVM中的对象生命周期

对象的生命周期一般分为7个阶段:创建阶段,应用阶段,不可视阶段,不可到达阶段,可收集阶段,终结阶段,释放阶段。

创建阶段,首先大家看一下,如下两段代码:
test1:

for( int i=0; i<10000; i++)

Object obj=new Object();

test2:

Object obj=null;

for( int i=0; i<10000; i++)

obj=new Object();

这两段代码都是相同的功能,但是显然test2的性能要比test1性能要好,内存使用率要高,这是为什么呢?原因很简单,test1每次执行for循环都要创建一个Object的临时对象,但是这些临时对象由于JVM的GC不能马上销毁,所以他们还要存在很长时间,而test2则只是在内存中保存一份对象的引用,而不必创建大量新临时变量.从而降低了内存的使用.

另外不要对同一个对象初始化多次.例如:
public class A{

private Hashtable table = new Hashtable();

  public A(){

     table = new Hashtable(); // 这里应该去掉,因为table已经被初始化.

  }

}



这样就new了两个Hashtable,但是却只使用了一个.另外一个则没有被引用.而被忽略掉.浪费了内存.并且由于进行了两次new操作.也影响了代码的执行速度。

应用阶段:即该对象至少有一个引用在维护他。

不可视阶段:即超出该变量的作用域.这里有一个很好的做法,因为JVM在GC的时候并不是马上进行回收,而是要判断对象是否被其他引用在维护.所以,这个时候如果我们在使用完一个对象以后对其obj=null或者obj.doSomething()操作,将其标记为空,可以帮助JVM及时发现这个垃圾对象。

不可到达阶段:就是在JVM中找不到对该对象的直接或者间接的引用。

可收集阶段,终结阶段,释放阶段:此为回收器发现该对象不可到达,finalize方法已经被执行,或者对象空间已被重用的时候。

二、java内存管理特点

Java一个最大的优点就是取消了指针,由垃圾收集器来自动管理内存的回收。程序员不需要通过调用函数来释放内存。

(1)Java的内存管理就是对象的分配和释放问题。

在Java中,内存的分配是由程序完成的,而内存的释放是有GC完成的,这种收支两条线的方法简化了程序员的工作。但也加重了JVM的工作。这也是Java程序运行速度较慢的原因之一。

在Java中,程序员需要通过关键字new为每个对象申请内存空间 (基本类型除外),所有的对象都在堆 (Heap)中分配空间,对象的释放是由GC决定和执行的。

GC释放空间方法:监控每一个对象的运行状态,包括对象的申请、引用、被引用、赋值等。当该对象不再被引用时,释放对象,但程序中对GC的操作并不一定能达到管理内存的效果,GC对于程序员来说基本是透明的,不可见的。我们只有几个函数可以访问GC,例如运行GC的函数System.gc()。但是根据Java语言规范定义,System.gc()函数不保证JVM的垃圾收集器一定会执行。因为,不同的JVM实现者可能使用不同的算法管理GC。通常,GC的线程的优先级别较低,而且强制内存回收对于系统自动的内存回收机制会产生负面影响,会加大系统自动回收的处理时间,所以应该尽量避免显式使用System.gc(),

JVM调用GC的策略有很多种,有的是内存使用到达一定程度时,GC才开始工作,也有定时执行的,有的是平缓执行GC,有的是中断式执行GC。但通常来说,我们不需要关心这些。除非在一些特定的场合,GC的执行影响应用程序的性能,例如对于基于Web的实时系统,如网络游戏等,用户不希望GC突然中断应用程序执行而进行垃圾回收,那么我们需要调整GC的参数,让GC能够通过平缓的方式释放内存,例如将垃圾回收分解为一系列的小步骤执行,Sun提供的HotSpot JVM就支持这一特性。

(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便会回收这些对象占用的内存。

(3)内存泄露检测

市场上已有几种专业检查Java内存泄漏的工具,它们的基本工作原理大同小异,都是通过监测Java程序运行时,所有对象的申请、释放等动作,将内存管理的所有信息进行统计、分析、可视化。开发人员将根据这些信息判断程序是否有内存泄漏问题。这些工具包括Optimizeit Profiler,JProbe Profiler,JinSight, Rational公司的Purify等。

在运行过程中,我们可以随时观察内存的使用情况,通过这种方式,我们可以很快找到那些长期不被释放,并且不再使用的对象。我们通过检查这些对象的生存周期,确认其是否为内存泄露。

四、java程序设计中有关内存管理的经验

1. 最基本的建议是尽早释放无用对象的引用。如:...

A a = new A();

//应用a对象

a = null; //当使用对象a之后主动将其设置为空

….

注:如果a 是方法的返回值,不要做这样的处理,否则你从该方法中得到的返回值永远为空,而且这种错误不易被发现、排除

2. 尽量少用finalize函数。它会加大GC的工作量。

3. 注意集合数据类型,包括数组、树、图、链表等数据结构,这些数据结构对GC来说,回收更为复杂。

4. 尽量避免在类的默认构造器中创建、初始化大量的对象,防止在调用其自类的构造器时造成不必要的内

存资源浪费。由于对象的创建是递归式的,也就是先调用超级类的构造,然后依次向下递归调用构造函数,


所以应该避免在类的构造函数中初始化变量,这样可以避免不必要的创建对象造成不必要的内存消耗.当然这里也就看出来接口的优势。

5. 尽量避免强制系统做垃圾内存的回收,增长系统做垃圾回收的最终时间

6. 尽量避免显式申请数组空间

7. 别用new Boolean()

在很多场景中Boolean类型是必须的,比如JDBC中boolean类型的set与get都是通过Boolean封装传递的,大部分ORM也是用Boolean来封装boolean类型的,比如:

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,就再也不会出现这种情况了。

8. 别用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中都可以使用这种特性。

9. 不要用StringBuffer代替字符串相加

10. 不要过滥使用哈希表

有一定开发经验的开发人员经常会使用hash表(hash表在JDK中的一个实现就是HashMap)来缓存一些数据,从而提高系统的运行速度。比如使用HashMap缓存一些物料信息、人员信息等基础资料,这在提高系统速度的同时也加大了系统的内存占用,特别是当缓存的资料比较多的时候。其实我们可以使用操作系统中的缓存的概念来解决这个问题,也就是给被缓存的分配一个一定大小的缓存容器,按照一定的算法淘汰不需要继续缓存的对象,这样一方面会因为进行了对象缓存而提高了系统的运行效率,同时由于缓存容器不是无限制扩大,从而也减少了系统的内存占用。现在有很多开源的缓存实现项目,比如ehcache、oscache等,这些项目都实现了FIFO、MRU等常见的缓存算法。

11. 避免过深的类层次结构和过深的方法调用。因为这两者都是非常占用内存的(特别是方法调用更是堆栈空间的消耗大户)。

12. 变量只有在用到它的时候才定义和实例化。

13. 共享静态存储空间

我们都知道静态变量在程序运行期间其内存是共享的,因此有时候为了节约内存工件,将一些变量声明为静态变量确实可以起到节约内存空间的作用。但是由于静态变量生命周期很长,不易被系统回收,所以使用静态变量要合理,不能盲目的使用.以免适得其反。

因此建议在下面情况下使用:变量所包含的对象体积较大,占用内存过多;变量所包含对象生命周期较长;变量所包含数据稳定;该类的对象实例有对该变量所包含的对象的共享需求.(也就是说是否需要作为全局变量)。
分享到:
评论
2 楼 虚弱的java 2011-08-16  
     
1 楼 zz459299295 2011-05-11  
说的不错!!

相关推荐

    高性能JAVA开发之内存管理

    ### 高性能JAVA开发之内存管理 在Java开发过程中,内存管理是确保程序高效稳定运行的关键环节之一。本文将深入探讨Java对象的生命周期,并通过具体的示例代码来解析不同阶段的特点及其对内存管理的影响。 #### ...

    基于java的开发源码-高性能内存消息和事件驱动库 Chronicle.zip

    基于java的开发源码-高性能内存消息和事件驱动库 Chronicle.zip 基于java的开发源码-高性能内存消息和事件驱动库 Chronicle.zip 基于java的开发源码-高性能内存消息和事件驱动库 Chronicle.zip 基于java的开发源码-...

    Java内存使用系列一Java对象的内存占用Java开发J

    在Java编程语言中,了解对象...通过合理分配和管理内存,可以提高程序的效率,减少系统资源消耗,从而构建出更加健壮和高性能的Java应用。在实践中,不断学习和优化内存使用策略,将有助于提升代码质量并降低维护成本。

    java内存管理详细介绍.doc

    Java内存管理是Java编程中至关重要的一环,它与C++等其他...总的来说,虽然Java的内存管理自动化程度较高,但深入理解JVM的内存管理机制,能够帮助开发者编写更高效、更稳定的代码,避免不必要的性能瓶颈和内存问题。

    利用Java开发高性能、高并发Web应用

    在Java开发领域,构建高性能、高并发的Web应用是一项核心任务。这涉及到多个技术层面的综合运用,包括但不限于系统架构设计、线程管理、数据访问优化、缓存策略、负载均衡以及性能监控等。以下是一些关键的知识点,...

    java php python erlang 千万级内存数据性能比较

    在IT领域,尤其是在大数据处理和高性能计算中,选择正确的编程语言对于实现千万级内存数据的高效处理至关重要。本文将深入探讨Java、PHP、Python和Erlang这四种语言在处理千万级内存数据时的性能差异。 首先,让...

    java学习笔记(java 反射机制 流 内存管理)

    Java是一种广泛使用的面向对象的编程语言,其强大的功能和灵活性使其在软件开发领域占据着...在实际项目中,根据需求灵活运用反射、流和内存管理,可以提高代码的可扩展性和性能,而扎实的基础知识则是这一切的前提。

    JAVA高级开发必备

    在IT行业中,Java作为一种强大的、跨平台的编程语言,一直深受...对于想要在Java开发领域深耕的人来说,掌握这些知识是不可或缺的。通过深入学习和实践,开发者可以提升自己的专业技能,更好地应对复杂项目的需求。

    如何利用Java开发高性能、高并发Web应用

    在开发高性能、高并发的Web应用时,Java程序员需要关注许多关键点来优化代码和提升系统性能。以下是一些基于给定内容的Java程序性能优化技巧: 1. **合理分配对象空间**: - 在创建对象时,了解并预估对象的大小...

    Java堆外内存的使用Java开发Java经验技巧共5页

    Java开发中的堆外内存使用是优化程序性能的重要策略之一,尤其在处理大数据、高并发或者内存敏感的应用场景中。堆外内存,也被称为直接内存(Direct Memory),是指不在JVM的堆内存中分配的内存,而是直接在操作系统...

    使用JAVA内存数据库h2database性能优化

    h2database是一款轻量级、高性能的Java内存数据库,特别适合用于处理实时性强、对响应速度要求高的业务逻辑。 2.1 h2database的启动 h2database有两种启动模式:嵌入式和控制台模式。嵌入式模式中,数据库与应用...

    java内存原理.doc

    Java 中的内存管理机制是自动的,开发者不需要手动释放内存,但是这也使得 Java 程序占用内存相对较高。为了避免内存泄露,开发者需要注意在编写程序时,合理地使用内存资源。 Java 内存原理的优点: * 自动内存...

    使用java开发。实现停车场管理系统,应用于车辆的出、入管理。.zip

    Java是一种高性能、跨平台的面向对象编程语言。它由Sun Microsystems(现在是Oracle Corporation)的James Gosling等人在1995年推出,被设计为一种简单、健壮、可移植、多线程、动态的语言。Java的主要特点和优势...

    java写的内存管理

    在Java中,虽然程序员通常不直接处理这样的底层操作,但理解这种算法有助于设计高性能的并发系统。 在"MemoryManager.java"这个文件中,我们可以预期它实现了模拟内存管理和时间片轮转调度的逻辑。可能包括以下几个...

    eclipse开发性能优化、java代码性能优化

    - 如果条件允许,升级硬件资源,比如增加 RAM 或使用 SSD 等高性能存储设备。 **7. 显示设置** - 关闭不必要的 UI 功能,比如动画效果等,以减少资源消耗。 #### 二、Java 代码性能优化 **1. 代码层面** - ...

    Java高性能文件上传及下载

    在Java开发中,实现高性能的文件上传和下载是至关重要的技术环节,特别是在处理大量用户交互或者大数据量传输的场景下。本资料集主要探讨如何利用Java技术和相关工具库来优化这一过程,确保系统的稳定性和效率。 ...

    java实现的内存分配

    开发者还可以利用Java的内存分析工具(如VisualVM或JProfiler)来监控和诊断内存使用情况,找出可能导致内存泄漏或性能瓶颈的问题。 总的来说,理解和掌握Java中的内存分配策略对于编写高效、健壮的代码至关重要。...

    Java 虚拟机JVM内存模型

    ### Java 虚拟机JVM内存模型知识点 ...了解和掌握这些知识点,对于开发高性能Java应用是非常有帮助的。在实际开发过程中,合理配置JVM参数、选择合适的垃圾收集器和优化内存分配策略是提升Java应用性能的关键。

    java_如何利用Java开发高性能、高并发Web应用

    综上所述,开发高性能、高并发的Java Web应用涉及多个层面:选择合适的框架、优化数据库访问、合理使用缓存、有效管理线程、采用微服务架构、设计良好的API、性能监控和自动化流程。只有深入理解并熟练运用这些技术...

    Java开发手册(黄山版)

    此外,手册还会包含“性能优化”内容,包括JVM调优、内存管理、垃圾收集、代码性能分析等。这些知识可以帮助开发者编写出更加高效、低耗的代码,提升系统的整体性能。 最后,手册可能会包含一些特定于阿里巴巴集团...

Global site tag (gtag.js) - Google Analytics