转自:http://blog.sina.com.cn/s/blog_538b279a0100098d.html

写的相当不错滴......................

摘  要

  Java语言中,内存空间中垃圾回收的工作由垃圾回收器(Garbage Collector,GC)完成,GC可以有效地减少内存泄露发生的概率,但它的启动无规律可循,因此不能完全避免内存泄露。通过分析产生内存泄露的原因,提出了Java编程中的一些注意事项,有效地避免了内存泄露。
关键词  Java  内存泄露  垃圾回收器  虚拟机
 
1 引言
       Java语言,是一种可以编写跨平台应用软件的面向对象的程序设计语言,由升阳公司[1]的詹姆斯·高斯林等人于二十世纪90年代初开发。伴随着Java技术的普及,网络上越来越多的服务器程序采用Java技术,特别是Internet[2]使Java成为网上最流行的编程语言。在传统的高级编程语言(例C++)中,对象的创建和回收均由程序员自行负责,如果创建了对象而忘记回收,则会造成内存泄漏,长此以往,程序运行的时候可能会生成很多不清除的垃圾,浪费了不必要的内存空间,有时还可能引起系统的崩溃。在Java中,引入了垃圾回收机制:垃圾回收器(Garbage Collector, GC),可以自动回收内存中的垃圾,这是Java语言相对于其它语言的优势地方,但是内存泄漏并不会因此而完全避免

2 内存泄露
       大多数C++编译器不支持垃圾回收机制。通常使用C++编程的时候,程序员所创建的对象在创建时在堆栈上分配一块内存地址,当不需要这个对象,进行析构或者删除的时候再释放分配的内存空间。如果对象是在堆上分配了,而程序员又忘记进行释放,这些空间又无法自动回收,就会造成内存泄漏。而无法回收的内存空间,即丢失的内存(我们称之为“垃圾”),除非是重新启动系统否则永远也不会还给操作系统。长此以往,程序运行的时候可能会生成很多垃圾,浪费了不必要的内存空间。更糟糕的是,如果同一内存地址被删除两次的话,程序会变得不稳定,甚至崩溃。
       Java语言则不同,上述的情况被自动垃圾回收机制自动处理。对象的建立和放置都是在内存堆栈上面进行的。程序或者其他的对象可以锁定一块堆栈地址来进行其他对象的引用。当一个对象没有任何引用的时候,Java的自动垃圾回收机制就发挥作用,自动删除这个对象所占用的空间,释放内存以避免内存泄漏。但是内存泄漏并不是就此而完全避免了,当程序员疏忽大意地忘记解除一个对象不应该有的引用的时候,内存泄漏仍然不可避免,不过发生的几率要比不启用垃圾回收机制的C++程序少很多。但是总体来讲,自动垃圾回收机制要安全和简单许多。
 
3 垃圾回收机制
3.1 什么是垃圾
       垃圾,内存中的垃圾,即内存中已无效但又无法自动释放的空间。在Java语言中,没有引用句柄指向的类对象最容易成为垃圾。,产生垃圾的情况有很多,主要有以下3种:
(1)超出对象的引用句柄的作用域时,这个引用句柄引用的对象就变成垃圾:

例:

{
Person p1 = new Person();
……
}

引用句柄p1的作用域是从定义到“}”处,执行完这对大括号中的所有代码后,产生的Person对象就会变成垃圾,因为引用这个对象的句柄p1已超过其作用域,p1已经无效,Person对象不再被任何句柄引用了。

(2)没有超出对象的引用句柄的作用域时,给这个引用句柄赋值为空时,这个引用句柄引用的对象就变成垃圾:
例:

{
Person p1 = new Person();
…..
p1 = null;
….
}

  在执行完“p1=null;”后,即使句柄p1还没有超出其作用域,仍然有效,但它已被赋值为空,不再指向任何对象,则这个Person对象不再被任何句柄引用,变成了垃圾。此后p1还可以指向其它Person对象,因为还没有超出它的作用域。

(3)创建匿名对象时,匿名对象用完以后即成垃圾:
例:

{
  new Person(); //因为是匿名对象,没有引用句柄指向它,即为垃圾
  new Person().print();
  //当运行完匿名对象的print()方法,这个对象也变成了垃圾
  ……
}

因此,在程序中应尽量少用匿名对象。

3.2 垃圾回收
  在Java程序运行过程中,一个垃圾回收器会(Garbage Collector,简称GC)不定时地被唤起检查是否有不再被使用的对象,并释放它们占用的内存空间。垃圾回收器的回收无规律可循,可能在程序的运行的过程中,一次也没有启动,也可能启动很多次。因此,并不会因为程序代码一产生垃圾,垃圾回收器就马上被唤起而自动回收垃圾,很可能到程序结束时垃圾回收器都没有启动。所以垃圾回收器并不能完全避免内存泄漏的问题。
     另一方面,垃圾回收会给系统资源带来额外的负担和时空开销。它被启动的几率越小,带来的负担的几率就越小。因此,垃圾的回收策略也很重要。

3.3 垃圾回收器的回收策略
  不同厂商、不同版本的Java虚拟机中的内存垃圾回收机制并不完全一样,通常越新版本的内存回收机制越快。而不同的Java虚拟机采用不同的回收策略,常用的有两种:复制式回收策略和自省式回收策略。
  复制式回收策略:先将正在运行中的程序暂停,然后把正在被使用的所有对象从它们所在的堆内存A里复制到另一块堆内存B,再释放堆内存A中的所有空间,这些那些不再使用的对象所占用的内存空间就会被释放掉。这种方式需要维护所需内存数量的至少两倍的内存空间,适合垃圾比较多的情况当程序只产生了少量垃圾或者没有垃圾时,这种回收策略的效率就非常低。

  自省式回收策略:首先检测所有正在使用的对象,并为它们标注,比如用1来标注正在使用的对象,用0来标注不再被使用的对象,然后将所有标注为0的内存空间一次释放。因为标注会增大系统的开销,因此这种方式的速度仍然很慢,尤其是在垃圾比较多的情况下,效率会很低。这种方法适合垃圾比较少的情况
  这两种方式具有互补性,因此在一些Java虚拟机中两种方式被有机的结合运用。

4  System.gc()
       由于Java的垃圾回收器的启用不由程序员控制,而且回收也无规律可循,并不会一产生了垃圾,垃圾回收器就被唤起;有时甚至可能到程序终止,回收器都没有启动的机会。因此这个垃圾回收机制不是一个很可靠的机制。因为垃圾不能及时回收,它们所占用的内存空间不能释放,就会影响程序的性能;如果某段程序产生大量的垃圾而没有回收,回收工作也会变得困难。为了解决这个问题,Java提供一个System.gc()方法,可以强制启动垃圾回收器来回收垃圾,以减少内存泄露发生的概率。

例:匿名对象会产生垃圾,如果担心这些垃圾不能及时回收,可以在使用完这些匿名对象以后,加上一条语句:System.gc(),强制启动垃圾回收器来回收垃圾。

class TestJc {
public void finalize() {
System.out.println("Free the occupied memory...");
} public static void main(String args[]) {
new TestJc();
new TestJc();
new TestJc();
System.gc();
System.out.println("End of program.");
}
}

程序的运行结果是:

  System.gc()有一个特点,就是在对象被当成垃圾从内存中释放前要调用finalize()方法,而且释放一个对象调用一次finalize()方法。从程序的运行结果可以看到:垃圾回收器启动以后,并不一定马上开始回收垃圾,很可能要等待一段时间才执行。这是因为在程序运行过程中,垃圾收集线程的优先级比较低,如果有比这个线程优先级高的线程,先运行这些优先级高的线程,等这些线程执行完毕,才进行垃圾回收。所以System.gc()方法只是一种“建议”,它建议Java虚拟机执行垃圾回收,释放内存空间,但什么时候能够回收就不能够预知了。

 如果我们把“System.gc();”语句,放在第二个匿名对象语句后面,再进行编译和执行,会发现结果是这样的:

  这是因为,启动完垃圾回收器以后,它只能检测到在垃圾回收器强制启动之前程序运行所产生的垃圾,Java的虚拟机尽最大的努力从被丢弃的对象上回收垃圾;对于在启动垃圾回收器以后产生的垃圾,这个线程检测到的概率就非常小了,如果检测不到,就不能回收这些垃圾。
      因此,Java中的垃圾回收器机制及System.gc()方法,并不能够完全避免内存泄露的问题,只是尽可能降低内存泄露的可能性和程度。

5  Java编程中需要注意的事项
为了提高垃圾的回收效率,在实际应用中,使用下列几种方法可以在一定程度上避免Java中的内存泄露:

(1)尽量少用匿名对象,慎用内部类:
  匿名对象被使用完以后就会变成垃圾;而在内部类中,隐含着一个外部类对象的引用,这个引用也无法自动消除。
(2)在使用System.gc()方法的程序中,尽量少用finalize()方法:
  因为System.gc()方法在回收每一个对象所占用的内存空间时,都会调用finalize()方法,在这个方法中的任何操作都会增加垃圾回收的开销。
(3)慎用System.gc()方法,减少线程的个数:
  在程序中可以显式地调用System.gc()方法,但这种方法不能保证清除所有的垃圾。另外垃圾回收也是一个线程,也会消耗系统的资源,启动垃圾回收也可能会造成间歇性停顿。线程越多,垃圾回收线程挂起和恢复的可能性就越大,而耗费的时间就越长,系统的开销就越大。

6 结语
       本文对Java中的内存泄漏和垃圾回收机制进行了论述,分析了内存泄露的原因,以及System.gc()的工作方式,提出了一些避免内存泄露的方法,希望能为Java爱好者提供一些参考。

[1] 升阳公司:Sun Microsystems,美国的一家电脑公司,在中国大陆的正式中文名为“太阳计算机系统公司”,在台湾的正式中文名为“升阳电脑公司”。公司创建于1982年,1986年在美国成功上市。主要产品是工作站及服务器。
[2] Internet:interconnection network因特网或互联网,“联接网络的网络”,可以是任何分离的实体网络之集合,这些网络以一组通用的协定相连,形成逻辑上的单一网络。

Java中内存泄露及垃圾回收机制的更多相关文章

  1. Java的内存泄漏和垃圾回收机制

    JAVA会产生内存泄露吗?首先,答案是肯定的. Java尽管有垃圾回收器,但依旧存在泄漏. Java内存泄漏跟C/C++内存泄漏的概念不一样:C/C++的内存泄漏是指Malloc了一些资源.最后没有f ...

  2. 巩固java(二)----JVM堆内存结构及垃圾回收机制

    前言:        我们在运行程序时,有时会碰到内存溢出(OutOfMemoryError)的问题,为了解决这种问题,我们有必要了解JVM的内存结构和垃圾回收机制. 正文: 1.JVM堆内存结构   ...

  3. JVM原理(Java代码编译和执行的整个过程+JVM内存管理及垃圾回收机制)

    转载注明出处: http://blog.csdn.net/cutesource/article/details/5904501 JVM工作原理和特点主要是指操作系统装入JVM是通过jdk中Java.e ...

  4. java中内存泄露有几种?如何分析泄露原因

    一.Java内存回收机制 不论哪种语言的内存分配方式,都需要返回所分配内存的真实地址,也就是返回一个指针到内存块的首地址.Java中对象是采用new或者反射的方法创建的,这些对象的创建都是在堆(Hea ...

  5. Java虚拟机学习笔记——JVM垃圾回收机制

    Java虚拟机学习笔记——JVM垃圾回收机制 Java垃圾回收基于虚拟机的自动内存管理机制,我们不需要为每一个对象进行释放内存,不容易发生内存泄漏和内存溢出问题. 但是自动内存管理机制不是万能药,我们 ...

  6. JVM内存管理和垃圾回收机制介绍

    http://backend.blog.163.com/blog/static/20229412620128233285220/     内存管理和垃圾回收机制是JVM最核心的两个组成部分,对其内部实 ...

  7. Java虚拟机内存模型及垃圾回收监控调优

    Java虚拟机内存模型及垃圾回收监控调优 如果你想理解Java垃圾回收如果工作,那么理解JVM的内存模型就显的非常重要.今天我们就来看看JVM内存的各不同部分及如果监控和实现垃圾回收调优. JVM内存 ...

  8. JVM内存分配与垃圾回收机制管理

    项目上线,性能优化有个重要组成就是jvm内存分配和垃圾回收机制的管理配置. 网上随便能搜到相关的具体步骤,以及内存中各种参数对应的意义,不再赘述. 干货就是直接抛出遇到的问题,以及如何解决的,再说说待 ...

  9. V8 内存管理和垃圾回收机制总结

    这篇文章主要介绍 V8 的内存管理和垃圾回收知识. V8 内存管理及垃圾回收机制浅析 由于 V8 引擎的原因,Node 在操作大内存对象时受到了一些限制,在 64 位的机器上,默认最大操作的对象大小约 ...

随机推荐

  1. 洛谷——P1629 邮递员送信

    P1629 邮递员送信 题目描述 有一个邮递员要送东西,邮局在节点1.他总共要送N-1样东西,其目的地分别是2~N.由于这个城市的交通比较繁忙,因此所有的道路都是单行的,共有M条道路,通过每条道路需要 ...

  2. Telnet操作memcached

    telnet登录 #telnet <ip> [root@c1 ~]# telnet Trying 127.0.0.1... Connected to 127.0.0.1. Escape c ...

  3. 杭电oj 1002

    #include <iostream> #include <algorithm> using namespace std; int nCases; ], n[]; ], b[] ...

  4. hdu 4055 Number String (基础dp)

    Number String Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)To ...

  5. 【强连通分量缩点】【拓扑排序】【dp预处理】CDOJ1640 花自飘零水自流,一种相思,两处闲愁。

    题意: 在n个点m条边的有向图上,从1出发的回路最多经过多少个不同的点 可以在一条边上逆行一次 题解: 在同一个强连通分量中,显然可以经过当中的每一个点 因此先将强连通分量缩点,点权为强连通分量的点数 ...

  6. Java编程思想学习(二)----一切都是对象

    2.1用应用操作对象 String s; 这里所创建的只是引用,并不是对象.如果此时向s发送一个消息,就会返回一个运行时错误.这是因为此时s没有和任何事物关联.因此,一种安全的做法是:创建一个引用的同 ...

  7. 十. 图形界面(GUI)设计14.键盘事件

    键盘事件的事件源一般丐组件相关,当一个组件处于激活状态时,按下.释放或敲击键盘上的某个键时就会发生键盘事件.键盘事件的接口是KeyListener,注册键盘事件监视器的方法是addKeyListene ...

  8. [转]iBatis简单入门教程

    iBatis 简介: iBatis 是apache 的一个开源项目,一个O/R Mapping 解决方案,iBatis 最大的特点就是小巧,上手很快.如果不需要太多复杂的功能,iBatis 是能够满足 ...

  9. Linux文件查找命令find和xargs详解

    PS:find是个很强大的命令,能够匹配正则,查找对应权限,能够帮你精确的定位你的系统中的任何地方任何目录下的文件.下面这篇是关于find命令用法最详细的一篇了,记录下.还有要注意的:本文的分号:其实 ...

  10. The Responsive jQuery Content Slider

    jquery slider 效果 http://bxslider.com/ http://www.cnblogs.com/lhb25/archive/2012/08/13/jquery-image-e ...