`
long_yu2
  • 浏览: 310032 次
社区版块
存档分类
最新评论

从JVM内存管理的角度谈谈静态方法和静态属性 和 java对象引用与JVM自动内存管理

 
阅读更多

试着从JVM的内存管理原理的角度来谈一下静态方法和静态属性的问题,不对的地方请指正。 (joezheng123.javaeye.com/blog/264695)
JVM的内存分为两部分:stack和heap

stack(栈)是JVM的内存指令区。stack管理很简单,push一定长度字节的数据或者指令,stack指针压栈相应的字节位移;pop一定字节长度数据或者指令,stack指针弹栈。stack的速度很快,管理很简单,并且每次操作的数据或者指令字节长度是已知的。所以Java 基本数据类型,Java 指令代码,常量都保存在stack中。

heap (堆)是JVM的内存数据区。heap 的管理很复杂,每次分配不定长的内存空间,专门用来保存对象的实例。在heap 中分配一定的内存来保存对象实例,实际上也只是保存对象实例的属性值,属性的类型和对象本身的类型标记等,并不保存对象的方法(方法是指令,保存在stack中),在heap 中分配一定的内存保存对象实例和对象的序列化比较类似。而对象实例在heap 中分配好以后,需要在stack中保存一个4字节的heap 内存地址,用来定位该对象实例在heap 中的位置,便于找到该对象实例。

由于stack的内存管理是顺序分配的,而且定长,不存在内存回收问题;而heap 则是随机分配内存,不定长度,存在内存分配和回收的问题;因此在JVM中另有一个GC进程,定期扫描heap ,它根据stack中保存的4字节对象地址扫描heap ,定位heap 中这些对象,进行一些优化(例如合并空闲内存块什么的),并且假设heap 中没有扫描到的区域都是空闲的,统统refresh(实际上是把stack中丢失了对象地址的无用对象清除了),这就是垃圾收集的过程。

我们首先要搞清楚的是什么是数据,什么是指令?然后要搞清楚对象的方法和对象的属性分别保存在哪里?

为了便于描述,我简单的统称:

1)方法本身是指令的操作码部分,保存在stack中;

2)方法内部变量作为指令的操作数部分,跟在指令的操作码之后,保存在stack中(实际上是简单类型保存在stack中,对象类型在stack中保存地址,在heap 中保存值);

上述的指令操作码和指令操作数构成了完整的Java 指令。

3)对象实例包括其属性值作为数据,保存在数据区heap 中。

非静态的对象属性作为对象实例的一部分保存在heap 中,而对象实例必须通过stack中保存的地址指针才能访问到。因此能否访问到对象实例以及它的非静态属性值完全取决于能否获得对象实例在stack中的地址指针。

先分析一下非静态方法和静态方法的区别:

非静态方法有一个和静态方法很重大的不同:非静态方法有一个隐含的传入参数,该参数是JVM给它的,和我们怎么写代码无关,这个隐含的参数就是对象实例在stack中的地址指针。因此非静态方法(在stack中的指令代码)总是可以找到自己的专用数据(在heap 中的对象属性值)。当然非静态方法也必须获得该隐含参数,因此非静态方法在调用前,必须先new一个对象实例,获得stack中的地址指针,否则JVM将无法将隐含参数传给非静态方法。

而静态方法无此隐含参数,因此也不需要new对象,只要class文件被ClassLoader load进入JVM的stack,该静态方法即可被调用。当然此时静态方法是存取不到heap 中的对象属性的。

总结一下该过程:当一个class文件被ClassLoader load进入JVM后,方法指令保存在stack中,此时heap 区没有数据。然后程序技术器开始执行指令,如果是静态方法,直接依次执行指令代码,当然此时指令代码是不能访问heap 数据区的;如果是非静态方法,由于隐含参数没有值,会报错。因此在非静态方法执行前,要先new对象,在heap 中分配数据,并把stack中的地址指针交给非静态方法,这样程序技术器依次执行指令,而指令代码此时能够访问到heap 数据区了。

再说一下静态属性和动态属性:

前面提到对象实例以及动态属性都是保存在heap 中的,而heap 必须通过stack中的地址指针才能够被指令(类的方法)访问到。因此可以推断出:静态属性是保存在stack中的,而不同于动态属性保存在heap 中。正因为都是在stack中,而stack中指令和数据都是定长的,因此很容易算出偏移量,也因此不管什么指令(类的方法),都可以访问到类的静态属性。也正因为静态属性被保存在stack中,所以具有了全局属性。

总结一下:静态属性保存在stack指令内存区,动态属性保存在heap 数据内存区。

补充:1.在java中有一个字符常量的池,专用来存储常量

2.基本类型是保存在栈中,例如:

inta=9;先在栈中寻找是否有这个值,有的话将引用指向它,没有的话在上面创建这个值,接着把引用指向它
3.

栈的存取速度较快 仅次于位于cpu中的寄存器

但位于其中的数据大小和生存期必须是确定的 缺乏了灵活性 一般用于存储声明的变量

堆由于可动态分配内存 使其速度慢于栈 一般用于开辟对象空间

还由于生存期不必事先告诉编译器 事后得由gc收集

Java中非静态方法是否共用同一块内存?

将某 class 产生出一个 instance 之后,此 class 所有的 instance field 都会新增一份,那么所有的 instance method 是否也会新增一份?答案是不会,我们用field表示字段,用method表示方法,那么加上static区分后就 有四种:

class field:有用static修饰的field
class method:有用static修饰的method
instance field:没有用static修饰的field
instance method:没有用static修饰的method

那么他们在内存中的表示为:

class field:共用一块记忆体
class method:共用一块记忆体
instance field:随着每个instance各有一块记忆体
instance method:共用一块记忆体

如果instance method也随着instance增加而增加的话,那内存消耗也太大了,为了做到共用一小段内存,Java是根据this关键字做到的,比如:instance1.instanceMethod(); instance2.instanceMethod(); 在传递给对象参数的时候,Java编译器自动先加上了一个this参数,它表示传递的是这个对象引用,虽然他们两个对象共用一个方法,但是他们的方法中所产生的数据是私有的,这是因为参数被传进来变成call stack内的entry,而各个对象都有不同call stack,所以不会混淆。其实调用每个非static方法时,Java编译器都会自动的先加上当前调用此方法对象的参数,有时候在一个方法调用另一个方法,这时可以不用在前面加上this的,因为要传递的对象参数就是当前执行这个方法的对象。

为什么静态方法中不能调用非静态方法?这是因为静态方法直接跟class相关,调用此方法的时候是类直接调用的,而不是对象,所以Java编译器就没有对象参数可以传递,这样,如果你在静态方法内部调用非静态方法,那么Java编译器怎么判断这个非静态方法是哪个对象调用的?对吧,所以Java编译器就会报错,但是也不是绝对的,Java编译器是隐式的传递对象参数,那么我们总可以显示的传递对象参数吧,如果我们把某个对象的引用传递到static方法里,然后通过这个引用就可以调用非静态方法和访问非静态数据成员了。

解析Java对象引用与JVM自动内存管理

对象引用应用程序设计接口是JDKTM1.2中新定义的。该应用程序设计接口允许应用程序以对象引用的方式与JVM的内存管理器进行交互。当应用程序需管理大量内存对象或者在新的Java对象创建之前需删除原有对象时,Java对象引用应用程序设计接口具有相当大的用途,例如:

● 基于Web的应用程序常常要求显示大量图片,当用户离开某一Web页时,往往不能确定是否能够顺利的返回。在这种程序中,应用Java对象引用API可以创建这样一个环境,即当堆内存以最小程度运行时,内存管理器创建对象。当用户返回时,应用程序就会重新载入已经创建的图片。

● 应用对象引用队列可以创建这样一个环境,当通过对象引用获得某一对象时,应用程序得到通知。然后,应用程序就可以对相关对象进行清除操作,同时使这些对象在内存管理器中合法化。


内存管理器的工作机制


下面将首先介绍未嵌入引用对象时内存管理器的工作机制,然后讨论引用对象加入之后Java堆发生的变化。

内存管理器的作用就是识别程序中不再使用的对象,并且回收其内存。

一个Java应用程序由一系列线程组成,每个线程执行一系列方法,而每个方法通过参数或局部变量来引用对象。这些引用属于引用集合中的一部分,直接进入应用程序。另外,引用集合中还包括类库中定义的静态引用变量,以及通过Java本地接口(JNI)API获得的引用。引用集合中的所有引用对象都可以被当前应用程序获取,而不必被回收。同样地,这些对象可能包含对其它对象的引用,也可以被应用程序获取,依此类推。Java堆中的其它对象视为不可获取的,而所有这些不可获取的对象在内存管理中也是合法的。如果一个不可获取的对象使用finalize()方法,任务就交给了对象所调用的收尾器(finalizer)。在内存回收期间,不具有收尾器的不可获取对象和已经调用收尾器的对象被简单回收。

内存回收的算法是不断变化的,共性的方面是从引用集合中识别可获取的对象以及回收被其它对象占据的内存空间。

加入引用对象之后的引用与常规引用的区别在于,引用对象中的引用专门由内存管理器来处理。引用对象封装了其它一些对象的引用,我们称之为指示对象。在引用对象创建的同时,也就定义了该引用对象的指示对象。

根据应用程序要求,对象可以是强引用(strong references)、次引用(soft references)、弱引用(weak references)、虚引用(phantom references)的任意组合。为了确定对象的可获取程度,JVM内存管理器从引用集合出发遍寻堆中所有到对象的路径。当到达某对象的任意路径都不含有引用对象时,则称该对象具有强获取能力;当路径中含有一个或几个引用对象时,根据内存管理器所查询的引用对象的类型分别归为次获取、弱获取、虚获取。

另外,对象引用API中还定义了引用对象队列(java.lang.ref.ReferenceQueue),这是内存管理器对引用对象进行管理的一种简单数据结构。值得注意的是,在进行引用对象定义时,要求phantom reference对象必须产生于一个引用对象队列,而soft reference和weak reference对象则无此限制,如:

ReferenceQueue queue = new ReferenceQueue();
PhantomReference pr = new PhantomReference(object, queue);

Soft References 应用实例


下面以在基于web的应用程序中使用soft references为例,来说明Java对象引用与JVM的内存管理器进行交互的原理。

当用户打开某一web页时,applet代码获得图片并且得到显示。如果在代码中同时创建了该图片对象的soft references,那么当用户离开该web页时,内存管理器对图片所分配的内存是否回收做出选择。当用户返回该web页时,在applet代码中使用SoftReference.get方法就会得到图片才内存中是否仍存在的消息。如果在内存管理器中未创建该图片,在web页上会很快得到显示;否则,applet代码就会重新获取。

下面是Example.java的完整源代码。



import java.awt.Graphics;import java.awt.Image;import java.applet.Applet;import java.lang.ref.SoftReference;public class Example extends Applet { SoftReference sr = null; public void init() { System.out.println("Initializing"); } public void paint(Graphics g) { Image im = (sr == null) ? null : (Image)(sr.get()); if (im == null) { System.out.println("Fetching image"); im = getImage(getCodeBase(),"yundong.gif"); sr = new SoftReference(im); } System.out.println("Painting"); g.drawImage(im, 25, 25, this); g.drawString("运动之美",20,20); im = null; /* Clear the strong reference to the image */ } public void start() { System.out.println("Starting"); } public void stop() { System.out.println("Stopping"); }}


在上面的代码中,对象image是一个图片对象,传递给一个SoftReference对象sr。其中image对象是sr的指示对象,sr中的引用域是从次引用(soft reference)到 image。


Weak References分析


对于一个稳定的对象,比如说线程类对象,当需要获取外部数据时,在程序中应用weak references是非常理想的。如果利用引用队列创建了某一线程的weak reference,那么当线程不再具有强获取能力时,应用程序得到通知,根据此通知,应用程序才能执行相关数据对象的清除工作。

当内存管理器未发现strong references 和 soft references 时,我们称对象具有弱获取能力,即在到达该对象的路径中至少包含一个weak reference。程序中weak references被清除一段时间后,弱获取对象被收尾器收集。由此也可以看出,soft reference和weak reference之间的区别在于,应用soft reference时,内存管理器利用算法决定是否创建弱获取对象,而应用weak reference时,内存管理器必须创建次获取对象。


引用对象链


当到达某一对象的路径中含有多个引用对象时,就构成了引用对象链。内存管理器按照由强到弱的顺序处理引用对象,具体处理步骤包括:Soft references、 Weak references、Finalization、Phantom references和创建对象五个部分。

当内存管理器未发现前三种对象引用时,我们称对象具有虚获取能力,即在到达该对象的路径中至少包含一个phantom reference。虚引用对象直接被收尾器收集,而不被重新创建。当内存管理器发现只有phantom references时,对象就将处于等候phantom reference状态,应用程序向引用队列发出通知,然后对虚引用对象调用clear()方法,将其引用域设置为null,最后对不可获取对象执行收集清除处理任务。

通常,对象所具有的获取能力与引用对象集合直接路径中的最弱连接者相同。据此可以看出:

虚引用对象具有强获取能力,其它对象均具虚获取能力;

(b)中虚引用对象和弱引用对象均具强获取能力,故次引用对象和对象集合具有若获取能力;

(c)中虚引用对象、弱引用对象和次引用对象均具强获取能力,那么对象集合则具次获取能力。

● 在程序中使用引用对象API不但可以在一定程度上控制内存管理器,实现内存自动管理,还可以提高程序的稳定性和安全性。

● 引用对象链中各个对象的获取能力与整个链相关。


试着从JVM的内存管理原理的角度来谈一下静态方法和静态属性的问题,不对的地方请指正。 (joezheng123.javaeye.com/blog/264695)
JVM的内存分为两部分:stack和heap

stack(栈)是JVM的内存指令区。stack管理很简单,push一定长度字节的数据或者指令,stack指针压栈相应的字节位移;pop一定字节长度数据或者指令,stack指针弹栈。stack的速度很快,管理很简单,并且每次操作的数据或者指令字节长度是已知的。所以Java 基本数据类型,Java 指令代码,常量都保存在stack中。

heap (堆)是JVM的内存数据区。heap 的管理很复杂,每次分配不定长的内存空间,专门用来保存对象的实例。在heap 中分配一定的内存来保存对象实例,实际上也只是保存对象实例的属性值,属性的类型和对象本身的类型标记等,并不保存对象的方法(方法是指令,保存在stack中),在heap 中分配一定的内存保存对象实例和对象的序列化比较类似。而对象实例在heap 中分配好以后,需要在stack中保存一个4字节的heap 内存地址,用来定位该对象实例在heap 中的位置,便于找到该对象实例。

由于stack的内存管理是顺序分配的,而且定长,不存在内存回收问题;而heap 则是随机分配内存,不定长度,存在内存分配和回收的问题;因此在JVM中另有一个GC进程,定期扫描heap ,它根据stack中保存的4字节对象地址扫描heap ,定位heap 中这些对象,进行一些优化(例如合并空闲内存块什么的),并且假设heap 中没有扫描到的区域都是空闲的,统统refresh(实际上是把stack中丢失了对象地址的无用对象清除了),这就是垃圾收集的过程。

我们首先要搞清楚的是什么是数据,什么是指令?然后要搞清楚对象的方法和对象的属性分别保存在哪里?

为了便于描述,我简单的统称:

1)方法本身是指令的操作码部分,保存在stack中;

2)方法内部变量作为指令的操作数部分,跟在指令的操作码之后,保存在stack中(实际上是简单类型保存在stack中,对象类型在stack中保存地址,在heap 中保存值);

上述的指令操作码和指令操作数构成了完整的Java 指令。

3)对象实例包括其属性值作为数据,保存在数据区heap 中。

非静态的对象属性作为对象实例的一部分保存在heap 中,而对象实例必须通过stack中保存的地址指针才能访问到。因此能否访问到对象实例以及它的非静态属性值完全取决于能否获得对象实例在stack中的地址指针。

先分析一下非静态方法和静态方法的区别:

非静态方法有一个和静态方法很重大的不同:非静态方法有一个隐含的传入参数,该参数是JVM给它的,和我们怎么写代码无关,这个隐含的参数就是对象实例在stack中的地址指针。因此非静态方法(在stack中的指令代码)总是可以找到自己的专用数据(在heap 中的对象属性值)。当然非静态方法也必须获得该隐含参数,因此非静态方法在调用前,必须先new一个对象实例,获得stack中的地址指针,否则JVM将无法将隐含参数传给非静态方法。

而静态方法无此隐含参数,因此也不需要new对象,只要class文件被ClassLoader load进入JVM的stack,该静态方法即可被调用。当然此时静态方法是存取不到heap 中的对象属性的。

总结一下该过程:当一个class文件被ClassLoader load进入JVM后,方法指令保存在stack中,此时heap 区没有数据。然后程序技术器开始执行指令,如果是静态方法,直接依次执行指令代码,当然此时指令代码是不能访问heap 数据区的;如果是非静态方法,由于隐含参数没有值,会报错。因此在非静态方法执行前,要先new对象,在heap 中分配数据,并把stack中的地址指针交给非静态方法,这样程序技术器依次执行指令,而指令代码此时能够访问到heap 数据区了。

再说一下静态属性和动态属性:

前面提到对象实例以及动态属性都是保存在heap 中的,而heap 必须通过stack中的地址指针才能够被指令(类的方法)访问到。因此可以推断出:静态属性是保存在stack中的,而不同于动态属性保存在heap 中。正因为都是在stack中,而stack中指令和数据都是定长的,因此很容易算出偏移量,也因此不管什么指令(类的方法),都可以访问到类的静态属性。也正因为静态属性被保存在stack中,所以具有了全局属性。

总结一下:静态属性保存在stack指令内存区,动态属性保存在heap 数据内存区。

补充:1.在java中有一个字符常量的池,专用来存储常量

2.基本类型是保存在栈中,例如:

inta=9;先在栈中寻找是否有这个值,有的话将引用指向它,没有的话在上面创建这个值,接着把引用指向它
3.

栈的存取速度较快 仅次于位于cpu中的寄存器

但位于其中的数据大小和生存期必须是确定的 缺乏了灵活性 一般用于存储声明的变量

堆由于可动态分配内存 使其速度慢于栈 一般用于开辟对象空间

还由于生存期不必事先告诉编译器 事后得由gc收集

Java中非静态方法是否共用同一块内存?

将某 class 产生出一个 instance 之后,此 class 所有的 instance field 都会新增一份,那么所有的 instance method 是否也会新增一份?答案是不会,我们用field表示字段,用method表示方法,那么加上static区分后就 有四种:

class field:有用static修饰的field
class method:有用static修饰的method
instance field:没有用static修饰的field
instance method:没有用static修饰的method

那么他们在内存中的表示为:

class field:共用一块记忆体
class method:共用一块记忆体
instance field:随着每个instance各有一块记忆体
instance method:共用一块记忆体

如果instance method也随着instance增加而增加的话,那内存消耗也太大了,为了做到共用一小段内存,Java是根据this关键字做到的,比如:instance1.instanceMethod(); instance2.instanceMethod(); 在传递给对象参数的时候,Java编译器自动先加上了一个this参数,它表示传递的是这个对象引用,虽然他们两个对象共用一个方法,但是他们的方法中所产生的数据是私有的,这是因为参数被传进来变成call stack内的entry,而各个对象都有不同call stack,所以不会混淆。其实调用每个非static方法时,Java编译器都会自动的先加上当前调用此方法对象的参数,有时候在一个方法调用另一个方法,这时可以不用在前面加上this的,因为要传递的对象参数就是当前执行这个方法的对象。

为什么静态方法中不能调用非静态方法?这是因为静态方法直接跟class相关,调用此方法的时候是类直接调用的,而不是对象,所以Java编译器就没有对象参数可以传递,这样,如果你在静态方法内部调用非静态方法,那么Java编译器怎么判断这个非静态方法是哪个对象调用的?对吧,所以Java编译器就会报错,但是也不是绝对的,Java编译器是隐式的传递对象参数,那么我们总可以显示的传递对象参数吧,如果我们把某个对象的引用传递到static方法里,然后通过这个引用就可以调用非静态方法和访问非静态数据成员了。

解析Java对象引用与JVM自动内存管理

对象引用应用程序设计接口是JDKTM1.2中新定义的。该应用程序设计接口允许应用程序以对象引用的方式与JVM的内存管理器进行交互。当应用程序需管理大量内存对象或者在新的Java对象创建之前需删除原有对象时,Java对象引用应用程序设计接口具有相当大的用途,例如:

● 基于Web的应用程序常常要求显示大量图片,当用户离开某一Web页时,往往不能确定是否能够顺利的返回。在这种程序中,应用Java对象引用API可以创建这样一个环境,即当堆内存以最小程度运行时,内存管理器创建对象。当用户返回时,应用程序就会重新载入已经创建的图片。

● 应用对象引用队列可以创建这样一个环境,当通过对象引用获得某一对象时,应用程序得到通知。然后,应用程序就可以对相关对象进行清除操作,同时使这些对象在内存管理器中合法化。


内存管理器的工作机制


下面将首先介绍未嵌入引用对象时内存管理器的工作机制,然后讨论引用对象加入之后Java堆发生的变化。

内存管理器的作用就是识别程序中不再使用的对象,并且回收其内存。

一个Java应用程序由一系列线程组成,每个线程执行一系列方法,而每个方法通过参数或局部变量来引用对象。这些引用属于引用集合中的一部分,直接进入应用程序。另外,引用集合中还包括类库中定义的静态引用变量,以及通过Java本地接口(JNI)API获得的引用。引用集合中的所有引用对象都可以被当前应用程序获取,而不必被回收。同样地,这些对象可能包含对其它对象的引用,也可以被应用程序获取,依此类推。Java堆中的其它对象视为不可获取的,而所有这些不可获取的对象在内存管理中也是合法的。如果一个不可获取的对象使用finalize()方法,任务就交给了对象所调用的收尾器(finalizer)。在内存回收期间,不具有收尾器的不可获取对象和已经调用收尾器的对象被简单回收。

内存回收的算法是不断变化的,共性的方面是从引用集合中识别可获取的对象以及回收被其它对象占据的内存空间。

加入引用对象之后的引用与常规引用的区别在于,引用对象中的引用专门由内存管理器来处理。引用对象封装了其它一些对象的引用,我们称之为指示对象。在引用对象创建的同时,也就定义了该引用对象的指示对象。

根据应用程序要求,对象可以是强引用(strong references)、次引用(soft references)、弱引用(weak references)、虚引用(phantom references)的任意组合。为了确定对象的可获取程度,JVM内存管理器从引用集合出发遍寻堆中所有到对象的路径。当到达某对象的任意路径都不含有引用对象时,则称该对象具有强获取能力;当路径中含有一个或几个引用对象时,根据内存管理器所查询的引用对象的类型分别归为次获取、弱获取、虚获取。

另外,对象引用API中还定义了引用对象队列(java.lang.ref.ReferenceQueue),这是内存管理器对引用对象进行管理的一种简单数据结构。值得注意的是,在进行引用对象定义时,要求phantom reference对象必须产生于一个引用对象队列,而soft reference和weak reference对象则无此限制,如:

ReferenceQueue queue = new ReferenceQueue();
PhantomReference pr = new PhantomReference(object, queue);

Soft References 应用实例


下面以在基于web的应用程序中使用soft references为例,来说明Java对象引用与JVM的内存管理器进行交互的原理。

当用户打开某一web页时,applet代码获得图片并且得到显示。如果在代码中同时创建了该图片对象的soft references,那么当用户离开该web页时,内存管理器对图片所分配的内存是否回收做出选择。当用户返回该web页时,在applet代码中使用SoftReference.get方法就会得到图片才内存中是否仍存在的消息。如果在内存管理器中未创建该图片,在web页上会很快得到显示;否则,applet代码就会重新获取。

下面是Example.java的完整源代码。



import java.awt.Graphics;import java.awt.Image;import java.applet.Applet;import java.lang.ref.SoftReference;public class Example extends Applet { SoftReference sr = null; public void init() { System.out.println("Initializing"); } public void paint(Graphics g) { Image im = (sr == null) ? null : (Image)(sr.get()); if (im == null) { System.out.println("Fetching image"); im = getImage(getCodeBase(),"yundong.gif"); sr = new SoftReference(im); } System.out.println("Painting"); g.drawImage(im, 25, 25, this); g.drawString("运动之美",20,20); im = null; /* Clear the strong reference to the image */ } public void start() { System.out.println("Starting"); } public void stop() { System.out.println("Stopping"); }}


在上面的代码中,对象image是一个图片对象,传递给一个SoftReference对象sr。其中image对象是sr的指示对象,sr中的引用域是从次引用(soft reference)到 image。


Weak References分析


对于一个稳定的对象,比如说线程类对象,当需要获取外部数据时,在程序中应用weak references是非常理想的。如果利用引用队列创建了某一线程的weak reference,那么当线程不再具有强获取能力时,应用程序得到通知,根据此通知,应用程序才能执行相关数据对象的清除工作。

当内存管理器未发现strong references 和 soft references 时,我们称对象具有弱获取能力,即在到达该对象的路径中至少包含一个weak reference。程序中weak references被清除一段时间后,弱获取对象被收尾器收集。由此也可以看出,soft reference和weak reference之间的区别在于,应用soft reference时,内存管理器利用算法决定是否创建弱获取对象,而应用weak reference时,内存管理器必须创建次获取对象。


引用对象链


当到达某一对象的路径中含有多个引用对象时,就构成了引用对象链。内存管理器按照由强到弱的顺序处理引用对象,具体处理步骤包括:Soft references、 Weak references、Finalization、Phantom references和创建对象五个部分。

当内存管理器未发现前三种对象引用时,我们称对象具有虚获取能力,即在到达该对象的路径中至少包含一个phantom reference。虚引用对象直接被收尾器收集,而不被重新创建。当内存管理器发现只有phantom references时,对象就将处于等候phantom reference状态,应用程序向引用队列发出通知,然后对虚引用对象调用clear()方法,将其引用域设置为null,最后对不可获取对象执行收集清除处理任务。

通常,对象所具有的获取能力与引用对象集合直接路径中的最弱连接者相同。据此可以看出:

虚引用对象具有强获取能力,其它对象均具虚获取能力;

(b)中虚引用对象和弱引用对象均具强获取能力,故次引用对象和对象集合具有若获取能力;

(c)中虚引用对象、弱引用对象和次引用对象均具强获取能力,那么对象集合则具次获取能力。

● 在程序中使用引用对象API不但可以在一定程度上控制内存管理器,实现内存自动管理,还可以提高程序的稳定性和安全性。

● 引用对象链中各个对象的获取能力与整个链相关。

分享到:
评论

相关推荐

    谈谈你对垃圾回收机制的了解?.docx

    一, 垃圾回收机制 在系统运行过程中会产生一些无用的对象,这些对象一直占用内存,不清理这些无用的对象可能会导致内存...jvm会把虚拟机栈和本地方法栈中正在引用的对象、静态属性引用的对象和常量,作为 GC Roots。

    JAVA面试题最全集

    方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。这个方法是由垃圾收集器在确定这个对象没有被引用时对这个对象调用的。它是在 Object 类中定义的,因此所有的类都继承了它。子类覆盖 finalize() ...

    java面试宝典

    21、当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里到底是值传递还是引用传递? 10 22、我们在web 应用开发过程中经常遇到输出某种编码的字符,如iso8859-1等,...

    java面试题

    对象引用遍历 43 63.2. 几种垃圾回收机制 43 63.2.1. 标记-清除收集器 43 63.2.2. 标记-压缩收集器 43 63.2.3. 复制收集器 44 63.2.4. 增量收集器 44 63.2.5. 分代收集器 44 63.2.6. 并发收集器 44 63.2.7. 并行...

    Java面试宝典-经典

    70、TreeSet里面放对象,如果同时放入了父类和子类的实例对象,那比较时使用的是父类的compareTo方法,还是使用的子类的compareTo方法,还是抛异常! 48 71、说出一些常用的类,包,接口,请各举5个 49 72、java中有...

    Java面试宝典2010版

    70、TreeSet里面放对象,如果同时放入了父类和子类的实例对象,那比较时使用的是父类的compareTo方法,还是使用的子类的compareTo方法,还是抛异常! 48 71、说出一些常用的类,包,接口,请各举5个 49 72、java中有...

    java面试题大全(2012版)

    70、TreeSet里面放对象,如果同时放入了父类和子类的实例对象,那比较时使用的是父类的compareTo方法,还是使用的子类的compareTo方法,还是抛异常! 48 71、说出一些常用的类,包,接口,请各举5个 49 72、java中有...

    最新Java面试宝典pdf版

    70、TreeSet里面放对象,如果同时放入了父类和子类的实例对象,那比较时使用的是父类的compareTo方法,还是使用的子类的compareTo方法,还是抛异常! 48 71、说出一些常用的类,包,接口,请各举5个 49 72、java中有...

    Java面试笔试资料大全

    70、TreeSet里面放对象,如果同时放入了父类和子类的实例对象,那比较时使用的是父类的compareTo方法,还是使用的子类的compareTo方法,还是抛异常! 48 71、说出一些常用的类,包,接口,请各举5个 49 72、java中有...

    java面试宝典2012

    70、TreeSet里面放对象,如果同时放入了父类和子类的实例对象,那比较时使用的是父类的compareTo方法,还是使用的子类的compareTo方法,还是抛异常! 52 71、说出一些常用的类,包,接口,请各举5个 54 72、java中有...

    JAVA面试宝典2010

    70、TreeSet里面放对象,如果同时放入了父类和子类的实例对象,那比较时使用的是父类的compareTo方法,还是使用的子类的compareTo方法,还是抛异常! 48 71、说出一些常用的类,包,接口,请各举5个 49 72、java中有...

    Java面试宝典2012版

    70、TreeSet里面放对象,如果同时放入了父类和子类的实例对象,那比较时使用的是父类的compareTo方法,还是使用的子类的compareTo方法,还是抛异常! 48 71、说出一些常用的类,包,接口,请各举5个 49 72、java中...

    Java面试宝典2012新版

    70、TreeSet里面放对象,如果同时放入了父类和子类的实例对象,那比较时使用的是父类的compareTo方法,还是使用的子类的compareTo方法,还是抛异常! 48 71、说出一些常用的类,包,接口,请各举5个 49 72、java中有...

    千方百计笔试题大全

    21、当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里到底是值传递还是引用传递? 10 22、我们在web 应用开发过程中经常遇到输出某种编码的字符,如iso8859-1等,...

    net学习笔记及其他代码应用

    程序员不用担心内存管理,因为垃圾收集器会自动进行管理。要请求垃圾收集,可以调用下面的方法之一: System.gc() Runtime.getRuntime().gc() 37.String s = new String(\"xyz\");创建了几个String Object? 答:...

Global site tag (gtag.js) - Google Analytics