关于面试题java内存泄漏想到的(3)

来源:互联网 发布:mysql using 编辑:程序博客网 时间:2024/06/09 23:49

此处再总结thinking in java中内存回收机制。

4.3章节。清除,收尾和垃圾收集。假定我们为对象分配了一个特殊的内存区域,没有使用new。垃圾回收器是无法回收的,为了解决这个问题,java提供了finalize()方法,理想情况下,一旦垃圾回收器准备释放空间,先调用finalize(),而且只有在下一次垃圾回收过程中,才会真正的回收内存。百度面试我回答的就是这个意思,然而这里只是假定,到底java有没有finalize()呢?当然不会面试通过的。下面的描述中说明finalize()是存在的,可以在finalize()中置入某种删除机制,当对象被内存回收时候,对象相关的一些需要处理的事情可以放在这里处理。

1、对象可能得不到GC;2 finalize()不是析构;3、垃圾回收(GC)只与内存有关!

finalize()用途? finalize()的用途:对象可采用与创建对象不同的方法分配一些存储空间。那采用什么方法呢?native方法?需要示例支持

无论GC还是finalize()都不保证一定会发生,如果在jvm还够使用的情况下,它是不会浪费在内存上面的。所以不要指望finalize()做清除工作。

示例:

 

class Book {

  boolean checkedOut = false;

  Book(boolean checkOut) {

    checkedOut = checkOut;

  }

  void checkIn() {

    checkedOut = false;

  }

  protected void finalize(){

    if(checkedOut)

      System.out.println("Error: checked out");

    //Normally, you'll also do this:

    //super.finalize(); // Call the base-class version

  }

}

 

public class TerminationCondition {

  public static void main(String[] args) {

    Book novel = new Book(true);

    // Propercleanup:

    novel.checkIn();

    // Dropthe reference, forget to clean up:

    new Book(true);

    // Forcegarbage collection & finalization:

    System.gc();

  }

}

输出结果:Error: checked out

在main中调用System.gc();类的finalize()方法会被自动调用;

GC如何工作的:

C++中各个对象管理自己的内存,java中由JVM管理

引数计数器速度慢,并且无法解决相互引用的问题。通常jvm遍历堆栈和静态存储区域,遍历引用,以及引用指向对象的引用。Jvm采取自适应模式。Stop-and-copy.需要暂停程序运行,将所有存活对象从一个堆复制到另外的堆。没有被复制的都是垃圾。复制之后注意引用要重新指向。比较耗时,同时需要两个空间。如果垃圾不多则采取mark-and-sweep。如果内存碎片较多,也会自动启用SC模式。

再回到内存泄漏问题。网上下载到中国移动的培训文档,估计是哪个前辈查阅大量资料总结的PPT。介绍java内存泄漏的知识。Java程序中内存关系(进程内存,其次是本地内存,再次是java堆),这个关系有待于查阅资料。
java堆指的是JVM用来分配java对象的内存。本地内存,JVM内部操作的的内存,主要是代码生成,线程创建,GC运行临时信息的保存,优化等临时空间。进程内存,堆内存,本地内存,加载的可执行文件以及库的总和。

表现1、jvm抛出java.lang.OutOfMemoryError;2、本地内存stdout输出无法获得内存,生成core文件;3、进程内存无法获得内存,生成core文件。

解决方案:1、堆内存不足则需要检查jvm是否有缺陷,两方面入手,OOM之前是否运行了完整的GC。是否由于没有整理碎片?怎么检查?JVM正常情况下,java的-Xmx命令调整最大可用java堆大小。2、本地内存不足,确保RAM与磁盘的交换空间之和足以满足该计算机中正在运行的所有进程需要,调整java堆,释放空间给本地内存。

GC采用有向图的方式进行内存管理,对象作为有向图的顶点,引用关系作为有向图的边。堆栈对象,静态对象作为起始顶点,大多数都是main进程开始的一颗进程树,根顶点可达的是有效对象,不可达的是无效对象。C++中,不可达的对象都是内存泄漏,java中这些都会被回收。C++中程序员要管理有向图的顶点和边,java中只需要管理边(对象引用就可以了),有效对象要可达,无效对象要不可达。Java内存泄漏,本质就是无效对象可达,无意识的对象引用与保持。

检测方法:1、代码走查;2、内存泄漏工具。

常见现象

1、全局集合1、Vector v =new Vector();

For(int I =0; i<100;i++)

{

Object obj = new Object();

v.add(obj); obj=null

}

分析:v持有obj对象,无效,可达。解决方案1、v中remove到不用的对象;2、v置空;v=null;

2、全局数据仓库。例如:1、JNDI;2、sessiontable;3、Collection。

解决方案:1、周期运行清楚程序;验证仓库中数据是否有效,无效的则清除。2、集合跟踪集合元素引用者的数量,当引用为零,则remove.

3、基于数组的集合,Statck类,对象出栈之后,栈顶引用置空。

4、高速缓存:1、OMP中的对象缓存;2、ARIS-SIAA中指标关系缓存。

解决方案:1、告诉缓存设定上限值。2、使用软引用来将对象放入缓存。什么是软引用?

5、类装载器:ClassLoader涉及元对象的引用,容易驻留JVM;解决方案:慎重使用个性化ClassLoader。ClassLoader的使用需要详细了解?

6、静态变量引用对象;静态变量在栈中,GC无法回收,成为有向图的起始顶点;

解决方案:1、慎重使用,尤其是集合类静态变量;2、出现了集合类静态变量,要注意自己管理自己的内存。

7、对象游离:

public class LeakyChecksum{ private byte[]byteArray; public synchronized int getFileChecksum(String fileName){ int len =getFileSIze(fileName); if(byteArray == null || byteArray.length <len){   readFileContents(fileName,byteArray);} } }

如果LeakyChecksum对象不被GC回收,那么byteArray就永久保持一个与最大文件一样大小的缓存。逻辑上来说readFileContents 的生命周期应该是归属于函数,结果却由于byteArray被提升为类对象,导致生命周期被无意延长。

解决方案:1、byteArray定义到函数内部,但是这样每次使用都将重新读取文件,速度变慢。2、使用软引用public class LeakyChecksum{ private SoftReference bufferRef; publicsynchronized int getFileChecksum(String fileName){ byte[] byteArray=bufferRef.get();  int len = getFileSIze(fileName); if(byteArray== null || byteArray.length <len){ byteArray= new byte[len]bufferRef.set(byteArray);         readFileContents(fileName,byteArray);}} }

8、类成员变量定义的时候初始化;可能并不使用这个对象,但是要分配内存。解决方案:定义时候指向null,使用的时候if(obj==null) obj = new Object();

 

截至目前,对于java中内存泄漏基本有了解了。Java编程思想里面介绍的有不是通过new方式申请的内存,java调用c的方法,需要通过finalize()处理,effective中介绍的过期对象的引用。下一步工作是内存监控工具,查看内存。之前做项目中使用过JDK自带的bin目录下的jconsole.exe,能检查出各个java进程的内存使用情况。

博客地址:http://blog.csdn.net/djy1135/article/details/2304465介绍了一个工具JProfiler。能查看各个类型的对象引用数量。很不错

强引用,弱引用等关系,介绍的蛮好,借鉴过来。从最强到最弱,不同的引用(可到达性)级别反映了对象的生命周期。

<!--[if!supportLists]-->l <!--[endif]-->Strong Ref(强引用):通常我们编写的代码都是StrongRef,于此对应的是强可达性,只有去掉强可达,对象才被回收。

<!--[if!supportLists]-->l <!--[endif]-->Soft Ref(软引用):对应软可达性,只要有足够的内存,就一直保持对象,直到发现内存吃紧且没有Strong Ref时才回收对象。一般可用来实现缓存,通过java.lang.ref.SoftReference类实现。

<!--[if!supportLists]-->l <!--[endif]-->Weak Ref(弱引用):比Soft Ref更弱,当发现不存在StrongRef时,立刻回收对象而不必等到内存吃紧的时候。通过java.lang.ref.WeakReference和java.util.WeakHashMap类实现。

<!--[if!supportLists]-->l <!--[endif]-->Phantom Ref(虚引用):根本不会在内存中保持任何对象,

关于think in java中的特殊内存的分配,我通过java调用C了,没有发现windows内存和java运行进程内存的增加,需要待验证
0 0