`
jiuyuehe
  • 浏览: 181456 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

java 内存的管理 转载▼

阅读更多
java 内存的管理 转载▼

内存的管理(1)

使用Java无须担心如何销毁对象。换句话说,就是在Java运行时,无须负责Java对象的内存管理。当Java不再使用某个对象时,它会自动进行垃圾回收。

垃圾回收是一个比较复杂的过程,当程序运行时会自动检查整个内存,检查内存中哪些对象引用不再被使用。一旦检查出来后,便会安全删除这些对象。然而,由于垃圾回收需要占用系统的资源,所以它可能会影响应用程序代码的运行,即如果在执行应用程序代码的过程中,执行垃圾回收,则应用程序代码的执行时间可能延长,这会导致程序运行的延迟。由于不知道何时会进行垃圾回收,因此延迟的时间也是不可预知的。实时应用程序对时间的要求非常严格,即它们必须在确定的、已知的延迟条件下,执行应用程序代码,因此垃圾回收所引起的不可预知的延迟,就成为一个实时程序致命的问题。

垃圾回收的主要问题是程序无法估计时间延迟导致程序执行的延迟。能否避免这种问题的发生呢?通过更频繁地进行垃圾回收,就可以限制最大延迟时间,因此使垃圾回收成为可预知的。

面试题10  垃圾回收

考题题干

下面哪一种说法是正确的,请选择一个正确的答案。

A.利用关键词delete可以明确地销毁对象
B.对象变得不可达后马上被垃圾收集掉
C.如果对象obja对于对象objb而言是可妥的,对象objb对于对象obja而言也是可妥的,则obja和objb都不适用于垃圾收集
D.对象一旦变得适用于垃圾收集,则在被销毁之前它会保持着这种适用性
E.如果对象obja可以访问适用于垃圾收集的对象objb,那么obja同样适用于垃圾收集

试题分析

在本面试题中,如果所有声名的对象引用都是来自其他也适合进行垃圾收集的对象,这个对象就是最适合进行垃圾收集的。所以,如果对象objb适合进行垃圾收集,而且对象obja包含执行objb的引用,那么对象obja也必须进行垃圾收集。Java没有delete关键词。对象在变得不可达之后不必马上作为垃圾被收集,该对象只是适合垃圾收集。

只要对象可以被任何存活线程访问,就不适合进行垃圾收集,一个已经成为垃圾收集目标的对象还可以消除这种适合性。当对象的finalize()方法创建了一个指向该对象的可达引用时,就可以出现这种情况。

参考答案

E

【深入学习】

1.何时对象被丢弃

Java语言规范没有明确地说明JVM使用哪种垃圾回收算法,但是任何一种垃圾收集算法一般要做两件基本的事情:(1)发现无用信息对象;(2)回收被无用对象占用的内存空间,使该空间可被程序再次使用。

大多数垃圾回收算法使用了根集(root set)这个概念;所谓根集就是正在执行的Java程序可以访问的引用变量的集合(包括局部变量、参数、类变量),程序可以使用引用变量访问对象的属性和调用对象的方法。垃圾收集首先需要确定从根开始哪些是可达的和哪些是不可达的,从根集可达的对象都是活动对象,它们不能作为垃圾被回收,这也包括从根集间接可达的对象。而根集通过任意路径不可达的对象符合垃圾收集的条件,应该被回收。

Java的垃圾回收机制一般包含近10种算法。对这些算法中的多数我们不必予以关心。其中最简单的一个:引用计数法,该方法是唯一没有使用根集的垃圾回收的算法,该算法使用引用计数器来区分存活对象和不再使用的对象。也就是说,当应用程序创建引用以及引用超出范围时,JVM必须适当增减引用数。当某对象的引用数为0时,便可以进行垃圾收集。在使用JVM的垃圾回收机制对堆空间做实时检测的时候,发现当某对象的引用计数为0时,就将该对象列入待回收列表中。

2.对象被丢弃,是否立即回收

如果一个对象赋值为null或者重新定向了该对象的引用者,则该对象被认定为没有存在的必要了,那么它所占用的内存就可以被释放。被回收的内存可以用于后续的再分配。

但是,并不是对象被抛弃后立即被回收的。用JVM进程做空间回收是有较大的系统开销的。在实际的项目开发中,丢弃一个对象,创建一个对象,这样的操作不计其数。如果每当某一应用进程丢弃一个对象,JVM就立即回收它的空间,势必会使整个系统的运转效率非常低下。

前面说过,JVM的垃圾回收机制有多个算法。除了引用计数法是用来判断对象是否已被抛弃外,其他算法是用来确定何时及如何进行回收。JVM的垃圾回收机制要在时间和空间之间做个平衡。

因此,为了提高系统效率,垃圾回收器通常只在满足两个条件时才运行:有对象要回收且系统需要回收。切记垃圾回收要占用时间,因此,Java运行时系统只在需要的时候才使用它。因此用户无法知道垃圾回收发生的精确时间。

3.垃圾回收

许多人对Java的垃圾回收不放心,希望在应用代码里控制JVM的垃圾回收运作。这是不可能的事。对垃圾回收机制来说,应用只有两个途径发消息给JVM。第一个前面已经说了,就是将指向某对象的所有引用变量全部移走。这就相当于向JVM发了一个消息:这个对象不再需要了。第二个是调用库方法System.gc(),多数书里说调用它让Java进行垃圾回收。

GC即垃圾收集机制是指JVM用于释放那些不再使用的对象所占用的内存。Java语言并不要求JVM有GC,也没有规定GC如何工作。不过常用的JVM都有GC,而且大多数GC都使用类似的算法管理内存和执行收集操作。

如果第一种方式是一个通知,那么调用System.gc()也仅仅算是一个请求。JVM接受这个消息后,并不是立即进行垃圾回收,而只是对几个垃圾回收算法进行加权,使垃圾回收操作容易发生,或提早发生,或回收较多而已。

在大多数的系统中认为希望JVM及时回收垃圾,是一种提高内存运行效率的需求。其实,还有些系统会提出相反的一种需要:在某段时间内最好不要回收垃圾。例如要求运行速度最快的实时系统,特别是嵌入式系统,往往希望如此。

Java的垃圾回收机制是为所有Java应用进程服务的,而不是为某个特定的进程服务的。因此,任何一个进程都没有权利去命令垃圾回收机制做什么、怎么做或做多少。
内存的管理(2)
4.finalize()方法
JVM垃圾收集器收集一个对象之前,一般要求程序调用适当的方法释放资源,但在没有明确释放资源的情况下,Java提供了默认机制来终止化该对象并释放资源,这个方法就是finalize()。它的原型为:
protected void finalize() throws Throwable
finalize是位于Object类的一个方法,该方法的访问修饰符为protected,由于所有类为Object的子类,因此用户类很容易访问到这个方法。
Java技术允许使用finalize()方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。这个方法是由垃圾收集器在确定这个对象没有被引用时对这个对象调用的。它是在Object类中定义的,因此所有的类都继承了它。子类覆盖finalize()方法以整理系统资源或者执行其他清理工作。finalize()方法是在垃圾收集器删除对象之前对这个对象调用的。由于finalize函数没有自动实现链式调用,我们必须手动地实现,因此finalize函数的最后一个语句通常是super.finalize()。通过这种方式,我们可以从下到上实现finalize的调用,即先释放自己的资源,然后再释放父类的资源。
根据Java语言规范,JVM保证调用finalize函数之前,这个对象是不可达的,但是JVM不保证这个函数一定会被调用。另外,规范还保证finalize函数最多运行一次。在finalize()方法返回之后,对象消失,垃圾收集开始执行。原型中的throws Throwable表示它可以抛出任何类型的异常。之所以要使用finalize(),是由于有时需要采取与Java的普通方法不同的一种方法,通过分配内存来做一些具有C风格的事情。这主要可以通过"固有方法"来进行,它是从Java里调用非Java方法的一种方式。
很多Java初学者会认为这个方法类似于C++中的析构函数,将很多对象、资源的释放都放在这一函数里面。其实,这不是一种很好的方式。原因有三:其一,GC为了能够支持finalize函数,要对覆盖这个函数的对象做很多附加的工作。其二,在finalize运行完成之后,该对象可能变成可达的,GC还要再检查一次该对象是否是可达的。因此,使用finalize会降低GC的运行性能。其三,由于GC调用finalize的时间是不确定的,因此通过这种方式释放资源也是不确定的。
面试题11  对象在什么地方变得适用于垃圾收集
考题题干
对于最初由gc4所引用的对象,该对象在如下程序中的什么地方变得适用于垃圾收集?
1   public class GcDemo { 
2       public static void main(String[] args) { 
3           String gc1; 
4           String gc2 = "This name was called with gc2 "; 
5           String gc3 = " This name was called with gc3"; 
6           String gc4 = new String((args.
length > 0) ? "" + args[0] + "'"
7                   : "< no argument>"); 
8               gc1 = gc4; 
9           gc4 = null;  
10          gc1 = gc2 + gc1 + gc3; 
11          gc2 = null;  
12          System.out.println(gc1); 
13          gc1 = null;  
14          gc3 = null;  
15          args = null; 
16      } 
17  }
A.从标签为9的那一行以后
B.从标签为10的那一行以后
C.从标签为11的那一行以后
D.从标签为13的那一行以后
E.从标签为14的那一行以后
F.从标签为15的那一行以后
试题分析
如果一个对象赋值为null或者重新定向了该对象的引用者,则该对象被认定为变得适用于垃圾收集。在第9行之前,最初被gc4引用的String对象由gc1和gc4来表示。在第9行之后,该String对象就只由gc1来表示。在第10行之后,引用gc1被赋予一个null值,即不再指向该对象的引用。而第10行之后,该对象已经没有引用了。
参考答案
B
面试题12  对垃圾回收的正确描述
考题题干
关于垃圾回收的叙述,下面哪些选项是正确的?
A.保持对不可达对象的跟踪
B.可以直接回收具体的对象
C.能够保证Java程序永远不会耗尽内存
D.作为一个低优先级的线程在后台运行
试题分析
Java的垃圾回收并不能保证内存的耗尽;垃圾回收只是一个低优先级的后台线程,而且跟踪可达或者不可达对象。
参考答案
AD
面试题13  关于finalize()的正确描述
考题题干
下面有关Java垃圾收集的说法,哪一个是正确的?
A.所有的对象都有一个finalize()方法
B.可以通过明确调用finalize()方法而销毁对象
C.finalize()方法声明时可以带有任何可访问性修饰符
D.对于适用于垃圾收集的对象而言,如果它的finalize()方法在执行期间抛出了异常,则Java会忽略该异常并销毁该对象
E.如果代码中所定义的覆盖finalize()方法并没有明确调用父类中的被覆盖finalize()方法,则编译器将无法编译
试题分析
Object对象有一个finalize()方法,由于所有的类都是从Object类继承来的,因此,所有的对象都有一个finalize()方法,所以A选项是正确的。
类可以覆盖finalize()方法,而且和普通的方法覆盖规则一样,不能降低finalize()方法的可访问性。调用finalize()方法本身不会破坏该对象。所以B选项和C选项是错误的。
当JVM的拦截收集器调用一个合适对象的finalize()方法时,它会忽略任何由finalize()方法抛出的异常。在其他情况下,finalize()方法中的异常处理并没有特殊的规定,同普通方法处理异常是一样的,所以D选项是错误的。子类继承父类,则自动拥有父类的全部的public的属性和方法,所以E选项的说法显然也是错误的。
参考答案
A

内存的管理(3)
【深入学习】当JVM处于空闲循环时,垃圾收集器线程会自动检查每一块分配出去的内存空间,然后自动回收每一块可以回收的无用的内存块。垃圾收集器线程是一种低优先级的线程,在一个Java程序的生命周期中,它只有在内存空闲的时候才有机会运行。
垃圾收集器的主要特点有:
(1)垃圾收集器的工作目标是回收已经无用的对象的内存空间,从而避免内存渗漏体的产生,节省内存资源,避免程序代码的崩溃。
(2)垃圾收集器判断一个对象的内存空间是否无用的标准是:如果该对象不能再被程序中任何一个"活动的部分"所引用,此时我们就说,该对象的内存空间已经无用。所谓"活动的部分"是指程序中某部分参与程序的调用,正在执行过程中,且尚未执行完毕。
(3)垃圾收集器线程虽然是作为低优先级的线程运行,但在系统可用内存量过低的时候,它可能会突发地执行来挽救内存资源。
(4)垃圾收集器不可以被强制执行,但程序员可以通过调用System.gc方法来建议执行垃圾收集器。
(5)不能保证一个无用的对象一定会被垃圾收集器收集,也不能保证垃圾收集器在一段Java语言代码中一定会执行。因此在程序执行过程中被分配出去的内存空间可能会一直保留到该程序执行完毕,除非该空间被重新分配或被其他方法回收。由此可见,完全彻底地根绝内存渗漏体的产生也是不可能的。但是请不要忘记,Java的垃圾收集器毕竟使程序员从手工回收内存空间的繁重工作中解脱了出来。设想一个程序员要用C或C++来编写一段10万行语句的代码,那么他一定会充分体会到Java垃圾收集器的优点!
(6)同样没有办法预知在一组均符合垃圾收集器收集标准的对象中,哪一个会被首先收集。
(7)循环引用对象不会影响其被垃圾收集器收集。
(8)可以通过将对象的引用变量(reference variables,即句柄handles)初始化为null值,来暗示垃圾收集器来收集该对象。但此时,如果该对象连接有事件监听器(典型的 AWT组件),那它还是不可以被收集。所以在设一个引用变量为null值之前,应注意该引用变量指向的对象是否被监听,若有,则要首先除去监听器,然后才可以赋空值。
(9)每一个对象都有一个finalize( )方法,这个方法是从Object类继承来的。
(10)finalize( )方法用来回收内存以外的系统资源,就像是文件处理器和网络连接器。该方法的调用顺序和用来调用该方法对象的创建顺序是无关的。换句话说,编写程序时该方法的顺序和方法的实际调用顺序是不相干的。请注意这只是finalize( )方法的特点。
(11)每个对象只能调用finalize( )方法一次。如果在finalize( )方法执行时产生异常(exception),则该对象仍可以被垃圾收集器收集。
(12)垃圾收集器跟踪每一个对象,收集那些不可到达的对象(即该对象没有被程序任何"活的部分"所调用),回收其占有的内存空间。但在进行垃圾收集的时候,垃圾收集器会调用finalize( )方法,通过让其他对象知道它的存在,而使不可到达的对象再次"复苏"为可到达的对象。既然每个对象只能调用一次finalize( )方法,所以每个对象也只可能"复苏"一次。
(13)finalize( )方法可以明确地被调用,但它却不能进行垃圾收集。
(14)finalize( )方法可以被重载(overload),但只有具备初始finalize( )方法特点的方法才可以被垃圾收集器调用。
(15)子类的finalize()方法可以明确地调用父类的finalize()方法,作为该子类对象的最后一次适当的操作。但Java编译器却不认为这是一次覆盖操作(overriding),所以也不会对其调用进行检查。
(16)当finalize( )方法尚未被调用时,System. runFinalization( )方法可以用来调用finalize( )方法,并实现相同的效果,对无用对象进行垃圾收集。
(17)当一个方法执行完毕,其中的局部变量就会超出使用范围,此时可以被当作垃圾收集,但以后每当该方法再次被调用时,其中的局部变量便会被重新创建。
(18)Java语言使用了一种"标记交换区的垃圾收集算法"。该算法会遍历程序中每一个对象的句柄,为被引用的对象做标记,然后回收尚未做标记的对象。
(19)Java语言允许程序员为任何方法添加finalize( )方法,该方法会在垃圾收集器交换回收对象之前被调用。但不要过分依赖该方法对系统资源进行回收和再利用,因为该方法调用后的执行结果是不可预知的。
通过以上对垃圾收集器特点的了解,你应该可以明确垃圾收集器的作用以及垃圾收集器判断一块内存空间是否无用的标准。简单地说,当你将一个对象赋值为null并且重新定向了该对象的引用者,此时该对象就符合垃圾收集器的收集标准。
判断一个对象是否符合垃圾收集器的收集标准,这是Sun公司程序员认证考试中垃圾收集器部分的重要考点(也可以说,这是唯一的考点)。

内存的管理(4)
面试题14  垃圾收集
考题题干
关于垃圾收集的哪些叙述是正确的?
A.垃圾收集将检查并释放不再使用的内存
B.垃圾收集允许程序开发者明确指定并立即释放该内存
C.程序开发者必须自己创建一个线程进行内存释放的工作
D.垃圾收集能够在期望的时间内释放被Java对象使用的内存
试题分析
Java语言将内存分配和释放的工组交给了自己,程序员不必做这些工作,它提供一个系统级的线程跟踪每个内存的分配,在JVM的空闲处理中,垃圾收集线程将检查和释放不再使用的内存(即可以被释放的内存)。垃圾收集的过程在Java程序的生存期中是自动的,不需要分配和释放内存,也避免了内存泄漏。可以调用System.gc()方法建议JVM执行垃圾收集以使得可被释放的内存能立即被使用,当此方法返回的时候,JVM已经做了最大的努力从被丢弃的对象上回收内存空间。程序员不能指定收集哪些内存,一般而言也不用关心这个问题,除非是程序的内存消耗很大,特别是有很多临时对象时可以"建议"进行垃圾收集以提高可用内存。需要指出的是调用System.gc()方法不能保证JVM立即进行垃圾收集,而只能是建议,因为垃圾收集线程的优先级很低(通常是最低的)。
在JVM垃圾收集器收集一个对象之前,一般要求程序调用适当的方法释放资源,但在没有明确释放资源的情况下,Java提供了默认机制来终止化该对象并释放资源,这个方法就是finalize( )。因此选项A是正确的。
垃圾收集器不可以被强制执行,但程序员可以通过调用System. gc方法来建议执行垃圾收集器。因此选项B是错误的。
Java的垃圾回收机制是为所有Java应用进程服务的,而不是为某个特定的进程服务的。因此,任何一个进程都没有权利去命令垃圾回收机制做什么、怎么做或做多少。因此选项C是错误的。
Java运行时系统只在需要的时候才使用垃圾收集。因此用户无法知道垃圾回收发生的精确时间。同样没有办法预知在一组均符合垃圾收集器收集标准的对象中,哪一个会被首先收集。因此选项D是错误的。
参考答案
A
面试题15  Sun公司程序员考试中的常见题型
考题题干
在下面的代码段中,在1~4行中第几行的obj符合垃圾收集器的收集标准?在5~8行中第几行的内存空间符合垃圾收集器的收集标准?
1 obj = new Object ( ) ;
2 obj. Method ( ) ;
3 obj = new Object ( ) ;
4 obj. Method ( ) ;
5 Object nobj = new Object ( ) ;
6 Object nobj = null ;
7 Object nobj = new Object ( ) ;
8 nobj = new Object ( ) ;
参考答案
(1)第3行,因为第3行的obj被赋了新值,产生了一个新的对象,即换了一块新的内存空间,也相当于为第1行中obj赋了null值。
(2)第5行和第7行。因为第6行为nobj赋值为null,所以在此第5行的nobj符合垃圾收集器的收集标准。而第8行相当于为nobj赋值为null,所以在此第7行的nobj也符合垃圾收集器的收集标准。
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics