zoukankan      html  css  js  c++  java
  • 高性能JAVA开发之内存管理(一)

    几天在找一个程序的bug,主要是java虚拟机内存溢出的问题,调研了一些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模型采用计数器方式管理构件,它与有向图相比,精度行低(很难处理循环引用的问题),但执行效率很高。

    (未完待续)

  • 相关阅读:
    小工具之文件整合
    [JavaWeb基础] 031.dom4j写入xml的方法
    AES128_CBC模式加密
    eatwhatApp开发实战(九)
    [Objective-C] 021 KVC、KVO
    eatwhatApp开发实战(八)
    eatwhatApp开发实战(七)
    [Objective-C] 020_ Block
    eatwhatApp开发实战(六)
    年终总结--我的2019
  • 原文地址:https://www.cnblogs.com/dandre/p/4507070.html
Copyright © 2011-2022 走看看