对java的一些总结<二>

Chapter4: GC机制

4.1GC基本概述

       一。谁在做Garbage Collection

       一种流行的说法:在C++里,是系统在做垃圾回收;而在Java里,是Java自身在做。在C++里,释放内存是手动处理的,要用delete运算符来释放分配的内存。这是流行的说法。确切地说,是应用认为不需要某实体时,就需用delete告诉系统,可以回收这块空间了。这个要求,对编码者来说,是件很麻烦、很难做到的事。随便上哪个BBS,在C/C++版块里总是有一大堆关于内存泄漏的话题。

    Java采用一种不同的,很方便的方法:Garbage Collection.垃圾回收机制放在JVM里。JVM完全负责垃圾回收事宜,应用只在需要时申请空间,而在抛弃对象时不必关心空间回收问题。

 二。对象在啥时被丢弃?

C++里,当对象离开其作用域时,该对象即被应用抛弃。是对象的生命期不再与其作用域有关,而仅仅与引用有关。Java的垃圾回收机制一般包含近十种算法。对这些算法中的多数,我们不必予以关心。只有其中最简单的一个:引用计数法,与编码有关。或者有一种探测是否有""的对象的方法,之后用"自适应"的回收技术[后面将会说到。一个对象,可以有一个或多个引用变量指向它。当一个对象不再有任何一个引用变量指向它时,这个对象就被应用抛弃了。或者说,这个对象可以被垃圾回收机制回收了。这就是说,当不存在对某对象的任何引用时,就意味着,应用告诉JVM:我不要这个对象,你可以回收了。JVM的垃圾回收机制对堆空间做实时检测。当发现某对象的引用计数为0时,就将该对象列入待回收列表中。但是,并不是马上予以销毁。

    三。丢弃就被回收?

    该对象被认定为没有存在的必要了,那么它所占用的内存就可以被释放。被回收的内存可以用于后续的再分配。但是,并不是对象被抛弃后当即被回收的。JVM进程做空间回收有较大的系统开销。如果每当某应用进程丢弃一个对象,就立即回收它的空间,势必会使整个系统的运转效率非常低下。前面说过,JVM的垃圾回收机制有多个算法。除了引用计数法是用来判断对象是否已被抛弃外,其它算法是用来确定何时及如何做回收。JVM的垃圾回收机制要在时间和空间之间做个平衡。因此,为了提高系统效率,垃圾回收器通常只在满足两个条件时才运行:即有对象要回收且系统需要回收。切记垃圾回收要占用时间,因此,Java运行时系统只在需要的时候才使用它。因此你无法知道垃圾回收发生的精确时间。

    四。没有引用变量指向的对象有用吗?

    前面说了,没挂上引用变量的对象是被应用丢弃的,这意味着,它在堆空间里是个垃圾,随时可能被JVM回收。不过,这里有个不是例外的例外。对于一次性使用的对象(有些书称之为临时对象),可以不用引用变量指向它。举个最简单也最常见的例子:

System.out.println(“I am Java!”);就是创建了一个字符串对象后,直接传递给println()法。

    五。应用能干预垃圾回收吗?

    许多人对Java的垃圾回收不放心,希望在应用代码里控制JVM的垃圾回收运作。这是不可能的事。对垃圾回收机制来说,应用只有两个途径发消息给JVM

 

4.2finalized方法

其实了解JAVA的人,都知道JAVAGC机制是其的一大优点,它令程序员不需要主动去考虑内存溢出和垃圾回收的问题,不像c++具有显式的析构函数对整个对象进行内存清理以及需要调用delete才可以进行显示的销毁对象。当然也有存在特殊的情况:假定你的对象(并非使用new方法)获得了一块“特殊”的内存区域,由于垃圾回收器只知道那些显示地经由new分配的内存空间,所以它不知道该如何释放这块“特殊”的内存区域,那么这个时候java允许在类中定义一个由finalize()方法。

对于上面这个问题,首先了解一下什么是“特殊”的内存区域?由于在分配内存的时候可能采用了类似 C语言的做法,而非JAVA的通常new做法。这种情况主要发生在native method中,比如native method调用了C/C++方法malloc()函数系列来分配存储空间,但是除非调用free()函数,否则这些内存空间将不会得到释放,那么这个时候就可能造成内存泄漏。但是由于free()方法是在C/C++中的函数,所以finalize()中可以用本地方法来调用它。以释放这些“特殊”的内存空间。另外finalize()方法还可以用作这样的用途:因为在JAVA中并没有提够像“析构”函数或者类似概念的函数,要做一些类似清理工作的时候,必须自己动手创建一个执行清理工作的普通方法,也就是override Object这个类中的finalize()方法。例如,假设某一个对象在创建过程中会将自己绘制到屏幕上,如果不是明确地从屏幕上将其擦出,它可能永远都不会被清理。如果在finalize()加入某一种擦除功能,当GC工作时,finalize()得到了调用,图像就会被擦除。要是GC没有发生,那么这个图像就会被一直保存下来。

其次,了解一下finalize()方法的工作原理:一旦垃圾回收器准备好释放对象占用的存储空间,首先会去调用finalize()方法进行一些必要的清理工作。只有到下一次再进行垃圾回收动作的时候,才会真正释放这个对象所占用的内存空间。

最后,必须一定要正视一个问题:finalize()并不是代替了delete()方法,来作为清理内存的方法。为什么呢?在C++中所有的对象运用delete()一定会被销毁,而JAVA里的对象并非总会被垃圾回收器回收。In another word, 1 对象可能不被垃圾回收,2 垃圾回收并不等于“析构”,3 垃圾回收只与内存有关。也就是说,并不是如果一个对象不再被使用,是不是要在finalize()中释放这个对象中含有的其它对象呢?不是的。因为无论对象是如何创建的,垃圾回收器都会负责释放那些对象占有的内存。

4.3:内存泄漏

所有与main()进程不再有任何引用关系的对象都被JVM视作垃圾,并会被适时回收;而 如果在需要废弃一个对象的时候,引用关系解除得不彻底,就会发生非预期的内存占用,即泄露。有没有什么工具能帮忙查出某个对象是否确实不再被引用、仅仅只是在等待GC来吃? 或者有什么办法实时地显示出JVM内部所有对象之间的引用关系?

举例: 有一个全局范围的容器Collection<Object> c 在局部范围内创建一个Object o,此时JVM从堆上划分一块内存创建对象XXX,交给o引用。因为某些需要,或者特殊情况,o被添加进c里,则此时c也会引用对象XXX。当o引用的对象XXX完成使命后,开发者为o赋了新值null,此时o不再引用对象XXX 由于某些情况,开发者忽略了步骤3,或者步骤3是背着开发者进行的,又或者开发者没有权限从c中删除对象XXX,则c将一直保持到对象XXX的引用,而对象XXX所占据的内存空间在进程生命周期内永远无法释放。

凑巧,这段代码依据一定的规则循环执行,则c内会继续添加对对象YYYZZZ等的引用,并且在这些对象的使命完成后也因为同样的原因而不释放空间。 天长日久,系统越来越慢,终于有一天,OutOfMemoryError,成为注定的结局…… 这种状况在使用外来组件时尤其容易发生,比如JDKLogger 

4.4:对象存储

其实要了解对象的释放,需要了解一下对象的存储,即对象是怎么样进行放置安排的呢?特别是内存中究竟是如何进行分配的呢?有五个不同的地方可以用来存储数据:寄存器。这是最快的存储区,因为它位于不同于其它地方的存储区位置——CPU内部。但是寄存器的数量是有限的,所以寄存器会根据需要进行分配。你不能直接控制,也不能在程序中感觉到寄存器存在的任何迹象。(C/C++是允许通过编译器建议寄存器的分配方式)

 栈,位于RAM中,但是通过栈指针可以从处理器那里获得直接的支持。栈指针向下移动,则分配新的存储空间;向上移动,则释放掉那些内存。这是一种快速有效的内存分配方式,仅次于寄存器。创建程序时,JAVA系统必须知道存储在栈所有项的确切的生命时期,以便于向下或者向上移动栈指针。这一约束也就限制了程序的灵活性,所以只有某些JAVA数据存储在栈中——基本数据类型以及对象引用。

堆,一种通用的内存池,这个也位于RAM中,用于存放JAVA所有的对象。堆不同于栈的好处是:编译器不需要知道存储的数据在堆内要存活多长的时间。因此,在堆里分配存储有很大的灵活性。当需要一个对象的时候,用new写一行代码,执行代码之后,会自动在堆中进行存储的分配。当然,为这种灵活性必须要付出的代价是:用堆进行存储分配和清理可能比用栈进行存储分配和清理花上的时间要多。堆是线程共同拥有的,但是栈确实线程独有的。

常量存储,常量值通常直接存放在程序代码内部。这样做是安全的,因为它们永远都不会被改变有时,在嵌入式系统中,常量本身会和其它部分隔开,所以在这种情况下,可以选择其他存放在ROM中。 RAM存储——持久化数据。如果数据完全存活在程序之外,那么它可以不受程序的控制。在程序没有运行时也可以存在,也就是持久性数据。其中,两个非常基本的例子——“流对象”和“持久化对象”。在流对象中,对象转化成字节流,通常被发送到另一台机器上。而在“持久化对象”中,对象将被存储在磁盘中,比如文件或者数据库。因此,即使程序终止了,他们仍然可以保持自己的数据状态。这种存储方式的技巧在于——把对象转化成可以存放在其他媒介上的事物,在需要的时候可以恢复成常规的、存储在 RAM的对象。JAVA提供了对轻量级持久化的支持,比如JDBC以及Hibernate这样的机制提供了更加复杂的对存储在数据库以及读取对象信息的支持。

(注:小弟水平有限,如有错误请指正)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值