`
snoopy7713
  • 浏览: 1149017 次
  • 性别: Icon_minigender_2
  • 来自: 火星郊区
博客专栏
Group-logo
OSGi
浏览量:0
社区版块
存档分类
最新评论

探究Java垃圾回收机制

    博客分类:
  • java
阅读更多
java垃圾回收机制使得java程序员不用手动去释放通过new关键字在heap上申请的空间。但是,任何事情都是有利有弊。它也许并不像我们想象中那样强大!让我们来一探究竟。本blog链接:blog.csdn.com/whuslei。
   阅读本文前,请务必参看《think in java》第四版的第五章"初始化和清理"。其他网上的资料就没必要看了,没有太大价值。 
   有几个问题应该考虑下:
1、什么是垃圾?它是如何形成的?
2、java的垃圾收集器是什么?它是什么时候执行的?执行过程是怎样的?
3、sun为什么不推荐使用finalize()函数来释放一个对象?这个函数究竟做了些什么?
4、java中是否也存在内存泄漏?是否和C++中的一样?如果存在,应当如何避免?

   带着问题来思考java内部是怎么做的。

一、对象在什么情况下变为垃圾?
   java中称那些不可达的对象为垃圾。那什么叫不可达?其实就是没办法再引用到该对象了。主要有以下几种情况使对象变为垃圾:

1、对非线程对象来说,所有的活动线程都不能访问该对象,那么该对象就变为垃圾。
2、对线程对象来说,满足上面的条件,且线程未启动或已停止。

例如:
(1)改变对象的引用,如置为null或者指向其他对象。
   Object x=new Object();//object1
   Object y=new Object();//object2
   x=y;//object1 变为垃圾
   x=y=null;//object2 变为垃圾

(2)超出作用域
   if(i==0){
      Object x=new Object();//object1
   }//括号结束后object1将无法被引用,变为垃圾

(3)类嵌套导致未完全释放
   class A{
      A a;
   }
   A x= new A();//分配一个空间
   x.a= new A();//又分配了一个空间
   x=null;//将会产生两个垃圾

(4)线程中的垃圾
   class A implements Runnable{ 
     void run(){
       //....
     }
   }
   //main
   A x=new A();//object1
   x.start();
   x=null;//等线程执行完后object1才被认定为垃圾

   这样看,确实在代码执行过程中会产生很多垃圾,不过不用担心,java可以有效地处理他们。

二、垃圾收集机制
   首先,垃圾收集机制是由垃圾收集器Garbage Collection(以下简称gc)来实现的。而gc其实就是一个后台守护进程(其实这种说法不是很确切,因为当可用内存不足时,gc会先暂停应用程序)。它的特别之处在于它是一个低优先级的进程,但是可以根据内存的使用情况动态地调整它的优先级。因此,通常在系统内存低到一定限度时才会自动运行,从而实现对内存的回收。这也就是我们通常说垃圾回收的时间是不确定的原因。
   为什么要这样设计呢?我们知道,gc也是进程,也要消耗cpu等资源,如果gc执行过于频繁会对java程序的执行产生较大影响(java解释器本来就不快)。因此JVM的设计者们选择了不定期的gc。

   在垃圾回收器回收内存前,还需要做一些清理工作。why?

   其实,gc只能回收通过new关键字申请的内存(在堆上),但是堆上的内存却不完全是通过new申请分配的!java中通过JNI可以访问本地方法,一般是调用C,而c是通过malloc和free来进行申请内存的。这部分"特殊"内存如果不手动释放,会引起内存泄漏!gc是无法回收这部分内存的!举例:System.gc()可以显示调用垃圾收集器。从java api中对这个函数的解释可以看出,这个方法只是一种尽力而为的回收,并不能保证将所有的垃圾都回收,为什么不能保证回收全部垃圾?原因如上。
   所以,java允许在类中定义一个名为finalize的方法(Object类中的一个方法)。它可以用来做一些清理工作。它的工作原理"假定"是这样的:一旦JVM的垃圾回收器准备好释放对象所占用的内存空间,将首先调用其finalize方法(每个类都有,他们都继承自Object类),并且在下一次垃圾回收动作时才会真正回收对象所占用的内存空间。

   对上面的原理,很容易产生疑惑,如下:
   1、gc是负责回收内存的,那finalize也是用来回收内存的,他们一样吗?
   答:通过创建对象产生的内存,gc可以回收(至于如何回收,后面会讲到)。但是有些特殊内存gc不能回收,因此可以在finalize中用本地方法(native method)如free操作等!

   2、一个对象的finalize方法有什么要注意的?
   答:一个对象的finalize方法只能被调用一次!它并没有减少垃圾回收器的工作量!执行finalize后,gc依然需要判断这块内存空间是否可以回收!

   3、为什么sun并不推荐用finalize方法来完成清理工作?
   答:finalize方法只在垃圾回收之前才被调用。但是我们知道,gc本来就是不确定的。因此,当一个对象变为垃圾后,我们并不知道gc何时运行,也就不知道finalize何时被调用了。有可能它永远都不被调用!(程序退出时,未回收的内存将直接交还给操作系统)。由此可知,依靠finalize方法来完成清理工作是不靠谱的。正确的方法是,在放弃这个对象前调用相应的方法来进行内存的释放!(显式地调用)。

   4、有人可能会问,显式调用System.gc()强制运行gc不也可以调用到finalize完成清理吗?
   答:我认为,不可取。因为每次想执行清理都需要调用gc()来强制启动垃圾处理器,这样垃圾回收率低,反而很影响程序效率!为何不将清理工作放到某个方法中,准备放弃该对象的内存空间之前就调用该方法!

   重点来了,如何回收内存?
   方法一:引用计数法。简单但速度很慢。缺陷是:不能处理循环引用的情况。用法如下:每个对象都含有一个引用计数器,当被连接到对象时计数器加1,当离开作用域或者被置为null时,引用计数器减1。垃圾回收时,释放计数器为0的内存空间。如果出现循环引用,则会出现"对象应该被回收,但计数器不为0"。例如: a引用了b, b又反过来引用了a, 这时a,b就在堆上形成了一个闭环,  如果除b外只有c引用了a, 且只有a引用了b, 则c死亡时a,b也应该死掉, 但此时b还在引用着a, 于是a死不掉, a死不掉则b也死不掉,这就是所谓的循环引用问题。
   方法二:停止-复制(stop and copy)。效率低,需要的空间大。用法如下:先暂停程序的运行,将所有存活的对象从一个堆复制到另一个堆。没有被复制的都是垃圾,被复制部分将是连续空间没有碎片。
   方法三:标记-清扫(mark and sweep)。速度较快,占用空间少,但是释放后空间不来连续。用法如下:先暂停程序的运行,遍历所有引用,每找到一个存活对象就给它一个标记,此过程中不进行回收。当标记结束后才开始清理。清理后,剩下的部分是不连续的。如果希望等到连续的空间则需要进行复制。

   JAVA虚拟机中是如何做的?
   java的做法很聪明,我们称之为"自适应"的垃圾回收器,或者是"自适应的、分代的、停止-复制、标记-清扫"式垃圾回收器。它会根据不同的环境和需要选择不同的处理方式。

三、java中的内存泄漏
   从泄漏的内存位置角度可以分为两种:JVM 中 Java Heap 的内存泄漏;JVM 内存中 native memory 的内存泄漏。
  1、java heap的内存泄漏。
   由于java heap的空间是在jvm过程中动态变化的。如果Java对象越来越多,占据Java Heap的空间也越来越大,JVM会在运行时扩充Java Heap的容量。如果Java Heap容量扩充到上限,并且在GC后仍然没有足够空间分配新的Java对象,便会抛出out of memory异常,导致JVM进程崩溃。Java Heap 中 out of memory 异常的出现有两种原因——①程序过于庞大,致使过多Java对象的同时存在;②程序编写的错误导致Java Heap内存泄漏。多种原因可能导致Java Heap内存泄漏。JNI编程错误也可能导致Java Heap的内存泄漏。

   2、JVM 中 native memory 的内存泄漏
   从操作系统角度看,JVM 在运行时和其它进程没有本质区别。在系统级别上,它们具有同样的调度机制,同样的内存分配方式,同样的内存格局。JVM进程空间中,Java Heap以外的内存空间称为JVM的native memory。进程的很多资源都是存储在JVM的native memory中,例如载入的代码映像,线程的堆栈,线程的管理控制块,JVM的静态数据、全局数据等等。也包括JNI程序中native code分配到的资源。在JVM运行中,多数进程资源从native memory中动态分配。当越来越多的资源在 native memory中分配,占据越来越多native memory空间并且达到native memory上限时,JVM会抛出异常,使JVM进程异常退出。而此时Java Heap往往还没有达到上限。多种原因可能导致JVM的native memory内存泄漏。例如JVM在运行中过多的线程被创建,并且在同时运行。JVM 为线程分配的资源就可能耗尽native memory的容量。
分享到:
评论

相关推荐

    探究C语言和Java语言中垃圾回收的不同方式.pdf

    C语言与Java语言在垃圾回收机制上的差异性分析 计算机编程语言各有特色和侧重点,其中C语言作为过程式编程语言,其编程风格偏向底层硬件操作和资源直接控制;而Java作为面向对象编程语言,它更倾向于抽象和平台无关...

    Java垃圾收集必备手册

    Java垃圾收集必备手册 Java 垃圾收集是 Java 语言中的一种自动内存管理机制,旨在释放不再使用的内存资源,以避免内存泄漏和提高程序性能。在这篇手册中,我们将深入探究 Java 垃圾收集的基础知识,包括垃圾收集的...

    Java编程语言在大数据开发中的应用探究.pdf

    4. 数据安全与管理:Java提供了类型安全机制,有助于避免数据处理过程中的错误,同时Java的垃圾回收机制有利于资源的有效管理。 5. 敏捷开发:Java的可移植性和跨平台性使得开发者可以在不同环境中快速部署和测试...

    Java编程语言在计算机软件开发应用中的探究.zip

    Java内置了自动垃圾回收机制,程序员无需手动管理内存,减少了内存泄漏和段错误的可能性,提高了程序的稳定性。 四、异常处理 Java的异常处理机制通过try-catch-finally语句块,使得程序能够优雅地处理错误,提高了...

    JAVA编程语言在计算机软件开发应用中的探究 (2).pdf

    JAVA内置了自动垃圾回收机制,减轻了程序员的负担,减少了因内存管理不当而引发的错误。 应用广泛:JAVA语言因其上述特点,在各个领域都有广泛的应用。从企业级应用、桌面软件到移动应用,JAVA都能够提供稳定、高效...

    基于计算机应用软件开发的Java编程语言探究.zip

    3. 自动内存管理:Java提供垃圾回收机制,自动管理内存,避免了内存泄漏问题。 4. 安全性:Java具有严格的类型检查和安全管理机制,有效防止恶意代码执行。 5. 多线程:Java内置多线程支持,便于实现并发操作。 6. ...

    三国志 java 源码.rar

    Java是一种跨平台、面向对象的编程语言,具有自动内存管理(垃圾回收机制)、异常处理、多线程以及丰富的类库等优势,这些使得Java成为移动游戏开发的常见选择。 在源码中,我们可以看到"src"目录,它是Java项目的...

    基于计算机软件开发的JAVA编程语言探究.zip

    2. 内存管理:Java采用自动垃圾回收机制,程序员无需手动释放内存。 3. 集合框架:Java集合框架提供了一组接口(如List、Set、Map)和实现这些接口的类,方便存储和操作对象。 4. 多线程:Java内置对多线程的支持,...

    基于计算机软件开发的JAVA编程语言探究.pdf

    - 简单性:与C语言等其他编程语言相比,JAVA不支持运算符的重载和多级继承,内存空间更大,自动垃圾回收能力提高,从而在操作过程中变得方便简单。 - 平台独立性:JAVA虚拟机的概念屏蔽了不同平台的具体要求,使得...

    java jdk 1.6 1.7 1.8 源码

    这些源码不仅适合初学者学习Java的基本概念,也适用于有经验的开发者深入探究Java的内部机制,例如垃圾回收、内存管理、编译器优化等方面。通过对比不同版本的源码,我们可以看到Java语言的发展历程和设计思想的演变...

    关于Java的练习,其中包括Java基础、Java理解、Oracle数据库、servlet等.zip

    其次,Java理解是指深入探究Java的运行机制,如垃圾回收(Garbage Collection)和内存管理,线程同步机制,以及JVM(Java虚拟机)的工作原理。理解这些概念有助于优化程序性能,避免并发问题,并能更好地调试和诊断...

    JAVA编程语言在计算机软件开发应用中的探究 (2).zip

    3. **自动内存管理**:JAVA提供了垃圾回收机制,自动处理内存分配和释放,避免了内存泄露问题。 4. **安全性**:JAVA的安全模型设计严格,能有效防止病毒和恶意代码的传播。 5. **稳定性和可扩展性**:JAVA的异常...

    狂神说java系列笔记(java基础+javaweb+ssm+微服务)全套

    10. **33、JVM探究.pdf**:深入Java虚拟机,理解内存模型、垃圾回收、性能优化等方面的知识。 通过这些内容的学习,读者不仅可以掌握Java编程的基本技能,还能了解到如何在实际项目中运用这些技能,提升自己的软件...

    怎么学习Java怎么学习Java

    在掌握Java的基础语法后,应深入学习Java的精华特性,比如垃圾回收机制、异常处理、集合框架等,并理解它们的设计理念。同时,了解并掌握设计模式是非常重要的一步,设计模式如单例模式、工厂模式、装饰者模式等,...

    狂神说JVM探究.rar

    - 堆内存:存放对象实例,进行垃圾回收的主要区域。 - 方法区(非堆):存储已加载的类信息、常量、静态变量等。 - 运行时常量池:方法区的一部分,存储编译期生成的各种字面量和符号引用。 4. **垃圾收集(GC)...

    FOR UPLOAD_java_源码

    它的特性包括跨平台性、强大的类库支持、垃圾回收机制以及严格的类型检查等。 根据压缩包子文件的文件名称列表,我们可以推测这些文件并非Java源代码,而是与生物学相关的资料,如讲座笔记或课件,如“Biology 2 ...

    java高级技能全体系知识结构梳理全景图

    首先,Java高级技能包括对Java语言特性的深入理解和运用,如多线程、并发编程、内存管理(垃圾回收机制)、反射以及动态代理等。深入理解这些特性,可以使开发者在编写高效、稳定、可维护的代码时游刃有余。 1. 多...

    Java虚拟机规范(中文版).pdf

    该规范不仅对Java开发者至关重要,也是深入探究Java语言特性的必备文献。 #### 规范的作用与地位 JVM规范并不是特定虚拟机实现的说明书,而是确保所有JVM实现保持一致性的合同文档。它定义了JVM的概念模型,即一个...

    java面试评价表

    - **GC机制与原理**:深入理解垃圾回收机制的工作原理,包括不同类型的GC(年轻代GC、老年代GC)触发条件。 - **ClassLoader**:讲解JVM中的类加载机制,包括自定义类加载器的实现。 - **双亲委派模型**:解释双亲...

    java基础讲课安排

    8. **垃圾回收机制**:了解Java的自动内存管理机制。 9. **Javadoc**:学习如何生成和使用API文档。 **第三阶段:Java核心API与高级特性** 1. **Java API**:熟悉常用类库,如String、Collections框架、IO流等。 2....

Global site tag (gtag.js) - Google Analytics