??xml version="1.0" encoding="utf-8" standalone="yes"?>
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
public class Testone {
public static void main(String args[]){
A a=new A();
//a.test();
//SoftReference sr = new SoftReference(a);
ReferenceQueue<A> rq = new ReferenceQueue<A>();
WeakReference<A> wr = new WeakReference<A>(a, rq);
a = null;
System.out.println(wr.get());
System.out.println(rq.poll());
System.gc();
System.runFinalization();
System.out.println(wr.get());
System.out.println(rq.poll());
if (wr != null) {
a = (A)wr.get();
System.out.println("asdasdas");
a.test();
}
else{
a = new A();
System.out.println("123123");
a.test();
a = null;
wr = new WeakReference<A>(a);
}
}
}
class A{
void test(){
System.out.println("A.test()");
}
}
]]>
垃圾攉概述
引用cȝ主要功能是能够引用仍可以被垃圾攉器回收的对象。在引入引用cM前,我们只能使用强引用(strong referenceQ。D例来_下面一行代码显C的是强引?nbsp;objQ?br />
Object obj = new Object();
obj q个引用引用堆中存储的一个对象。只?nbsp;obj 引用q存在,垃圾攉器就永远不会(x)释放用来容纳该对象的存储I间?br />
?nbsp;obj 出范围或被昑ּ地指定ؓ(f) null Ӟ垃圾攉器就认ؓ(f)没有对这个对象的其它引用Q也可以收集它了。然而?zhn)q需要注意一个重要的l节Q仅凭对象可以被攉q不意味着垃圾攉器的一ơ指定运行就能够回收它。由于各U垃圾收集算法有所不同Q某些算法会(x)更频J地分析生存期较短的对象Q而不是较老、生存期较长的对象。因此,一个可供收集的对象可能永远也不?x)被回收。如果程序在垃圾攉器释攑֯象之前结束,q种情况可能会(x)出现。因此,概括地说Q?zhn)永远无法保证可供攉的对象L?x)被垃圾攉器收集?br />
q些信息对于(zhn)分析引用类是很重要的。由于垃圾收集有着特定的性质Q所以引用类实际上可能没有?zhn)原来惛_的那么有用,管如此Q它们对于特定问题来说还是很有用的类。Y引用Qsoft referenceQ、弱引用Qweak referenceQ和虚引用(phantom referenceQ对象提供了三种不同的方式来在不妨碍攉的情况下引用堆对象。每U引用对象都有不同的行ؓ(f)Q而且它们与垃圾收集器之间的交互也有所不同。此外,q几个新的引用类都表现出比典型的强引?#8220;更弱”的引用Ş式。而且Q内存中的一个对象可以被多个引用Q可以是强引用、Y引用、弱引用或虚引用Q引用。在q一步往下讨Z前,让我们来看看一些术语:(x)
强可及对象(strongly reachableQ:(x)可以通过强引用访问的对象?br />
软可及对象(softly reachableQ:(x)不是强可及对象,q且能够通过软引用访问的对象?br />
弱可及对象(weakly reachableQ:(x)不是强可及对象也不是软可及对象,q且能够通过弱引用访问的对象?br />
虚可及对象(phantomly reachableQ:(x)不是强可及对象、Y可及对象Q也不是弱可及对象,已经l束的,可以通过虚引用访问的对象?br />
清除Q将引用对象?nbsp;referent 域设|ؓ(f) nullQƈ引用类在堆中引用的对象声明为可l束的?br />
SoftReference c?br />
SoftReference cȝ一个典型用途就是用于内存敏感的高速缓存。SoftReference 的原理是Q在保持对对象的引用时保证在 JVM 报告内存不情况之前清除所有的软引用。关键之处在于,垃圾攉器在q行时可能会(x)Q也可能不会(x)Q释放Y可及对象。对象是否被释放取决于垃圾收集器的算法以及垃圾收集器q行时可用的内存数量?nbsp;
WeakReference c?br />
WeakReference cȝ一个典型用途就是规范化映射Qcanonicalized mappingQ。另外,对于那些生存期相对较长而且重新创徏的开销也不高的对象来说Q弱引用也比较有用。关键之处在于,垃圾攉器运行时如果到了弱可及对象Q将释放 WeakReference 引用的对象。然而,h意,垃圾攉器可能要q行多次才能扑ֈq攑ּ可及对象?br />
PhantomReference c?br />
PhantomReference cd能用于跟t对被引用对象即进行的攉。同P它还能用于执?nbsp;pre-mortem 清除操作。PhantomReference 必须?nbsp;ReferenceQueue cM起用。需?nbsp;ReferenceQueue 是因为它能够充当通知机制。当垃圾攉器确定了某个对象是虚可及对象ӞPhantomReference 对象p攑֜它的 ReferenceQueue 上。将 PhantomReference 对象攑֜ ReferenceQueue 上也是一个通知Q表?nbsp;PhantomReference 对象引用的对象已l结束,可供攉了。这使?zhn)能够刚好在对象占用的内存被回收之前采取行动?nbsp;
垃圾攉器和引用交互
垃圾攉器每ơ运行时都可以随意地释放不再是强可及的对象占用的内存。如果垃圾收集器发现了Y可及对象Q就?x)出C列情况:(x)
SoftReference 对象?nbsp;referent 域被讄?nbsp;nullQ从而该对象不再引?nbsp;heap 对象?br />
SoftReference 引用q的 heap 对象被声明ؓ(f) finalizable?br />
?nbsp;heap 对象?nbsp;finalize() Ҏ(gu)被运行而且该对象占用的内存被释放,SoftReference 对象pd到它?nbsp;ReferenceQueueQ如果后者存在的话)?br />
如果垃圾攉器发C弱可及对象,׃(x)出现下列情况Q?br />
WeakReference 对象?nbsp;referent 域被讄?nbsp;nullQ从而该对象不再引?nbsp;heap 对象?br />
WeakReference 引用q的 heap 对象被声明ؓ(f) finalizable?br />
?nbsp;heap 对象?nbsp;finalize() Ҏ(gu)被运行而且该对象占用的内存被释放时QW(xu)eakReference 对象pd到它?nbsp;ReferenceQueueQ如果后者存在的话)?br />
如果垃圾攉器发C虚可及对象,׃(x)出现下列情况Q?br />
PhantomReference 引用q的 heap 对象被声明ؓ(f) finalizable?br />
与Y引用和弱引用有所不同QPhantomReference 在堆对象被释放之前就被添加到它的 ReferenceQueue。(误住,所有的 PhantomReference 对象都必ȝl过兌?nbsp;ReferenceQueue 来创建。)q(zhn)能够在堆对象被回收之前采取行动?br />
误虑清单 1 中的代码?br />
清单 1. 使用 WeakReference ?nbsp;ReferenceQueue 的示例代?br />
//Create a strong reference to an object
MyObject obj = new MyObject(); //1
//Create a reference queue
ReferenceQueue rq = new ReferenceQueue(); //2
//Create a weakReference to obj and associate our reference queue
WeakReference wr = new WeakReference(obj, rq); //3
?nbsp;//1 创徏 MyObject 对象Q而行 //2 则创?nbsp;ReferenceQueue 对象。行 //3 创徏引用其引用对?nbsp;MyObject ?nbsp;WeakReference 对象Q还创徏它的 ReferenceQueue。请注意Q每个对象引用(obj、rq ?nbsp;wrQ都是强引用。要利用q些引用c,(zhn)必d消对 MyObject 对象的强引用Q方法是?nbsp;obj 讄?nbsp;null。前面说q,如果不这样做Q对?nbsp;MyObject 永远都不?x)被回收Q引用类的Q何优炚w?x)被削弱?br />
每个引用c都有一?nbsp;get() Ҏ(gu)Q?nbsp;ReferenceQueue cL一?nbsp;poll() Ҏ(gu)。get() Ҏ(gu)q回对被引用对象的引用。在 PhantomReference 上调?nbsp;get() L?x)返?nbsp;null。这是因?nbsp;PhantomReference 只用于跟t收集。poll() Ҏ(gu)q回已被d到队列中的引用对象,如果队列中没有Q何对象,它就q回 null。因此,执行清单 1 之后再调?nbsp;get() ?nbsp;poll() 的结果可能是Q?br />
wr.get(); //returns reference to MyObject
rq.poll(); //returns null
现在我们假定垃圾攉器开始运行。由?nbsp;MyObject 对象没有被释放,所?nbsp;get() ?nbsp;poll() Ҏ(gu)返回同L(fng)|obj 仍然保持对该对象q行强引用。实际上Q对象布局q是没有改变Q和?nbsp;1 所C的差不多。然而,误虑下面的代码:(x)
obj = null;
System.gc(); //run the collector
现在Q调?nbsp;get() ?nbsp;poll() 生与前面不同的结果:(x)
wr.get(); //returns null
rq.poll(); //returns a reference to the WeakReference object
q种情况表明QMyObject 对象Q对它的引用原来是由 WeakReference 对象q行的)不再可用。这意味着垃圾攉器释放了 MyObject 占用的内存,从而 WeakReference 对象可以被放在它?nbsp;ReferenceQueue 上。这P(zhn)就可以知道?nbsp;WeakReference ?nbsp;SoftReference cȝ get() Ҏ(gu)q回 null Ӟ有一个对象被声明?nbsp;finalizableQ而且可能Q不q不一定)被收集。只有当 heap 对象完全l束而且其内存被回收后,W(xu)eakReference ?nbsp;SoftReference 才会(x)被放C其关联的 ReferenceQueue 上。清?nbsp;2 昄了一个完整的可运行程序,它展CZq些原理中的一部分。这D代码本w就颇具说明性,它含有很多注释和打印语句Q可以帮助?zhn)理解?br />
清单 2. 展示引用cd理的完整E序
import java.lang.ref.*;
class MyObject
{
protected void finalize() throws Throwable
{
System.out.println("In finalize method for this object: " +
this);
}
}
class ReferenceUsage
{
public static void main(String args[])
{
hold();
release();
}
public static void hold()
{
System.out.println("Example of incorrectly holding a strong " +
"reference");
//Create an object
MyObject obj = new MyObject();
System.out.println("object is " + obj);
//Create a reference queue
ReferenceQueue rq = new ReferenceQueue();
//Create a weakReference to obj and associate our reference queue
WeakReference wr = new WeakReference(obj, rq);
System.out.println("The weak reference is " + wr);
//Check to see if it´s on the ref queue yet
System.out.println("Polling the reference queue returns " +
rq.poll());
System.out.println("Getting the referent from the " +
"weak reference returns " + wr.get());
System.out.println("Calling GC");
System.gc();
System.out.println("Polling the reference queue returns " +
rq.poll());
System.out.println("Getting the referent from the " +
"weak reference returns " + wr.get());
}
public static void release()
{
System.out.println("");
System.out.println("Example of correctly releasing a strong " +
"reference");
//Create an object
MyObject obj = new MyObject();
System.out.println("object is " + obj);
//Create a reference queue
ReferenceQueue rq = new ReferenceQueue();
//Create a weakReference to obj and associate our reference queue
WeakReference wr = new WeakReference(obj, rq);
System.out.println("The weak reference is " + wr);
//Check to see if it´s on the ref queue yet
System.out.println("Polling the reference queue returns " +
rq.poll());
System.out.println("Getting the referent from the " +
"weak reference returns " + wr.get());
System.out.println("Set the obj reference to null and call GC");
obj = null;
System.gc();
System.out.println("Polling the reference queue returns " +
rq.poll());
System.out.println("Getting the referent from the " +
"weak reference returns " + wr.get());
}
}
用途和风格
q些c背后的原理是避免在应用程序执行期间将对象留在内存中。相反,(zhn)以软引用、弱引用或虚引用的方式引用对象,q样垃圾攉器就能够随意地释攑֯象。当(zhn)希望尽可能减小应用E序在其生命周期中用的堆内存大时Q这U用途就很有好处。?zhn)必须CQ要使用q些c,(zhn)就不能保留对对象的强引用。如果?zhn)q么做了Q那׃(x)费q些cL提供的Q何好处?br />
另外Q?zhn)必须使用正确的编E风g查收集器在用对象之前是否已l回收了它,如果已经回收了,(zhn)首先必重新创对象。这个过E可以用不同的编E风格来完成。选择错误的风g(x)D出问题。请考虑清单 3 中从 WeakReference 索被引用对象的代码风|(x)
清单 3. 索被引用对象的风?br />
obj = wr.get();
if (obj == null)
{
wr = new WeakReference(recreateIt()); //1
obj = wr.get(); //2
}
//code that works with obj
研究了这D代码之后,L(fng)看清?nbsp;4 中从 WeakReference 索被引用对象的另一U代码风|(x)
清单 4. 索被引用对象的另一U风?br />
obj = wr.get();
if (obj == null)
{
obj = recreateIt(); //1
wr = new WeakReference(obj); //2
}
//code that works with obj
h较这两种风格Q看看?zhn)能否定哪种风格一定可行,哪一U不一定可行。清?nbsp;3 中体现出的风g一定在所有情况下都可行,但清?nbsp;4 的风格就可以。清?nbsp;3 中的风格不够好的原因在于Qif 块的Ml束之后 obj 不一定是非空倹{请考虑一下,如果垃圾攉器在清单 3 的行 //1 之后但在?nbsp;//2 执行之前q行?x)怎样。recreateIt() Ҏ(gu)重新创对象Q但它会(x)?nbsp;WeakReference 引用Q而不是强引用。因此,如果攉器在?nbsp;//2 在重新创建的对象上施加一个强引用之前q行Q对象就?x)丢失,wr.get() 则返?nbsp;null?br />
清单 4 不会(x)出现q种问题Q因 //1 重新创徏了对象ƈ为其指定了一个强引用。因此,如果垃圾攉器在该行之后Q但在行 //2 之前Q运行,该对象就不会(x)被回收。然后,?nbsp;//2 创建对 obj ?nbsp;WeakReference。在使用q个 if 块之后的 obj 之后Q?zhn)应该?nbsp;obj 讄?nbsp;nullQ从而让垃圾攉器能够回收这个对象以充分利用弱引用。清?nbsp;5 昄了一个完整的E序Q它?yu)展C刚才我们描q的风格之间的差异。(要运行该E序Q其q行目录中必L一?#8220;temp.fil”文g?br />
清单 5. 展示正确的和不正的~程风格的完整程序?br />
import java.io.*;
import java.lang.ref.*;
class ReferenceIdiom
{
public static void main(String args[]) throws FileNotFoundException
{
broken();
correct();
}
public static FileReader recreateIt() throws FileNotFoundException
{
return new FileReader("temp.fil");
}
public static void broken() throws FileNotFoundException
{
System.out.println("Executing method broken");
FileReader obj = recreateIt();
WeakReference wr = new WeakReference(obj);
System.out.println("wr refers to object " + wr.get());
System.out.println("Now, clear the reference and run GC");
//Clear the strong reference, then run GC to collect obj.
obj = null;
System.gc();
System.out.println("wr refers to object " + wr.get());
//Now see if obj was collected and recreate it if it was.
obj = (FileReader)wr.get();
if (obj == null)
{
System.out.println("Now, recreate the object and wrap it
in a WeakReference");
wr = new WeakReference(recreateIt());
System.gc(); //FileReader object is NOT pinned...there is no
//strong reference to it. Therefore, the next
//line can return null.
obj = (FileReader)wr.get();
}
System.out.println("wr refers to object " + wr.get());
}
public static void correct() throws FileNotFoundException
{
System.out.println("");
System.out.println("Executing method correct");
FileReader obj = recreateIt();
WeakReference wr = new WeakReference(obj);
System.out.println("wr refers to object " + wr.get());
System.out.println("Now, clear the reference and run GC");
//Clear the strong reference, then run GC to collect obj
obj = null;
System.gc();
System.out.println("wr refers to object " + wr.get());
//Now see if obj was collected and recreate it if it was.
obj = (FileReader)wr.get();
if (obj == null)
{
System.out.println("Now, recreate the object and wrap it
in a WeakReference");
obj = recreateIt();
System.gc(); //FileReader is pinned, this will not affect
//anything.
wr = new WeakReference(obj);
}
System.out.println("wr refers to object " + wr.get());
}
}
ȝ
如果使用得当Q引用类q是很有用的。然而,׃它们所依赖的垃圾收集器行ؓ(f)有时候无法预知,所以其实用性就?x)受到媄响。能否有效地使用它们q取决于是否应用了正的~程风格Q关键在于?zhn)要理解这些类是如何实现的以及如何对它们进行编E?br />
=================================================================================
Java 对象的状态有:
* 已创?created)
* 强可?strong reachable)
* 不可?invisible)
* 不可?unreachable)
* 已收?collected)
* l化(finalized)
* 已回?deallocated)
Java对象生命周期的状态{? {image:img=objectstatus.jpg|width=400} 引用对象
三种新的引用cd:
* 软引?soft reference)
* 弱引?weak reference)
* qd?phantom reference)
强可?Strong Reachable)
定义: ~An object is strong reachable if it can be reached by some thread without traversing any reference objects. A newly-created object is strong reachable by the thread that created it.~
处于强可辄态的对象, 在Q何情况下都不?x)被回收? 软可?Softly Reachable)
定义:~An object is softly reachable if it is not strongly reachable but can be reached by traversing a soft reference.~
含义?当对象不处于强可辄? q且可以通过软引用进行访问时, 卛_于Y可达状?
当程序申请内存的时? 垃圾攉器会(x)判断是否开始回收处于Y可达状态的对象, 如果军_回收某个对象, 那么垃圾攉器会(x)清除所有指向该对象的Y引用, 如果M处于其它软可辄态的对象可以通过强引用访问该对象, 那么指向q些对象的Y引用也会(x)被清除掉. 垃圾攉器在军_哪些软可辄态的对象被收集时, 采用"最久未被?原则, 或称"最不常使用"原则. 垃圾攉器也保证在OutOfMemeryError产生以前, 所有的软引用都被清?
* 产生和用一个Y引用
// createSoftReference sr = new SoftReference(new SomeObject());// getSomeObject o = (SomeObject) sf.get();// create in a reference queue;ReferenceQueue queue = new ReferenceQueue();SoftReference sr = new SoftReference(new SomeObject(), queue);
弱可?Weakly Reachable)
定义:~An Object is weakly reachable if it is neither strongly nor softly reachable but can be reached by traversing a weak reference.~
垃圾攉器会(x)一ơ清除所有弱引用. qd?Phantomly Reachable)
定义:~An object is phantomly reachable if it is neither strongly, softly, nor weakly reachable, it has been finalized, and some phantom reference refers to it.~
qd用不能直接创? 必须通过向引用队列等U的途径来创?
ReferenceQueue queue = new ReferenceQueue();PhantomReference pr = new PhantomReference (new SomeObject(), queue);
你不可能从引用再次得到对象, pr.get()永远q回null. 另外, 必须调用Reference.clear()手工清除qd? All About ReferenceObjects No InterWiki reference defined in properties for Wiki called '[http'!)]
Reference Objects No InterWiki reference defined in properties for Wiki called '[http'!)]
Reference Objects and Garbage Collection No InterWiki reference defined in properties for Wiki called '[http'!)]
\[Jike Thread\?Soft, Weak, and Phantom References|http://www-124.ibm.com/pipermail/jikesrvm-core/2003-May/000365.html]
Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1492810
]]>
Java虚拟?JVM)的类装蝲是指将包含在类文g中的字节码装载到JVM? q其成为JVM一部分的过E。JVM的类动态装载技术能够在q行时刻动态地加蝲或者替换系l的某些功能模块, 而不影响pȝ其他功能模块的正常运行。本文将分析JVM中的c装载系l,探讨JVM中类装蝲的原理、实C及应用?
二、Java虚拟机的c装载实C应用
2.1 装蝲q程?
所谓装载就是寻找一个类或是一个接口的二进制Ş式ƈ用该二进制Ş式来构造代表这个类或是q个接口的class对象的过E,其中cL接口的名U是l定了的。当然名UC可以通过计算得到Q但是更常见的是通过搜烦源代码经q编译器~译后所得到的二q制形式来构造?
在Java中,c装载器把一个类装入Java虚拟ZQ要l过三个步骤来完成:(x)装蝲、链接和初始化,其中链接又可以分成校验、准备和解析三步Q除了解析外Q其它步骤是严格按照序完成的,各个步骤的主要工作如下:(x)
装蝲Q查扑֒导入cL接口的二q制数据Q?
链接Q执行下面的校验、准备和解析步骤Q其中解析步骤是可以选择的;
校验Q检查导入类或接口的二进制数据的正确性;
准备Q给cȝ静态变量分配ƈ初始化存储空_
解析Q将W号引用转成直接引用Q?
初始化:(x)Ȁzȝ的静态变量的初始化Java代码和静态Java代码块?
至于在类装蝲和虚拟机启动的过E中的具体细节和可能?x)抛出的错误Q请参看《Java虚拟范》以及《深入Java虚拟机》,它们在网l上面的资源地址是:(x)
http://java.sun.com/docs/books/vmspec/2nd-edition/html/Preface.doc.html
http://www.artima.com/insidejvm/ed2/index.html
׃本文的讨论重点不在此׃再多叙述?
2.2 装蝲的实?
JVM中类的装载是由ClassLoader和它的子cL实现?Java ClassLoader 是一个重要的Javaq行时系l组件。它负责在运行时查找和装入类文g的类?
在Java中,ClassLoader是一个抽象类Q它在包java.lang?可以q样_只要了解了在ClassLoader中的一些重要的Ҏ(gu)Q再l合上面所介绍的JVM中类装蝲的具体的q程Q对动态装载类q项技术就有了一个比较大概的掌握Q这些重要的Ҏ(gu)包括以下几个:
①loadCassҎ(gu) loadClass(String name ,boolean resolve)其中name参数指定了JVM需要的cȝ名称,该名UC包表C法表示,如Java.lang.ObjectQresolve参数告诉Ҏ(gu)是否需要解析类Q在初始化类之前,应考虑c解析,q不是所有的c都需要解析,如果JVM只需要知道该cL否存在或扑և该类的超c?那么׃需要解析。这个方法是ClassLoader 的入口点?
②defineClassҎ(gu) q个Ҏ(gu)接受cL件的字节数组q把它{换成Class对象。字节数l可以是从本地文件系l或|络装入的数据。它把字节码分析成运行时数据l构、校验有效性等{?
③findSystemClassҎ(gu) findSystemClassҎ(gu)从本地文件系l装入文件。它在本地文件系l中LcL?如果存在,׃用defineClass字节数l{换成Class对象,以将该文件{换成cR当q行Java应用E序?q是JVM 正常装入cȝ~省机制?
④resolveClassҎ(gu) resolveClass(Class c)Ҏ(gu)解析装入的类,如果该类已经被解析过那么不做处理。当调用loadClassҎ(gu)?通过它的resolve 参数军_是否要进行解析?
⑤findLoadedClassҎ(gu) 当调用loadClassҎ(gu)装入cL,调用findLoadedClass Ҏ(gu)来查看ClassLoader是否已装入这个类,如果已装?那么q回Class对象,否则q回NULL。如果强行装载已存在的类,会(x)抛出链接错误?
2.3 装蝲的应?
一般来_我们使用虚拟机的c装载时需要承抽象类java.lang.ClassLoader,其中必须实现的方法是loadClass()Q对于这个方法需要实现如下操?(1) 认cȝ名称;(2) 查请求要装蝲的类是否已经被装?(3) 查请求加载的cL否是pȝc?(4) 试从类装蝲器的存储取所h的类;(5) 在虚拟机中定义所h的类;(6) 解析所h的类;(7) q回所h的类?
所有的Java 虚拟机都包括一个内|的c装载器Q这个内|的cd装蝲器被UCؓ(f)根装载器(bootstrap ClassLoader)。根装蝲器的Ҏ(gu)之处是它只能够装载在设计时刻已知的类,因此虚拟机假定由根装载器所装蝲的类都是安全的、可信Q?可以不经q安全认证而直接运行。当应用E序需要加载ƈ不是设计时就知道的类?必须使用用户自定义的装蝲?user-defined ClassLoader)。下面我们D例说明它的应用?
public abstract class MultiClassLoader extends ClassLoader{
...
public synchronized Class loadClass(String s, boolean flag)
throws ClassNotFoundException
{
/* 查类s是否已经在本地内?/
Class class1 = (Class)classes.get(s);
/* cs已经在本地内?/
if(class1 != null) return class1;
try/*用默认的ClassLoader 装入c?/ {
class1 = super.findSystemClass(s);
return class1;
}
catch(ClassNotFoundException _ex) {
System.out.println(">> Not a system class.");
}
/* 取得cs的字节数l?/
byte abyte0[] = loadClassBytes(s);
if(abyte0 == null) throw new ClassNotFoundException();
/* 类字节数组转换为类*/
class1 = defineClass(null, abyte0, 0, abyte0.length);
if(class1 == null) throw new ClassFormatError();
if(flag) resolveClass(class1); /*解析c?/
/* 新加蝲的类攑օ本地内存*/
classes.put(s, class1);
System.out.println(">> Returning newly loaded class.");
/* q回已装载、解析的c?/
return class1;
}
...
}
三、Java虚拟机的c装载原?
前面我们已经知道Q一个Java应用E序使用两种cd的类装蝲器:(x)根装载器(bootstrap)和用户定义的装蝲?user-defined)。根装蝲器是Java虚拟机实现的一部分QD个例子来_如果一个Java虚拟机是在现在已l存在ƈ且正在被使用的操作系l的剙用CE序来实现的Q那么根装蝲器将是那些CE序的一部分。根装蝲器以某种默认的方式将c装入,包括那些Java API的类。在q行期间一个JavaE序能安装用戯己定义的c装载器。根装蝲器是虚拟机固有的一部分Q而用户定义的c装载器则不是,它是用Java语言写的Q被~译成class文g之后然后再被装入到虚拟机Qƈ像其它的M对象一样可以被实例化?Javac装载器的体pȝ构如下所C:(x)
Java的类装蝲模型是一U代?delegation)模型。当JVM 要求c装载器CL(ClassLoader)装蝲一个类?CL首先这个类装蝲h转发l他的父装蝲器。只有当父装载器没有装蝲q无法装载这个类?CL才获得装载这个类的机?x)。这? 所有类装蝲器的代理关系构成了一U树(wi)状的关系。树(wi)的根是类的根装蝲?bootstrap ClassLoader) , 在JVM 中它?null"表示。除根装载器以外的类装蝲器有且仅有一个父装蝲器。在创徏一个装载器? 如果没有昑ּ地给出父装蝲? 那么JVM默认系l装载器为其父装载器。Java的基本类装蝲器代理结构如?所C:(x)
下面针对各种c装载器分别q行详细的说明?
?Bootstrap) 装蝲?该装载器没有父装载器Q它是JVM实现的一部分Q从sun.boot.class.path装蝲q行时库的核心代码?
扩展(Extension) 装蝲?l承的父装蝲器ؓ(f)根装载器Q不像根装蝲器可能与q行时的操作pȝ有关Q这个类装蝲器是用纯Java代码实现的,它从java.ext.dirs (扩展目录)中装载代码?
pȝ(System or Application) 装蝲?装蝲器ؓ(f)扩展装蝲器,我们都知道在安装JDK的时候要讄环境变量(CLASSPATH )Q这个类装蝲器就是从java.class.path(CLASSPATH 环境变量)中装载代码的Q它也是用纯Java代码实现的,同时q是用户自定义类装蝲器的~省父装载器?
应用程?Applet) 装蝲? 装蝲器ؓ(f)pȝ装蝲器,它从用户指定的网l上的特定目录装载小应用E序代码?
在设计一个类装蝲器的时候,应该满以下两个条gQ?
对于相同的类名,c装载器所q回的对象应该是同一个类对象
如果c装载器CL1装载类C的请求{l类装蝲器CL2Q那么对于以下的cL接口,CL1和CL2应该q回同一个类对象:a)S为C的直接超c?b)S为C的直接超接口;c)S为C的成员变量的cd;d)S为C的成员方法或构徏器的参数cd;e)S为C的成员方法的q回cd?
每个已经装蝲到JVM中的c都隐式含有装蝲它的c装载器的信息。类Ҏ(gu)getClassLoader 可以得到装蝲q个cȝc装载器。一个类装蝲器认识的cd括它的父装蝲器认识的cd它自p载的c,可见c装载器认识的类是它自己装蝲的类的超集。注意我们可以得到类装蝲器的有关的信息,但是已经装蝲到JVM中的cL不能更改它的c装载器的?
Java中的cȝ装蝲q程也就是代理装载的q程。比?Web览器中的JVM需要装载一个小应用E序TestApplet。JVM调用应用程序装载器ACL(Applet ClassLoader)来完成装载。ACL首先h它的父装载器, 即系l装载器装蝲TestApplet是否装蝲了这个类, ׃TestApplet不在pȝ装蝲器的装蝲路径? 所以系l装载器没有扑ֈq个c? 也就没有装蝲成功。接着ACL自己装蝲TestApplet。ACL通过|络成功地找CTestApplet.class 文gq将它导入到了JVM中。在装蝲q程? JVM发现TestAppet是从类java.applet.Appletl承的。所以JVM再次调用ACL来装载j(lu)ava.applet.AppletcRACL又再ơ按上面的顺序装载Appletc? l果ACL发现他的父装载器已经装蝲了这个类, 所以ACLq接将q个已经装蝲的类q回l了JVM , 完成了Appletcȝ装蝲。接下来,Appletcȝ类也一样处理。最? TestApplet及所有有关的c都装蝲CJVM中?
四、结?
cȝ动态装载机制是JVM的一Ҏ(gu)心技? 也是Ҏ(gu)被忽视而引起很多误解的地方。本文介l了JVM中类装蝲的原理、实C及应用,其分析了ClassLoader的结构、用途以及如何利用自定义的ClassLoader装蝲q执行Javac,希望能读者对JVM中的c装载有一个比较深入的理解
<filter>
<filter-name>Set Character Encoding</filter-name>
<filter-class>filters.SetCharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>GBK</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>Set Character Encoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<Connector
port="80" maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
enableLookups="false" redirectPort="8443" acceptCount="100"
debug="0" connectionTimeout="20000"
disableUploadTimeout="true"
URIEncoding="GBK"
/>
<%@ page contentType="text/html;charset=gb2312"%>
<%@ page import="java.util.*"%>
<%
String q=request.getParameter("q");
q = q == null? "没有? : q;
%>
<HTML>
<HEAD><TITLE>新闻列表昄</TITLE>
<META http-equiv=Content-Type content="text/html; charset=gb2312">
<META http-equiv=pragma content=no-cache>
<body>
你提交了Q?br /> <%=q%>
<br>
<form action="tcnchar.jsp" method="post">
输入中文:<input type="text" name="q"><input type="submit" value="定">
<br>
<a href="tcnchar.jsp?q=中国">通过get方式提交</a>
</form>
</BODY></HTML>
/**
* MD5加密c?br />
* @author zhang
*/
public class MD5Encoding
{
/**
*
*
*/
private MD5Encoding()
{
}
/**
* 加密法MD5
*
* @param text 明文
* @return String 密文
*/
public final static String encoding(String text)
{
char hexDigits[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
String encodingStr = null;
try
{
byte[] strTemp = text.getBytes();
MessageDigest mdTemp = MessageDigest.getInstance("MD5");
mdTemp.update(strTemp);
byte[] md = mdTemp.digest();
int j = md.length;
char str[] = new char[j * 2];
int k = 0;
for (int i = 0; i < j; i++)
{
byte byte0 = md[i];
str[k++] = hexDigits[byte0 >>> 4 & 0xf];
str[k++] = hexDigits[byte0 & 0xf];
}
encodingStr = new String(str);
}
catch (Exception e)
{
}
return encodingStr;
}
public static void main(String[] areg)
{
MD5Encoding md5 = new MD5Encoding();
md5.encoding("admin");
}
}
如果是Win与Linuxpȝ启动Tomcat服务?需要在tomcat/bin/catalina.sh?tomcat/bin/catalina.bat两个文g:在两个文仉面加?
SET CATALINA_OPTS= -Xms64m -Xmx128m
2, Tomcatq接数配|?/font>
配置Tomcatq接?需要在Server.xml文g里面加如:
maxThreads="150" 表示最多同时处?50个连?
minSpareThreads="25" 表示即没有Z用也开q么多空U程{待
maxSpareThreads="75" 表示如果最多可以空75个线E,例如某时L80问,之后没有问了Q则tomcat不会(x)保留80个空U程Q而是关闭5个空的?
acceptCount="100" 当同时连接的人数辑ֈmaxThreadsӞq可以接收排队的q接Q超q这个连接的则直接返回拒l连接?/font>
connectionTimeout="20000" maxThreads="150"
minSpareThreads="25"
maxSpareThreads="75"
enableLookups="false"
acceptCount="100" debug="0"
disableUploadTimeout="true"
redirectPort="8443" URIEncoding="UTF-8"/>
URIEncoding="UTF-8" 是设定JSP~码格式.
q里聊下传统意义上的DAO模式Q在阎宏的JAVA与模式书中有详细的介l)Q需要注意的几个斚wQ?/span>
1、不要DAO中出C务逻辑
DAO只需x持久化部分,可以通过Facade来控制事务的边界Q从而提高DAO的复用性,在不同的事务{略中应?/span>
2、不要过多的?DAO层捕捉异?/span>
在很多的开发中Q会(x)喜欢使用Checked ExceptionQ抛到servcie层、再到action层,其实在DAO中发生的异常常常是不可恢复的Q比如DB的连接问题)Q所以应该选择 RuntimeExceptionQ我们所需要的只是log的记录ƈ通知理员,q过全局的异常处理画面告之?/span>
暂时q些Q由于ORM的懒加蝲技术,在DAO中可能会(x)有些调整Qؓ(f)了增加DAO的复用性,q方面的技术也应该剥离出来Q不q暂时还没这么处理过Q所以无法ȝQ希望有q方面经验的人提供一?/span>
------~写可测试性代?/span>
1. 接口依赖
q是最重要的一点,因ؓ(f)q可以得我们很Ҏ(gu)的针对一个接口实?/span>Mock对象Q模?/span>/替换实际对象?x)变的很?gu)。达C一个被对象处于一个孤立环境的试要求?/span>
q里Q?/span>ClassA依赖?/span>ClassB的具体实玎ͼTestCaseҎ(gu)无法独立?/span>ClassB?/span>ClassAq行独立试?/span>
因此Q我们将ClassA改ؓ(f)依赖于接?/span>B_Inf。这P可以很容易的实现一?/span>Mock_B替换ClassB?/span>ClassAq行孤立试?/span>
2. 依赖注入
一个方法对外部cȝ依赖Q应该是可注入的。即可以通过构造方法?/span>get/setҎ(gu)的方式在外部依赖关pL入。事实上Q这也ؓ(f)我们在测试用例中替换待测cȝ依赖对象提供了机?x)。不应该出现在方法内部新建对象用的情况?/span>
3. 降低耦合?/span>
待测cd与最的c耦合Q即最交互原则。特别是要减与那些M具体环境?yu)׃能运行的cȝ耦合。可以通过门面模式{对外部调用q行隔离?/span>
5Q?/span>AOP
面向切面~程。给我们提供的启C是Q将真正需要测的逻辑分离出来。摆脱那些无意义且简单重复的代码Ҏ(gu)试的q扰?/span>
6. 明确的契U?/span>
Ҏ(gu)一定要有明清晰的输入/输出。徏议在Ҏ(gu)的注释描qCQ分三段“描述”“前置条g”“后置条g”?/span>
1. 可恶的静态方?/span>
在我们的代码中有大量的调用静态方法的地方。用h我们很爽Q但q对试来说却是N。因为我们除了通过改变代码创徏stub来改变这些方法的行ؓ(f)外,我们没有M途径。更要命的是Q写q些stub的代价非常的巨大Q常o人望而却步?/span>
解决Ҏ(gu)Q?/span>
方法内部的q些调用提取?/span>protectedҎ(gu)。在外部创徏待测cȝ子类Q重写该protectedҎ(gu)?/span>
最?jng)_践:(x)
q些静态方法由单态类提供Q单态类由工厂方法获取,具体cM用这些单态类的接口?/span>
我们在方法中通过接口使用对外部模块的调用。一斚wQ隔M外部模块改变Ҏ(gu)们生的冲击Q另一斚wQ也使我们?/span>Mock替换实际的外部组Ӟ创徏孤立试环境成ؓ(f)可能?/span>
2. 待测cL法中Q?/span>new出另一个对象ƈ使用其方?/span>
两种Ҏ(gu)Q?/span>1Q将new 出对象的q程装?/span>protectedҎ(gu)。同重构1?/span>2)该对象提取成类属性,即由使用关系变成兌关系?/span>
3. 分离不可?/span>/不必代?/span>
在不影响的情况下Q将不可部分分d一些不需要测的简单方法中厅R或者将可测的部分提取到一个私有方法中厅R然后针对这个私有方法进行测试?/span>
通常q种做法使用范围有限Q但有些时候还是值的一试?/span>
4. 单一职责
职责太多Q肯定不好测。针对于q一?#8220;不好的Ҏ(gu)必然不好?#8221;。当Ҏ(gu)q大Q承担责任过多时Q拆分是应该的?/span>
5. 为类提供一个空构造方法?/span>
我们的各个测试方法都依赖于对象处于一个特定的状态,满一定的前置条g。而要对象置为我们希望的状态,我们必须首先拥有一个对象。然而,很多时候,在一个隔ȝ单元试环境下,构造函数由于各U原因不能正常初始化?/span>
此时Q可以ؓ(f)cL供一个的I的构造方法。在外部构造一?/span>“?/span>”对象Q而后Ҏ(gu)前置条g各个属性设|成需要的Mock对象?/span>
-----------------------------------------------------------------------------------------------------------------------------------------------------------
事实上,要想写出h可测性的代码Q最佳的办法是试驱动开发。先写测试代码把功能体现出来Q再写功能代码让试通过。这样写出的代码显而易见会(x)更具有测试性?/span>
原地址:http://www.aygfsteel.com/wukaichun600/archive/2008/07/18/214125.html
q是最为古老的一U测试技能。通过cdơ上的替换实C对待环境的模拟?/span>
实现的时候有两种途径Q?/span>
1、重写实际类Q在试Ӟ先于实际cd载,卌盖。如Q我们在unittest/stub文g夹下针对于每一个重写类都有相同的包l构和类名:(x)
在类路径中优先加载:(x)
2、在实际代码中添加判断。比如,如果当前是测试环?/span>if(isUT)执行XX操作Q截断真正需要做的事?/span>
publicvoid sendCommand(int cmdCode)
{
if(isUT())
{
//...
}
else
{
//...
}
}
Stub技术的问题在于我们在重写q些cȝ时候,不仅仅要x接口Q还要关注其内部逻辑。如果你只是单的q回一个固定的响应Q会(x)很简单。但是对于每一ơ运行需要根据不同的输入q回不同的输出时Ҏ(gu)内部的处理就?x)复杂的多?/span>
׃实现的难度,所以,使用时就要注意:(x)有高价倹{重用度高、数量少。这是_重写一个类Q就可以有一大批cd以用?/span>
Mock是目前单元测试中最常用的。用来在对象层次上实现细cd替换十分方便?/span>
当我们在试中,需要其它类/接口的一个方法时Q我们可?span style="color: blue;">通过l承
/实现其一个子cd象来替换实际对象。在Mock子类中将需要的Ҏ(gu)直接q回需要的l果p了?/span>privateclass Mock_QueryCtrl extends QueryCtrl
{
public List queryNEList()
{
List neList = new ArrayList();
//直接填充q返回你需要的数据...
return neList;
}
}
同样Q我们也可以通过试待测cȝ子类来测试待类。这对于被测Ҏ(gu)使用了自w类的方法时很适用?/span>
单元试的一个关键就是替换。类层次上的替换Q通过在类路径中提前加载就可以实现。而在对象层次上,java的反机制提供了很好的帮助?/span>
1Q?/span>.获取/注入U有属?/span>
2Q?/span>.执行U有Ҏ(gu)
附:(x)注入U有属性的实现Q?/span>
publicvoid setFieldObject(Object instance, String fieldName, Object value)
throws IllegalArgumentException, IllegalAccessException,
NoSuchFieldException {
Field field = null;
Class c = instance.getClass();
do {
try
{
field = c.getDeclaredField(fieldName);
} catch (SecurityException e)
{
e.printStackTrace();
} catch (NoSuchFieldException e)
{
c = c.getSuperclass();
}
}
while (c.getName() != "java.lang.Object" && field == null);
if (field != null)
{
field.setAccessible(true);
field.set(instance, value);
}
else
{
thrownew NoSuchFieldException(fieldName);
}
}
注:(x)q是一个简单实玎ͼ实际中需要优化?/span>
下例演示了如何测试类NEListTable?/span>ShowNETable()Ҏ(gu)。其中注意的是,Ҏ(gu)中调用了c?/span>QueryCtrl?/span>queryNEList()Ҏ(gu)?/span>
待测c:(x)
publicclass NEListTable
{
QueryCtrl ctrl = null;
publicvoid ShowNETable()
{
List neList = ctrl.queryNEList();
for(int i = 0;i<neList.size();i++)
{
//?/span>neList转换D
}
//昄表格...
}
}
publicclass QueryCtrl {
public List queryNEList()
{
returnnull;
}
}
试c:(x)
public class TestNEListTable extends TestCase
{
private NEListTable table = null;
private TestHelper helper = null;
public void testShowNETable()
{
Mock_QueryCtrl ctrl = new Mock_QueryCtrl();
helper.setObjectField(table,"ctrl",ctrl);//?/span>Mock对象注入table
table.ShowNETable();
assertTrue(table.getRowCount()>0);
}
private class Mock_QueryCtrl extends QueryCtrl
{
public List queryNEList()
{
List neList = new ArrayList();
//q回你需要的数据...
return neList;
}
}
}
原地址:http://www.aygfsteel.com/wukaichun600/archive/2008/07/10/213790.html
UT的是方法,验的是一个类对外界的承诺。因此,大多数情况下Q我们测的应该是公共Ҏ(gu)Q除非不得已才对U有Ҏ(gu)q行试?/span>
Ҏ(gu)是程序设计的最单位?/span>UT的局限也体现在这里,它ƈ没有针对cM间的交互做检验。所以,不能指望单元试做完了,没有问题了。在q个斚w的欠~,我们可以通过自动化的功能/lg试来完成。这也是开发者测试的一部分?/span>
1. 早的发现问?/span>
说白了,是不要让问题流?/span>厅R让我们的缺L(fng)降低Q把我们的品做的漂?/span>?/span>另一斚wQ一些细cd的问题在q里也确实更Ҏ(gu)发现Q同时也行更大粒度的试做好集成准备?/span>
2. ~织一层保护网
l新的代码徏立有效的保护?/span> 保证对代码每一份改动,都不?x)对现有pȝ造成伤害。避免了引入问题?/span>
3.写出优雅的代?/span>
~写单元试的过E,实质是用我们自׃码的q程。我们成了第一个真正意义上的体验者。在q个q程中,我们Z使代码易用会(x)q行不断的重构。最l的交付代码必然?x)更优雅?/span>
4. 建立E序员的自信
我们?fn)惯于两g抚wQ不三七二十一把代码写完了?/span>Code阶段l束Q然后不断的调试Q修修补补跑hp?/span>TR4。没人敢_他代码一写完了,p跑v来。这U做法是很没人性的Q系l搞挂几ơ后心里发虚,一点底气都没有?/span>但是Q?/span>q是我们的职业,我们为着自己的荣耀而战。在M时候,都需要信心满盈?/span>
1.一个类、一个方法、一条\?/span>
我们一ơ只一个类? 一个方法。刚开始做单元试的时候,很多Z(x)自然而然的做成了功能试。因为,前一部分执行的结果恰好ؓ(f)后一部分准备好了输入。而另一斚wQ连l的执行q? E组成了一个明的场景Q让具体的功能变得完整可见,q正是我们期待的Q让我们变得有信心。那Z么不其自然呢?
原因在于Q?/span>
1Q、单元测试要保证一定的微粒度。从单元试到功能测试,q之间的_度?x)越来越大,往后,我们?x)越的xl节。如果直接蟩跃到功能试上,?x)让我们遗漏掉一些问题,在以后的_粒度测试中Q它们会(x)转变为很N现或者不可重现的致命问题?/span>
2Q、上q场景之所? ?x)出玎ͼ是因为先写代码后写测试导致的。相当于代码已经集成Q具备了做功能测试的一定条件。这个时候让再走回头路做单元试Q当然不如直接就做功能测试来 的顺当。所以,应该一个测试、一个方法,一个方法一个测试,q样不断的一步一步的循环q代集成来的好?/span>
另一斚wQؓ(f)了将一个方法的多个不同的执行\径分开Q我?span style="color: blue;">必须保证一ơ只一个方法的一个\径?/span>q样Q前|条件和后置条g׃(x)很明,Ҏ(gu)准备试环境?/span>
2.重构以便于测?/span>
面对着一个方法,你感C{莫展。ƈ不是你的错,而是因ؓ(f)q个Ҏ(gu)很烂。测一个方法就是在使用q个Ҏ(gu)Q你自己都这h奈,来真正使用的h岂不是要骂娘Q?/span>雁过留声Qhq留?/span>。这个时候,重构一下很值得?/span>
3.保证试Ҏ(gu)z?/span>
如果q测试方法都很复杂,N我们q要再写试用例来保证它的正执行不成?q样岂不是麻烦大了!所以,试Ҏ(gu)一定要写的可能的单,写到你认为白痴都能看懂的E度?/span>
1.代码要首先可,然后才能?/span>
首先要遵?/span>契约式设计。类的每一个方法都应该对外承担了一份契U,有明的前置条g和后|条件?/span> 当你对这个方法进行测试之前必L楚明白这两个条g。一个有效的Ҏ(gu)一定是做了什么事的。一定会(x)产生一定的影响Q我们可以通过对外围环境的改变来检方法生的作用是否如预?/span>(例如,获取某一对象的属性进行检?/span>)?/span>
其次是,?/span>Ce和单一责Q原则。一个方法对外的依赖应该单一Q不应该取决于很多的外部环境。因Z同的外部环境多Q组合项p多,要测的先x件就多。而一个方法对外部环境的媄响太多,则意味着职责不单一Q对于输难测?/span>
曄听有刎ͼq些道理Q你懂了懂Q不懂就不懂Q说了没用。但我认为,如果你还以ؓ(f)q些只是大道理,如果你还惛_它有点切w的感受Q做单元试是一个很好的途径?/span>
2.信Q你该信Q的?/span>
对于已经E_的部分,cM于第三方包,q_部分Q其x遗留pȝ中已l证明是可靠的部分,都可以信仅R这些是我们用例代码依赖的部分,是我们用来检验其它待部分的基石。如果什么都要测Q就?x)变成什么都不了?/span>
3. 单元试要尽量少的增加开发h员的负担?/span>
一斚wQ我们实在被问题单压抑的太久了。所以,从全局上来看待q个问题Q如果可以确实的减少后期的维护压力,Ҏ(gu)们自w而言当然是有益的。所谓增加的负担Q不q是提早了结了一些痛苦?/span>
另一斚wQ?/span>单元试必须自动?/span> Q必?/span>单,?c)化?/span>q是我们要努力的目标?/span>
4.调试的旉用来写单元测?/span>
没有做过自动化单元测试的人永q也不能体会(x)其给E序员带来的自信和好处。如果你q在调试Q不如顺手加个测试。以后,保证同样的问题不?x)从你的眼皮下溜走?/span>
5. 现在的单元测试难在哪里?
难以打桩。因为我们对其它模块的关联是q样的?/span>
q就是麻烦的所在,兌太多。如果我们要,我们p打桩。但是,
1. 望而生畏,太多?/span>
2. 无法下手Q都是直接对象依赖,而不是接口依赖?/span>
所以,你让我来这L(fng)代码Q我是不?x)干的?/span>
因ؓ(f)Q我希望的是q样的:(x)
但是Q我们现在的代码Ơ债太多了。没有条件和能力再回dq些代码q行单元试。而且Q这些功能经q这么多q的l护Q大多已E_。做的性h(hun)比也不高,不够实惠。所以,不必要做?/span>
但在新功能开发过E中Q这些老代码依旧会(x)如恶梦一L(fng)~着我们。让写单元测试过E中常常面(f)着举步l艰的境地。我们不得不在让代码变得可测与对代码的R入性测试之间进行抉择?/span>
原地址:http://www.aygfsteel.com/wukaichun600/archive/2008/06/10/206999.html