??xml version="1.0" encoding="utf-8" standalone="yes"?>
q里我想主要讨论下在finally块加?/span>return语句Q?strong>屏蔽异常的问题?/span>
我们先来看这样一D代码,
代码1Q?/span>
以上代码?/span>Eclipse里是不会~译通过的,因ؓ?/span>catch块中throw了一个异常,?/span>printҎq没有声明要抛出异常?/span>
现在我们修改代码Q让它能够通过~译Q代?/span>2Q?br />
是?/span>print?/span>mainҎ后加throws ExceptionQ然后运行,看运行结果:
E序先是走到?/span>catch块的W一句,打印?/span>at catch block step 1.
但ƈ没有紧接着?/span>throw eQ?strong>x有立L个异?/span>Q之所以这栯Q是因ؓ异常的信息是?/span>finally块的打印信息之后才打印的?/span>
q个例子告诉我们Q?/span>finally不管?/span>?/span>异常与否Q?/span>都必?/span>?/span>?/span>行的?/span>?/span>?/span>Q如果中途终止了jvmQ就不必L行了Q?/span>
那么何时执行finally块中的代码呢Q?/span>
在这个例子中Q?/span>try块中有异常抛出,所?/span>finally块中的代?strong>是在执行?/span>catch语句之后、退出方法之前被执行?/span>(如果q里执行?/span>throw eQ则Ҏ退Z) ?/span>
下面再看另外一个代码,代码3Q?/span>
q段代码与之前相比,在finally块中增加了return语句?/p>
虽然在catch块中有throw e语句Q但在printҎ后ƈ不用声明throws ExceptionQ也可以通过~译?/p>
因ؓ在try块中有Thread.sleep(1);语句Q所以必要捕获InterruptedExceptionQ但在这U情况下Q即使我们把catch块去掉了Q也不会有问题,q是怎么回事呢?
因ؓ在finally块中的return语句屏蔽了异常?/p>
l过代码2我们已经知道了,异常在finally块被执行之前Q虽然会执行catch块中的代码,但ƈ不会退出方法,在退出方法之前,会{向finally块中执行Q而在finally块中又恰好有return语句Q所以方法就正常退ZQ在try块中产生的异常就不会有机会被抛出?/p>
----2009q?2?3?/p>
问题的发玎ͼ
pȝ中在q箋不停地、反复进行一个操作(先打开AQ然后切替到画面BQ点ȝ面历再回到AQ如此反复)。经q长旉的测试,l常?/span>20时Q?/span>JVM的内存用量增长30M以上?/span>
问题的分析:
首先Ҏ操作Q找C执行的代码,对代码进行分析?/span>
Java会生内存泄露的原因Q经q本ơ调查,
1.对于打开?/span>socket{资源,没有做及时的回收处理?/span>虽然?/span>javaE序Q有GC帮助我们理内存Q但好的~程习惯q是需要的Q可以避免不必要的麻烦?/span>
1.复杂的对象,在不需要的情况下,最好能实现对它的成员的销毁,然后再将其赋?/span>null?br /> 2.对于打开的流Q一定要做及时的处理。另外对?/span>HttpURLConnection对象Q连接后Q要调用它的disconnectQ)Q不要对资源q行不必要的费?br /> 3.量用全局变量?br /> 4.在哪里生成对象,在哪里销毁它?/span>最后,记述一下我记录内存的方法?/span>
׃对代码做好修改之后,要确认一下内存是否有明显增长?/span>
于是写一D代码,每个5分钟对对内存q行一ơ记录,在连l运?/span>20时候,做成曲线图,以便分析?/span>
Q以下是Z方便Q重新写的,原来目中用到的Q有一整套完备的定时器生成和v动的理c,q里没有写出来。)
如果安装了JDKQ会发现你的电脑有两套JREQ一套位?\jre 另外一套位?C:\Program Files\Java\j2re1.4.1_01 目录下,后面q套比前面那套少了Server端的Java虚拟机,不过直接前面那套的Server端Java虚拟机复制过来就行了。而且在安装JDK可以选择是否安装q个位于 C:\Program Files\Jav a 目录下的JRE。如果你只安装JREQ而不是JDKQ那么只会在 C:\Program Files\Java 目录下安装唯一的一套JRE?
JRE的地位就象一台PCZP我们写好的Win32应用E序需要操作系l帮我们q行Q同LQ我们编写的JavaE序也必要JRE才能q行。所以当你装完JDK后,如果分别在硬盘上的两个不同地方安装了两套JREQ那么你可以惌你的电脑有两台虚拟的Java PC机,都具有运行JavaE序的功能。所以我们可以说Q只要你的电脑安装了JREQ就可以正确q行Jav a应用E序?/p>
1、ؓ什么Sun要让JDK安装两套相同的JREQ这是因为JDK里面有很多用Java所~写的开发工P如javac.exe、jar.exe{)Q而且都放|在 \lib\tools.jar 里。从下面例子可以看出Q先tools.jar改名为tools1.jarQ然后运行javac.exeQ显C如下结果: Exception in thread "main" java.lang.NoClassDefFoundError: com/sun/tools/javac /Main q个意思是_你输入javac.exe与输?java -cp c:\jdk\lib\tools.jar com.sun.tools.javac.Main 是一LQ会得到相同的结果。从q里我们可以证明javac.exe只是一个包装器QWrapperQ,而制作的目的是ؓ了让开发者免于输入太长的指命。而且可以发现\lib目录下的E序都很,不大? 9KQ从q里我们可以得出一个结论。就是JDK里的工具几乎是用Java所~写Q所以也是Java应用E序Q因此要使用JDK所附的工具来开发JavaE序Q也必须要自行附一套JRE才行Q所以位于C:\Program Files\Java目录下的那套JRE是用来q行一般JavaE序用的?
2、如果一台电脑安装两套以上的JREQ谁来决定呢Q这个重大Q务就落在java.exew上。Java.exe的工作就是找到合适的JRE来运行JavaE序?Java.exe依照底下的顺序来查找JREQ自q目录下有没有JREQ父目录有没有JREQ查询注册表Q?[HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Java Runtime Environment] 所以java.exe的运行结果与你的电脑里面哪个JRE被执行有很大的关pR?
3、介lJVM JRE目录下的Bin目录有两个目录:server与client。这是真正的jvm.dll所在?jvm.dll无法单独工作Q当jvm.dll启动后,会用explicit的方法(是使用Win32 API之中的LoadLibrary()与GetProcAddress()来蝲入辅助用的动态链接库Q,而这些辅助用的动态链接库Q?dllQ都必须位于jvm.dll所在目录的父目录之中。因此想使用哪个JVMQ只需要设|PATHQ指向JRE所在目录底下的jvm.dll?/p>
----2008q?1?4?/p>
----2008q?1?7?/span>
单的_
Java把内存划分成两种Q一U是栈内存,一U是堆内存?
在函C定义的一些基本类型的变量和对象的引用变量都在函数的栈内存中分配?
当在一D代码块定义一个变量时QJava在栈中个变量分配内存空_当超q变量的作用域后QJava会自动释放掉变量所分配的内存空_该内存空间可以立卌另作他用?
堆内存用来存攄new创徏的对象和数组?
在堆中分配的内存Q由Java虚拟机的自动垃圾回收器来理?
在堆中生了一个数l或对象后,q可以在栈中定义一个特D的变量Q让栈中q个变量的取值等于数l或对象在堆内存中的首地址Q栈中的q个变量成了数l或对象的引用变量?
引用变量q当于是ؓ数组或对象v的一个名Uͼ以后可以在E序中用栈中的引用变量来访问堆中的数组或对象?
具体的说Q?br />
栈与堆都是Java用来在Ram中存放数据的地方。与C++不同QJava自动理栈和堆,E序员不能直接地讄栈或堆?
Java的堆是一个运行时数据?cȝ(对象从中分配I间。这些对象通过new、newarray、anewarray和multianewarray{?指o建立Q它们不需要程序代码来昑ּ的释放。堆是由垃圾回收来负责的Q堆的优势是可以动态地分配内存大小Q生存期也不必事先告诉编译器Q因为它是在q行?动态分配内存的QJava的垃圾收集器会自动收走这些不再用的数据。但~点是,׃要在q行时动态分配内存,存取速度较慢?
栈的优势是,存取速度比堆要快Q仅ơ于寄存器,栈数据可以共享。但~点是,存在栈中的数据大与生存期必L定的,~Z灉|性。栈中主要存放一些基?cd的变量(,int, short, long, byte, float, double, boolean, charQ和对象句柄?
栈有一个很重要的特D性,是存在栈中的数据可以共享。假设我们同时定义:
int a = 3;
int b = 3Q?
~译器先处理int a = 3Q首先它会在栈中创徏一个变量ؓa的引用,然后查找栈中是否?q个|如果没找刎ͼ将3存放q来Q然后将a指向3。接着处理int b = 3Q在创徏完b的引用变量后Q因为在栈中已经?q个|便将b直接指向3。这P出Ca与b同时均指?的情c这Ӟ如果再oa=4Q那么编译器 会重新搜索栈中是否有4|如果没有Q则?存放q来Qƈ令a指向4Q如果已l有了,则直接将a指向q个地址。因此a值的改变不会影响到b的倹{要注意q?U数据的׃n与两个对象的引用同时指向一个对象的q种׃n是不同的Q因U情况a的修改ƈ不会影响到b, 它是q译器完成的,它有利于节省I间。而一个对象引用变量修改了q个对象的内部状态,会媄响到另一个对象引用变量?
String是一个特D的包装cL据。可以用Q?
String str = new String("abc");
String str = "abc";
两种的Ş式来创徏Q第一U是用new()来新建对象的Q它会在存放于堆中。每调用一ơ就会创Z个新的对象?
而第二种是先在栈中创Z个对Stringcȝ对象引用变量strQ然后查找栈中有没有存放"abc"Q如果没有,则将"abc"存放q栈Qƈ令str指向”abc”Q如果已l有”abc” 则直接ostr指向“abc”?
比较c里面的数值是否相{时Q用equals()ҎQ当试两个包装cȝ引用是否指向同一个对象时Q用==Q下面用例子说明上面的理论?
String str1 = "abc";
String str2 = "abc";
System.out.println(str1==str2); //true
可以看出str1和str2是指向同一个对象的?
String str1 =new String ("abc");
String str2 =new String ("abc");
System.out.println(str1==str2); // false
用new的方式是生成不同的对象。每一ơ生成一个?
因此用第二种方式创徏多个”abc”字符?在内存中其实只存在一个对象而已. q种写法有利与节省内存空? 同时它可以在一定程度上提高E序的运行速度Q因为JVM会自动根据栈中数据的实际情况来决定是否有必要创徏新对象。而对于String str = new String("abc")Q的代码Q则一概在堆中创徏新对象,而不其字符串值是否相{,是否有必要创建新对象Q从而加重了E序的负担?
另一斚w, 要注? 我们在用诸如String str = "abc"Q的格式定义cLQL惛_然地认ؓQ创ZStringcȝ对象str。担心陷阱!对象可能q没有被创徏Q而可能只是指向一个先前已l创建的 对象。只有通过new()Ҏ才能保证每次都创Z个新的对象?׃Stringcȝimmutable性质Q当String变量需要经常变换其值时Q应该考虑使用StringBufferc,以提高程序效率?/p>
java中内存分配策略及堆和栈的比较
2.1 内存分配{略
按照~译原理的观?E序q行时的内存分配有三U策?分别是静态的,栈式?和堆式的.
静态存储分配是指在~译时就能确定每个数据目标在q行时刻的存储空间需?因而在~译时就可以l他们分配固定的内存I间.q种分配{略要求E序代码中不?许有可变数据l构(比如可变数组)的存?也不允许有嵌套或者递归的结构出?因ؓ它们都会D~译E序无法计算准确的存储空间需?
栈式存储分配也可UCؓ动态存储分?是由一个类g堆栈的运行栈来实现的.和静态存储分配相?在栈式存储方案中,E序Ҏ据区的需求在~译时是完全未知 ?只有到运行的时候才能够知道,但是规定在运行中q入一个程序模块时,必须知道该程序模块所需的数据区大小才能够ؓ其分配内?和我们在数据l构所熟知 的栈一?栈式存储分配按照先进后出的原则进行分配?
静态存储分配要求在~译时能知道所有变量的存储要求,栈式存储分配要求在过E的入口处必ȝ道所有的存储要求,而堆式存储分配则专门负责在编译时或运行时 模块入口处都无法定存储要求的数据结构的内存分配,比如可变长度串和对象实例.堆由大片的可利用块或I闲块组?堆中的内存可以按照Q意顺序分配和??
2.2 堆和栈的比较
上面的定义从~译原理的教材中ȝ而来,除静态存储分配之?都显得很呆板和难以理?下面撇开静态存储分?集中比较堆和?
从堆和栈的功能和作用来通俗的比?堆主要用来存攑֯象的Q栈主要是用来执行程序的.而这U不同又主要是由于堆和栈的特点决定的:
在编E中Q例如C/C++中,所有的Ҏ调用都是通过栈来q行?所有的局部变?形式参数都是从栈中分配内存空间的。实际上也不是什么分?只是从栈?向上用就?好像工厂中的传送带(conveyor belt)一?Stack Pointer会自动指引你到放东西的位|?你所要做的只是把东西放下来就?退出函数的时候,修改栈指针就可以把栈中的内容销?q样的模式速度最? 当然要用来运行程序了.需要注意的?在分配的时?比如Z个即要调用的程序模块分配数据区?应事先知道这个数据区的大?也就说是虽然分配是在E?序运行时q行?但是分配的大多是定?不变?而这?大小多少"是在~译时确定的,不是在运行时.
堆是应用E序在运行的时候请求操作系l分配给自己内存Q由于从操作pȝ理的内存分?所以在分配和销毁时都要占用旉Q因此用堆的效率非常?但是堆的 优点在于,~译器不必知道要从堆里分配多存储空_也不必知道存储的数据要在堆里停留多长的时?因此,用堆保存数据时会得到更大的灵zL。事实上,?向对象的多态?堆内存分配是必不可少?因ؓ多态变量所需的存储空间只有在q行时创Z对象之后才能定.在C++中,要求创徏一个对象时Q只需?new命o~制相关的代码即可。执行这些代码时Q会在堆里自动进行数据的保存.当然Qؓ辑ֈq种灉|性,必然会付Z定的代h:在堆里分配存储空间时会花 掉更长的旉Q这也正是导致我们刚才所说的效率低的原因,看来列宁同志说的?人的优点往往也是人的~点,人的~点往往也是人的优点(晕~).
2.3 JVM中的堆和?
JVM是基于堆栈的虚拟?JVM为每个新创徏的线E都分配一个堆?也就是说,对于一个JavaE序来说Q它的运行就是通过对堆栈的操作来完成的。堆栈以帧ؓ单位保存U程的状态。JVM对堆栈只q行两种操作:以为单位的压栈和出栈操作?
我们知道,某个U程正在执行的方法称为此U程的当前方?我们可能不知?当前Ҏ使用的UCؓ当前帧。当U程ȀzM个JavaҎ,JVM׃在线E的 Java堆栈里新压入一个。这个自然成ؓ了当前.在此Ҏ执行期间,q个帧将用来保存参数,局部变?中间计算q程和其他数?q个帧在q里和编?原理中的zdU录的概忉|差不多的.
从Java的这U分配机制来?堆栈又可以这L?堆栈(Stack)是操作系l在建立某个q程时或者线E?在支持多U程的操作系l中是线E?个线E徏立的存储区域Q该区域h先进后出的特性?
每一个Java应用都唯一对应一个JVM实例Q每一个实例唯一对应一个堆。应用程序在q行中所创徏的所有类实例或数l都攑֜q个堆中,q由应用所有的U程 ׃n.跟C/C++不同QJava中分配堆内存是自动初始化的。Java中所有对象的存储I间都是在堆中分配的Q但是这个对象的引用却是在堆栈中分配,?是说在建立一个对象时从两个地斚w分配内存Q在堆中分配的内存实际徏立这个对象,而在堆栈中分配的内存只是一个指向这个堆对象的指?引用)而已?br />
2.4 GC的思?br />
JavaZ么慢?JVM的存在当然是一个原?但有,在Java?除了单类?int,char{?的数据结?其它都是在堆中分配内?所以说Java的一切都是对?Q这也是E序慢的原因之一?br />
我的x?应该说代表TIJ的观?,如果没有Garbage Collector(GC),上面的说法就是成立的.堆不象栈是连l的I间,没有办法指望堆本w的内存分配能够象堆栈一h有传送带般的速度,因ؓ,谁会 Z整理庞大的堆I间,让你几乎没有延迟的从堆中获取新的I间?
q个时?GC站出来解决问?我们都知道GC用来清除内存垃圾,为堆腑ևI间供程序?但GC同时也担负了另外一个重要的d,是要让Java中堆 的内存分配和其他语言中堆栈的内存分配一样快,因ؓ速度的问题几乎是众口一词的对Java的诟?要达到这L目的,必M堆的分配也能够做到象传送带 一?不用自己操心LI闲I间.q样,GC除了负责清除Garbage?q要负责整理堆中的对?把它们{Ud一个远Garbage的纯净I间中无 间隔的排列v?p堆栈中一L?q样Heap Pointer可以方便的指向传送带的v始位|?或者说一个未使用的空?Z一个需要分配内存的对象"指引方向".因此可以q样?垃圾攉影响了对 象的创徏速度,听v来很?对不?
那GC怎样在堆中找到所有存zȝ对象?前面说了,在徏立一个对象时Q在堆中分配实际建立q个对象的内?而在堆栈中分配一个指向这个堆对象的指???,那么只要在堆?也有可能在静态存储区)扑ֈq个引用,可以跟t到所有存zȝ对象.扑ֈ之后,GC它们从一个堆的块中移到另外一个堆的块?q?它们一个挨一个的排列h,p我们上面说的那样,模拟Z一个栈的结?但又不是先进后出的分?而是可以L分配?在速度可以保证的情况下, Isn't it great?
但是Q列宁同志说?人的优点往往也是人的~点,人的~点往往也是人的优点(再晕~~).GC()的运行要占用一个线E?q本w就是一个降低程序运行性能 的缺?更何况这个线E还要在堆中把内存翻来覆ȝ折腾.不仅如此,如上面所?堆中存活的对象被搬移了位|?那么所有对q些对象的引用都要重新赋?q?些开销都会D性能的降?
基础数据cd直接在栈I间分配Q方法的形式参数Q直接在栈空间分配,当方法调用完成后从栈I间回收。引用数据类型,需要用new来创建,既在栈空?分配一个地址I间Q又在堆I间分配对象的类变量 。方法的引用参数Q在栈空间分配一个地址I间Qƈ指向堆空间的对象区,当方法调用完成后从栈I间回收。局部变量new出来Ӟ在栈I间和堆I间中分配空 _当局部变量生命周期结束后Q栈I间立刻被回Ӟ堆空间区域等待GC回收。方法调用时传入的literal参数Q先在栈I间分配Q在Ҏ调用完成后从?I间分配。字W串帔R在DATA区域分配Qthis在堆I间分配。数l既在栈I间分配数组名称Q又在堆I间分配数组实际的大!
?对了Q补充一下static在DATA区域分配?/p>
其实是有规律的,只要你理解了q些个基本的原理Q?/p>
堆空间的话:操作pȝ有一个记录空闲内存地址的链表,当系l收到程序的甌Ӟ会遍历该链表Q寻扄一个空间大 于所甌I间的堆l点Q然后将该结点从I闲l点链表中删除,q将该结点的I间分配l程序。另外,对于大多数系l,会在q块内存I间中的首地址处记录本ơ分 配的大小Q这样代码中的delete语句才能正确的释放本内存I间。另外由于找到的堆结点的大小不一定正好等于申L大小Q系l会自动的将多余的那部分?新放入空闲链表中。是由new分配的内存,一般速度比较慢,而且Ҏ产生内存片Q不q用h最方便。另外,在WINDOWS下,最好的方式是用 VirtualAlloc分配内存Q他不是在堆Q也不是在栈是直接在q程的地址I间中保留一快内存,虽然用v来最不方ѝ但是速度快,也最灉|。是向高?址扩展的数据结构,是不q箋的内存区域。这是由于系l是用链表来存储的空闲内存地址的,自然是不q箋的,而链表的遍历方向是由低地址向高地址。堆的大受 限于计算机系l中有效的虚拟内存。由此可见,堆获得的I间比较灉|Q也比较大?/p>
栈空间的话:在Windows? 栈是向低地址扩展的数据结构,是一块连l的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是pȝ预先规定好的Q在WINDOWS下,栈的大小是固?的(是一个编译时q定的常数Q,如果甌的空间超q栈的剩余空间时Q将提示overflow。因此,能从栈获得的I间较小。只要栈的剩余空间大于所甌 I间Q系l将为程序提供内存,否则报异常提示栈溢出?ql自动分配,速度较快。但E序员是无法控制的?/p>
JVM中的堆和?/p>
JVM是基于堆栈的虚拟机。JVM为每个新创徏的线E都分配一个堆栈。也是_对于一个JavaE序来说Q它的运行就是通过对堆栈的操作来完成的。堆栈以帧ؓ单位保存U程的状态。JVM对堆栈只q行两种操作:以为单位的压栈和出栈操作?/p>
我们知道Q某个线E正在执行的ҎUCؓ此线E的当前Ҏ。我们可能不知道Q当前方法用的帧称为当前。当U程ȀzM个JavaҎQJVM׃?U程的Java堆栈里新压入一个。这个自然成ؓ了当前.在此Ҏ执行期间Q这个ឮ用来保存参敎ͼ局部变量,中间计算q程和其他数据。这个在这?和编译原理中的活动纪录的概念是差不多的?/p>
从Java的这U分配机制来?堆栈又可以这L解:堆栈(Stack)是操作系l在建立某个q程时或者线E?在支持多U程的操作系l中是线E?个线E徏立的存储区域Q该区域h先进后出的特性?/p>
每一个Java应用都唯一对应一个JVM实例Q每一个实例唯一对应一个堆。应用程序在q行中所创徏的所有类实例或数l都攑֜q个堆中Qƈ由应用所?的线E共享。跟C/C++不同QJava中分配堆内存是自动初始化的。Java中所有对象的存储I间都是在堆中分配的Q但是这个对象的引用却是在堆栈中??也就是说在徏立一个对象时从两个地斚w分配内存Q在堆中分配的内存实际徏立这个对象,而在堆栈中分配的内存只是一个指向这个堆对象的指?引用)?巌Ӏ?/p>
----2008q?1?4?/p>