??xml version="1.0" encoding="utf-8" standalone="yes"?> Brian Walker 可能让h觉得很奇怪,但好像的没有什?/strong>“正式?/strong>”T-SQL ~码标准。早?/strong> 1999 q末的时候,我惊喜地发现 John Hindmarsh 提出?/strong> SQL Server 7.0 标准Q我?/strong> 2000 q?/strong> 2 月的C论中对他的某些q行了ȝ。(2000 q?/strong> 2 月以及本月的“下蝲”中都包括?/strong> John 原来的标准。)后来Q?/strong>Ron Talmage 撰写了一pd专栏文章Q提Z他对各种“最x?/strong>”的徏议,当然Q?/strong>SQL Server 组也已正式发布?/strong> SQL Server 最x法分析器 (SQLBPA)。现在,一位具有超q?/strong> 25 q经验的数据库管理员和应用程序开发员 Brian Walker 又提Z他的和提C?/strong> q行 T-SQL ~程时常怼忽略~码标准Q但q些标准却是开发小l顺利开展工作的关键工具。这里介l的~码标准是我多年的开发成果。它们当然还没有得到普遍接受Q而且不可否认Q有些标准带有主观色彩。我的目的实际上更多的是Z提高大家的意识,而不是吹捧自己是 T-SQL 样式斚w的仲裁者:最重要的是要徏立某些合理的~码标准q循这些标准。您在这文章中会发现有?T-SQL ~程的一pd不同的编码标准、技巧和提示。它们ƈ未以M特定的优先或重要性顺序列出?/p>
让我们从格式开始。表面上QT-SQL 代码的格式似乎ƈ不重要,但一致的格式可以使您的同事(不论是同一组的成员还是更大范围的 T-SQL 开发团队的成员Q更L地浏览和理解您的代码。T-SQL 语句有一个结构,遵@一目了然的l构使您可以更轻村֜查找和确认语句的不同部分。统一的格式还使您可以更轻村֜在复?T-SQL 语句中增删代码段Q调试工作变得更容易。下面是 SELECT 语句的格式示例: Z个嵌套代码块中的语句使用四个I格的羃q。(上述代码中的多行 SELECT 语句是一?SQL 语句。)在同一语句中开始新行时Q SQL 关键字右寚w。将代码~辑器配|ؓ使用I格Q而不是用制表符。这P不管使用何种E序查看代码Q格式都是一致的?/p>
►大写所有的 T-SQL 关键字,包括 T-SQL 函数。变量名U及光标名称使用混和大小写。数据类型用小写?/p>
名别名要短,但意义要量明确。通常Q用大写的表名作ؓ别名Q?AS 关键字指定表或字D늚别名?/p>
►当一?T-SQL 语句中涉及到多个表时Q始l用表名别名来限定字段名。这使其他h阅读h更清楚,避免了含义模p的引用?/p>
►当相关数字出现在连l的代码行中Ӟ例如一pd SUBSTRING 函数调用Q,它们排成列。这样容易浏览数字列表?/p>
Z用一个(而不是两个)I分隔 T-SQL 代码的逻辑块,只要需要就可以使用?/p>
►声?T-SQL 局部变量(例如 @lngTableIDQ时Q用适当的数据类型声明和一致的大写?/p>
?strong>始终指定字符数据cd的长度,q确保允许用户可能需要的最大字W数Q因出最大长度的字符会丢失?/p>
?strong>始终指定十进制数据类型的_ֺ和范_否则Q将默认为未指定_ֺ和整数范围? Z用错误处理程序,但要C行首 (BOL) 中的错误查示例不会象介绍的那栯v作用。用来检?@@ERROR pȝ函数?T-SQL 语句 (IF) 实际上在q程中清除了 @@ERROR |无法再捕获除零之外的M倹{(即ɽCZ起作用,它们也只能捕h后发生的一个错误,而不是您更想捕获的第一个错误。)必须使用 SET ?SELECT 立即捕获错误代码Q如前面CZ所C。如果状态变量仍然ؓӞ应{换到状态变量? ►避免?#8220;未声明的”功能Q例如系l表中未声明的列、T-SQL 语句中未声明的功能或者未声明的系l存储过E或扩展的存储过E? ?strong>不要依赖M隐式的数据类型{换。例如,不能为数字变量赋予字W|而假?T-SQL 会进行必要的转换。相反,在ؓ变量赋值或比较g前,应用适当?CONVERT 函数使数据类型相匚w。另一个示例:虽然 T-SQL 会在q行比较之前对字W表辑ּq行隐式且自动的 RTRIMQ但不能依赖此行为,因ؓ兼容性别设|非字符表达式会使情况复杂化?/p>
?strong>不要空的变量值直接与比较q算W(W号Q比较。如果变量可能ؓI,应?IS NULL ?IS NOT NULL q行比较Q或者?ISNULL 函数?/p>
Z要?STR 函数q行舍入Q此函数只能用于整数。如果需要十q制值的字符串Ş式,应先使用 CONVERT 函数Q{至不同的范围Q或 ROUND 函数Q然后将其{换ؓ字符丌Ӏ也可以使用 CEILING ?FLOOR 函数?/p>
Z用数学公式时要小心,因ؓ T-SQL 可能会将表达式强制理解ؓ一个不需要的数据cd。如果需要十q制l果Q应在整数常量后加点和零 (.0)?/p>
►决不要依赖 SELECT 语句会按M特定序q回行,除非?ORDER BY 子句中指定了序?/p>
►通常Q应?ORDER BY 子句?SELECT 语句一起用。可预知的顺序(即不是最方便的)比不可预知的序强,其是在开发或调试q程中。(部v到生产环境中之前Q可能需要删?ORDER BY 子句。)在返回行的顺序无关紧要的情况下,可以忽略 ORDER BY 的开销?/p>
Z要在 T-SQL 代码中用双引号。应为字W常量用单引号。如果没有必要限定对象名Uͼ可以使用Q非 ANSI SQL 标准Q括号将名称括v来?/p>
►在 SQL Server 2000 中,量使用表变量来代替临时表。如果表变量包含大量数据Q请注意索引非常有限Q只有主键烦引)?/p>
►先在例E中创徏临时表,最后再昑ּ删除临时表。将 DDL ?DML 语句混合使用有助于处理额外的重新~译zd?/p>
认识C时表q不?/strong>不可使用Q适当C用它们可以某些例程更有效,例如Q当需要重复引用大型表或常用表中的某个数据集时。但是,对于一ơ性事Ӟ最好用导? ?/em>使用表?UDF 时要心Q因为在变量Q而不是常量)中传递某个参数时Q如果在 WHERE 子句中用该参数Q会D表扫描。还要避免在一个查询中多次使用相同的表?UDF。但是,表?UDF 实h某些非常方便的动态编译功能?em>[相关资料Q参?/em> Tom Moreau ?/em> 2003 q?/em> 11 月䆾“生成序列?/em>”专栏中的“使用 UDF 填充表变?/em>”。-~者按Q?/em> ►几乎所有的存储q程都应在开始时讄 SET NOCOUNT ONQ而在l束时设|?SET NOCOUNT OFF?em>[SET NOCOUNT ON ?/em> SQL Server 无需在执行存储过E的每个语句后向客户端发?/em> DONE_IN_PROC 消息?/em>- ~者按] 此标准同样适用于触发器?/p>
►只要在例程中用多个数据库修改语句Q包括在一个@环中多次执行一个语句,应考虑声明昑ּ事务?/p>
Z用基于光标的Ҏ或时表Ҏ之前Q应先寻扑֟于集的解x案来解决问题。基于集的方法通常更有效?/p>
Z临时表一P光标q不是不可用。对型数据集?FAST_FORWARD 光标通常要优于其他逐行处理ҎQ尤其是在必d用几个表才能获得所需的数据时。在l果集中包括“合计”的例E通常要比使用光标执行的速度快。如果开发时间允许,Z光标的方法和Z集的Ҏ都可以尝试一下,看哪一U方法的效果更好?/p>
Z用包含序P?1 ?NQ的表很方便?/p>
►理?CROSS JOIN 的工作原理ƈ加以利用。例如,您可以在工作数据表和序号表之间有效地使用 CROSS JOINQ结果集中将包含每个工作数据与序L合的记录? ►我的结束语是:T-SQL 代码往往很简z,因此如果某个代码块看h很难处理或重复内容较多,那么可能存在一U更单,更好的方法? l论 如果您对我的有Q何看法,Ƣ迎随时向我发送电子邮件进行讨论,也可以就其他问题提出您的。我希望您将此作话的开场白? 其他信息Q摘?/strong> Karen 2000 q?/strong> 2 月䆾的社?/strong> 在标准开发的前沿阵地上,有一股以 SQL Server 数据库管理员 John Hindmarsh 为首的独立的新生力量。MCT、MCSE ?MCDBA 都是最值得您花旉ȝI的。John 的A献是撰写了一份详l的白皮书,概述了他对各U?SQL Server 相关标准提出的徏议。我所知道的其他唯一提出cM的文章是 Andrew Zanevsky 的《Transact-SQL Programming?ISBN 1-56592-401-0) 中的“Format and Style”一章。Andrew、SQL Server Professional 的投Eh Tom Moreau ?Paul Munkenbeck 以及 John 的朋友兼同事 Stephen James 都ؓ John 的白皮书做出qA献。下面是 John 为编写存储过E提供的CZQ?/p>
使用 SQL-92 标准q接句法? Z提高性能Q应优先使用q接Q然后用子查询或嵌套查询? 保变量和参数的cd和大与表数据列相匹配? 保使用所有变量和参数Q或者全部删除? 可能将临时对象攄在本地? 只用在存储q程中创建的临时表? 查输入参数的有效性? 优先使用 SELECT...INTOQ然后?INSERT...SELECTQ以避免大量死锁? l护工作需要的逻辑单元Q在可以~短的情况下Q不要创建大量或长时间运行的q程? 不要在Q何代码中使用 SELECT *? 在过E中使用~进、块、制表符和空|参阅CZ脚本Q? T-SQL 语句要大写? 在过E中d大量注释Q确保可以识别进E。在有助于澄清处理步骤的地方使用行注释? 包括事务理Q除非要?MTS q程中调用过E。(?MTS q程~写独立的过E。) 监视 @@TRANCOUNT 以确定事务的责QU别? 避免使用 GOTOQ错误处理程序中除外? 避免使用嵌套q程? 避免隐式解析对象名称Q确保所有对象都?dbo 所有? 有关 SQL Server Professional ?Pinnacle Publishing 的详l信息,误问其 Web 站点 http://www.pinpub.com/ 注意Q这不是 Microsoft Corporation ?Web 站点。Microsoft 对该 Web 站点上的内容不承担Q何责仅R?/p>
本文转蝲?2004 q?12 月䆾?SQL Server Professional。除非另行说明,否则版权所?2004 Pinnacle Publishing, Inc.。保留所有权利。SQL Server Professional ?Pinnacle Publishing 独立发行的刊物。未l?Pinnacle Publishing, Inc. 事先同意Q不得以M方式使用或复制本文的M部分Q评论文章中的简短引用除外)。如需?Pinnacle Publishing, Inc. 联系Q请拨打 1-800-788-1900?/p>
ORACLE 的解析器按照从右到左的顺序处?/span>FROM子句中的表名Q?/span>FROM子句中写在最后的?/span>(基础?/span> driving table)被最先处理,?/span>FROM子句中包含多个表的情况下,你必选择记录条数最的表作为基表。如果有3个以上的表连接查?/span>, 那就需要选择交叉?/span>(intersection table)作ؓ基础?/span>, 交叉表是指那个被其他表所引用的表. Q?/span>2Q?/span> WHERE子句中的q接序Q: ORACLE采用自下而上的顺序解?/span>WHERE子句,Ҏq个原理,表之间的q接必须写在其他WHERE条g之前, 那些可以qo掉最大数量记录的条g必须写在WHERE子句的末?/span>. Q?/span>3Q?/span> SELECT子句中避免?/span> ‘ * ‘Q?/span> ORACLE在解析的q程?/span>, 会将'*' 依次转换成所有的列名, q个工作是通过查询数据字典完成?/span>, q意味着耗费更多的时?/span> Q?/span>4Q?/span> 减少讉K数据库的ơ数Q?/span> ORACLE在内部执行了许多工作: 解析SQL语句, 估算索引的利用率, l定变量 , L据块{; Q?/span>5Q?/span> ?/span>SQL*Plus , SQL*Forms?/span>Pro*C中重新设|?/span>ARRAYSIZE参数, 可以增加每次数据库访问的索数据量 ,gؓ200 Q?/span>6Q?/span> 使用DECODE函数来减处理时_ 使用DECODE函数可以避免重复扫描相同记录或重复连接相同的?/span>. Q?/span>7Q?/span> 整合?/span>,无关联的数据库访问: 如果你有几个单的数据库查询语?/span>,你可以把它们整合C个查询中(即它们之间没有关系) Q?/span>8Q?/span> 删除重复记录Q?/span> 最高效的删除重复记录方?/span> ( 因ؓ使用?/span>ROWID)例子Q?/span> DELETE FROM EMP E WHERE E.ROWID > (SELECT MIN(X.ROWID) FROM EMP X WHERE X.EMP_NO = E.EMP_NO); Q?/span>9Q?/span> ?/span>TRUNCATE替代DELETEQ?/span> 当删除表中的记录?/span>,在通常情况?/span>, 回滚D?/span>(rollback segments ) 用来存放可以被恢复的信息. 如果你没?/span>COMMIT事务,ORACLE会将数据恢复到删除之前的状?/span>(准确地说是恢复到执行删除命o之前的状?/span>) 而当q用TRUNCATE?/span>, 回滚D不再存放Q何可被恢复的信息.当命令运行后,数据不能被恢?/span>.因此很少的资源被调用,执行旉也会很短. (译者按: TRUNCATE只在删除全表适用,TRUNCATE?/span>DDL不是DML) Q?/span>10Q?/span> 量多?/span>COMMITQ?/span> 只要有可?/span>,在程序中量多?/span>COMMIT, q样E序的性能得到提高,需求也会因?/span>COMMIT所释放的资源而减?/span>: COMMIT所释放的资?/span>: a. 回滚D上用于恢复数据的信?/span>. b. 被程序语句获得的?/span> c. redo log buffer 中的I间 d. ORACLE为管理上q?/span>3U资源中的内部花?/span> Q?/span>11Q?/span> ?/span>Where子句替换HAVING子句Q?/span> 避免使用HAVING子句, HAVING 只会在检索出所有记录之后才对结果集q行qo. q个处理需要排?/span>,总计{操?/span>. 如果能通过WHERE子句限制记录的数?/span>,那就能减这斚w的开销. (?/span>oracle?/span>)on?/span>where?/span>havingq三个都可以加条件的子句中,on是最先执行,whereơ之Q?/span>having最后,因ؓon是先把不W合条g的记录过滤后才进行统计,它就可以减少中间q算要处理的数据Q按理说应该速度是最快的Q?/span>where也应该比having快点的,因ؓ它过滤数据后才进?/span>sumQ在两个表联接时才用on的,所以在一个表的时候,剩?/span>where?/span>having比较了。在q单表查询统计的情况下,如果要过滤的条g没有涉及到要计算字段Q那它们的结果是一LQ只?/span>where可以使用rushmore技术,?/span>having׃能,在速度上后者要慢如果要涉及到计的字段Q就表示在没计算之前Q这个字D늚值是不确定的Q根据上写的工作流E,where的作用时间是在计之前就完成的,?/span>having是在计后才v作用的,所以在q种情况下,两者的l果会不同。在多表联接查询Ӟon?/span>where更早起作用。系l首先根据各个表之间的联接条Ӟ把多个表合成一个时表后,再由whereq行qoQ然后再计算Q计完后再?/span>havingq行qo。由此可见,要想qo条g起到正确的作用,首先要明白这个条件应该在什么时候v作用Q然后再军_攑֜那里 Q?/span>12Q?/span> 减少对表的查询: 在含有子查询?/span>SQL语句?/span>,要特别注意减对表的查询.例子Q?/span> SELECT TAB_NAME FROM TABLES WHERE (TAB_NAME,DB_VER) = ( SELECT TAB_NAME,DB_VER FROM TAB_COLUMNS WHERE VERSION = 604) Q?/span>13Q?/span> 通过内部函数提高SQL效率.Q?/span> 复杂?/span>SQL往往牺牲了执行效?/span>. 能够掌握上面的运用函数解决问题的Ҏ在实际工作中是非常有意义?/span> Q?/span>14Q?/span> 使用表的别名(Alias)Q?/span> 当在SQL语句中连接多个表?/span>, 请用表的别名ƈ把别名前~于每?/span>Column?/span>.q样一?/span>,可以减解析的旉q减那些由Column歧义引v的语法错?/span>. Q?/span>15Q?/span> ?/span>EXISTS替代IN、用NOT EXISTS替代NOT INQ?/span> 在许多基于基表的查询?/span>,Z满一个条?/span>,往往需要对另一个表q行联接.在这U情况下, 使用EXISTS(?/span>NOT EXISTS)通常提高查询的效率. 在子查询?/span>,NOT IN子句执行一个内部的排序和合q?/span>. 无论在哪U情况下,NOT IN都是最低效?/span> (因ؓ它对子查询中的表执行了一个全表遍?/span>). Z避免使用NOT IN ,我们可以把它改写成外q接(Outer Joins)?/span>NOT EXISTS. 例子Q?/span> Q高效)SELECT * FROM EMP (基础?/span>) WHERE EMPNO > 0 AND EXISTS (SELECT ‘X' FROM DEPT WHERE DEPT.DEPTNO = EMP.DEPTNO AND LOC = ‘MELB') (低效)SELECT * FROM EMP (基础?/span>) WHERE EMPNO > 0 AND DEPTNO IN(SELECT DEPTNO FROM DEPT WHERE LOC = ‘MELB') Q?/span>16Q?/span> 识别'低效执行'?/span>SQL语句Q?/span> 虽然目前各种关于SQL优化的图形化工具层出不穷,但是写出自己?/span>SQL工具来解决问题始l是一个最好的ҎQ?/span> SELECT EXECUTIONS , DISK_READS, BUFFER_GETS, ROUND((BUFFER_GETS-DISK_READS)/BUFFER_GETS,2) Hit_radio, ROUND(DISK_READS/EXECUTIONS,2) Reads_per_run, SQL_TEXT FROM V$SQLAREA WHERE EXECUTIONS>0 AND BUFFER_GETS > 0 AND (BUFFER_GETS-DISK_READS)/BUFFER_GETS < 0.8 ORDER BY 4 DESC; Q?/span>17Q?/span> 用烦引提高效率: 索引是表的一个概念部?/span>,用来提高索数据的效率Q?/span>ORACLE使用了一个复杂的自^?/span>B-treel构. 通常,通过索引查询数据比全表扫描要?/span>. ?/span>ORACLE扑և执行查询?/span>Update语句的最佌\径时, ORACLE优化器将使用索引. 同样在联l多个表时用烦引也可以提高效率. 另一个用烦引的好处?/span>,它提供了主键(primary key)的唯一性验?/span>.。那?/span>LONG?/span>LONG RAW数据cd, 你可以烦引几乎所有的?/span>. 通常, 在大型表中用烦引特别有?/span>. 当然,你也会发?/span>, 在扫描小表时,使用索引同样能提高效?/span>. 虽然使用索引能得到查询效率的提高,但是我们也必L意到它的代h. 索引需要空间来存储,也需要定期维?/span>, 每当有记录在表中增减或烦引列被修Ҏ, 索引本n也会被修?/span>. q意味着每条记录?/span>INSERT , DELETE , UPDATEؓ此多付出4 , 5 ơ的盘I/O . 因ؓ索引需要额外的存储I间和处?/span>,那些不必要的索引反而会使查询反应时间变?/span>.。定期的重构索引是有必要?/span>.Q?/span> ALTER INDEX <INDEXNAME> REBUILD <TABLESPACENAME> Q?/span>18Q?/span> ?/span>EXISTS替换DISTINCTQ?/span> 当提交一个包含一对多表信?/span>(比如部门表和雇员?/span>)的查询时,避免?/span>SELECT子句中?/span>DISTINCT. 一般可以考虑?/span>EXIST替换, EXISTS 使查询更?/span>,因ؓRDBMS核心模块在子查询的条g一旦满_,立刻q回l果. 例子Q?/span> (低效): SELECT DISTINCT DEPT_NO,DEPT_NAME FROM DEPT D , EMP E WHERE D.DEPT_NO = E.DEPT_NO (高效): SELECT DEPT_NO,DEPT_NAME FROM DEPT D WHERE EXISTS ( SELECT ‘X' FROM EMP E WHERE E.DEPT_NO = D.DEPT_NO); Q?/span>19Q?/span> sql语句用大写的Q因?/span>oracleL先解?/span>sql语句Q把写的字母{换成大写的再执行 Q?/span>20Q?/span> ?/span>java代码中尽量少用连接符“Q?#8221;q接字符Ԍ Q?/span>21Q?/span> 避免在烦引列上?/span>NOT 通常Q 我们要避免在索引列上使用NOT, NOT会生在和在索引列上使用函数相同的媄?/span>. ?/span>ORACLE”遇到”NOT,他就会停止用烦引{而执行全表扫?/span>. Q?/span>22Q?/span> 避免在烦引列上用计. WHERE子句中,如果索引列是函数的一部分Q优化器不使用索引而用全表扫描. 举例: 低效Q?/span> SELECT … FROM DEPT WHERE SAL * 12 > 25000; 高效: SELECT … FROM DEPT WHERE SAL > 25000/12; Q?/span>23Q?/span> ?/span>>=替代> 高效: SELECT * FROM EMP WHERE DEPTNO >=4 低效: SELECT * FROM EMP WHERE DEPTNO >3 两者的区别在于, 前?/span>DBMS直接蟩到第一?/span>DEPT{于4的记录而后者将首先定位?/span>DEPTNO=3的记录ƈ且向前扫描到W一?/span>DEPT大于3的记?/span>. Q?/span>24Q?/span> ?/span>UNION替换OR (适用于烦引列) 通常情况?/span>, ?/span>UNION替换WHERE子句中的OR会起到较好的效?/span>. 对烦引列使用OR造成全表扫描. 注意, 以上规则只针对多个烦引列有效. 如果?/span>column没有被烦?/span>, 查询效率可能会因Z没有选择OR而降?/span>. 在下面的例子?/span>, LOC_ID ?/span>REGION上都建有索引. 高效: SELECT LOC_ID , LOC_DESC , REGION FROM LOCATION WHERE LOC_ID = 10 UNION SELECT LOC_ID , LOC_DESC , REGION FROM LOCATION WHERE REGION = “MELBOURNE” 低效: SELECT LOC_ID , LOC_DESC , REGION FROM LOCATION WHERE LOC_ID = 10 OR REGION = “MELBOURNE” 如果你坚持要?/span>OR, 那就需要返回记录最的索引列写在最前面. Q?/span>25Q?/span> ?/span>IN来替?/span>OR q是一条简单易记的规则Q但是实际的执行效果q须验,?/span>ORACLE8i下,两者的执行路径g是相同的Q 低效: SELECT…. FROM LOCATION WHERE LOC_ID = 10 OR LOC_ID = 20 OR LOC_ID = 30 高效 SELECT… FROM LOCATION WHERE LOC_IN IN (10,20,30); Q?/span>26Q?/span> 避免在烦引列上?/span>IS NULL?/span>IS NOT NULL 避免在烦引中使用M可以为空的列Q?/span>ORACLE无法用该索引Q对于单列烦引,如果列包含空|索引中将不存在此记录. 对于复合索引Q如果每个列都ؓI,索引中同样不存在此记?/span>. 如果臛_有一个列不ؓI,则记录存在于索引中.举例: 如果唯一性烦引徏立在表的A列和B列上, q且表中存在一条记录的A,Bgؓ(123,null) , ORACLE不接受下一条具有相?/span>A,B|123,nullQ的记录(插入). 然而如果所有的索引列都为空Q?/span>ORACLE认为整个键gؓI空不等于空. 因此你可以插?/span>1000 条具有相同键值的记录,当然它们都是I?/span>! 因ؓIg存在于烦引列?/span>,所?/span>WHERE子句中对索引列进行空值比较将?/span>ORACLE停用该烦?/span>. 低效: (索引失效) SELECT … FROM DEPARTMENT WHERE DEPT_CODE IS NOT NULL; 高效: (索引有效) SELECT … FROM DEPARTMENT WHERE DEPT_CODE >=0; Q?/span>27Q?/span> L使用索引的第一个列Q?/span> 如果索引是徏立在多个列上, 只有在它的第一个列(leading column)?/span>where子句引用?/span>,优化器才会选择使用该烦?/span>. q也是一条简单而重要的规则Q当仅引用烦引的W二个列?/span>,优化器用了全表扫描而忽略了索引 Q?/span>28Q?/span> ?/span>UNION-ALL 替换UNION ( 如果有可能的?/span>)Q?/span> ?/span>SQL 语句需?/span>UNION两个查询l果集合?/span>,q两个结果集合会?/span>UNION-ALL的方式被合ƈ, 然后在输出最l结果前q行排序. 如果?/span>UNION ALL替代UNION, q样排序׃是必要了. 效率׃因此得到提高. 需要注意的是,UNION ALL 重复输Z个结果集合中相同记录. 因此各位q是要从业务需求分析?/span>UNION ALL的可行?/span>. UNION 对l果集合排序,q个操作会用到SORT_AREA_SIZEq块内存. 对于q块内存的优化也是相当重要的. 下面?/span>SQL可以用来查询排序的消耗量 低效Q?/span> SELECT ACCT_NUM, BALANCE_AMT FROM DEBIT_TRANSACTIONS WHERE TRAN_DATE = '31-DEC-95' UNION SELECT ACCT_NUM, BALANCE_AMT FROM DEBIT_TRANSACTIONS WHERE TRAN_DATE = '31-DEC-95' 高效: SELECT ACCT_NUM, BALANCE_AMT FROM DEBIT_TRANSACTIONS WHERE TRAN_DATE = '31-DEC-95' UNION ALL SELECT ACCT_NUM, BALANCE_AMT FROM DEBIT_TRANSACTIONS WHERE TRAN_DATE = '31-DEC-95' Q?/span>29Q?/span> ?/span>WHERE替代ORDER BYQ?/span> ORDER BY 子句只在两种严格的条件下使用索引. ORDER BY中所有的列必d含在相同的烦引中q保持在索引中的排列序. ORDER BY中所有的列必d义ؓ非空. WHERE子句使用的烦引和ORDER BY子句中所使用的烦引不能ƈ?/span>. 例如: ?/span>DEPT包含以下?/span>: DEPT_CODE PK NOT NULL DEPT_DESC NOT NULL DEPT_TYPE NULL 低效: (索引不被使用) SELECT DEPT_CODE FROM DEPT ORDER BY DEPT_TYPE 高效: (使用索引) SELECT DEPT_CODE FROM DEPT WHERE DEPT_TYPE > 0 Q?/span>30Q?/span> 避免改变索引列的cd.: 当比较不同数据类型的数据?/span>, ORACLE自动对列q行单的cd转换. 假设 EMPNO是一个数值类型的索引?/span>. SELECT … FROM EMP WHERE EMPNO = ‘123' 实际?/span>,l过ORACLEcd转换, 语句转化?/span>: SELECT … FROM EMP WHERE EMPNO = TO_NUMBER(‘123') q运的是,cd转换没有发生在烦引列?/span>,索引的用途没有被改变. 现在,假设EMP_TYPE是一个字W类型的索引?/span>. SELECT … FROM EMP WHERE EMP_TYPE = 123 q个语句?/span>ORACLE转换?/span>: SELECT … FROM EMP WHERETO_NUMBER(EMP_TYPE)=123 因ؓ内部发生的类型{?/span>, q个索引不会被用到! Z避免ORACLE对你?/span>SQLq行隐式的类型{?/span>, 最好把cd转换用显式表现出?/span>. 注意当字W和数值比较时, ORACLE会优先{换数值类型到字符cd Q?/span>31Q?/span> 需要当心的WHERE子句: 某些SELECT 语句中的WHERE子句不用烦?/span>. q里有一些例?/span>. 在下面的例子?/span>, (1)‘!=' 不使用索引. C, 索引只能告诉你什么存在于表中, 而不能告诉你什么不存在于表?/span>. (2) ‘||'是字W连接函?/span>. p其他函数那样, 停用了烦?/span>. (3) ‘+'是数学函?/span>. p其他数学函数那样, 停用了烦?/span>. (4)相同的烦引列不能互相比较,q将会启用全表扫?/span>. Q?/span>32Q?/span> a. 如果索数据量过30%的表中记录数.使用索引没有显著的效率提高. b. 在特定情况下, 使用索引也许会比全表扫描?/span>, 但这是同一个数量上的区别. 而通常情况?/span>,使用索引比全表扫描要块几倍乃臛_千?/span>! Q?/span>33Q?/span> 避免使用耗费资源的操?/span>: 带有DISTINCT,UNION,MINUS,INTERSECT,ORDER BY?/span>SQL语句会启?/span>SQL引擎 执行耗费资源的排?/span>(SORT)功能. DISTINCT需要一ơ排序操?/span>, 而其他的臛_需要执行两ơ排?/span>. 通常, 带有UNION, MINUS , INTERSECT?/span>SQL语句都可以用其他方式重写. 如果你的数据库的SORT_AREA_SIZE调配得好, 使用UNION , MINUS, INTERSECT也是可以考虑?/span>, 毕竟它们的可L很?/span> Q?/span>34Q?/span> 优化GROUP BY: 提高GROUP BY 语句的效?/span>, 可以通过不需要的记录?/span>GROUP BY 之前qo?/span>.下面两个查询q回相同l果但第二个明显快了许?/span>. 低效: SELECT JOB , AVG(SAL) FROM EMP GROUP JOB HAVING JOB = ‘PRESIDENT' OR JOB = ‘MANAGER' 高效: SELECT JOB , AVG(SAL) FROM EMP WHERE JOB = ‘PRESIDENT' OR JOB = ‘MANAGER' GROUP JOB 问题一
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存在,垃圾攉器就永远不会释放用来容纳该对象的存储I间?br />
?nbsp;obj 出范围或被昑ּ地指定ؓ null Ӟ垃圾攉器就认ؓ没有对这个对象的其它引用Q也可以收集它了。然而您q需要注意一个重要的l节Q仅凭对象可以被攉q不意味着垃圾攉器的一ơ指定运行就能够回收它。由于各U垃圾收集算法有所不同Q某些算法会更频J地分析生存期较短的对象Q而不是较老、生存期较长的对象。因此,一个可供收集的对象可能永远也不会被回收。如果程序在垃圾攉器释攑֯象之前结束,q种情况可能会出现。因此,概括地说Q您永远无法保证可供攉的对象L会被垃圾攉器收集?br />
q些信息对于您分析引用类是很重要的。由于垃圾收集有着特定的性质Q所以引用类实际上可能没有您原来惛_的那么有用,管如此Q它们对于特定问题来说还是很有用的类。Y引用Qsoft referenceQ、弱引用Qweak referenceQ和虚引用(phantom referenceQ对象提供了三种不同的方式来在不妨碍攉的情况下引用堆对象。每U引用对象都有不同的行ؓQ而且它们与垃圾收集器之间的交互也有所不同。此外,q几个新的引用类都表现出比典型的强引?#8220;更弱”的引用Ş式。而且Q内存中的一个对象可以被多个引用Q可以是强引用、Y引用、弱引用或虚引用Q引用。在q一步往下讨Z前,让我们来看看一些术语:
强可及对象(strongly reachableQ:可以通过强引用访问的对象?br />
软可及对象(softly reachableQ:不是强可及对象,q且能够通过软引用访问的对象?br />
弱可及对象(weakly reachableQ:不是强可及对象也不是软可及对象,q且能够通过弱引用访问的对象?br />
虚可及对象(phantomly reachableQ:不是强可及对象、Y可及对象Q也不是弱可及对象,已经l束的,可以通过虚引用访问的对象?br />
清除Q将引用对象?nbsp;referent 域设|ؓ nullQƈ引用类在堆中引用的对象声明为可l束的?br />
SoftReference c?br />
SoftReference cȝ一个典型用途就是用于内存敏感的高速缓存。SoftReference 的原理是Q在保持对对象的引用时保证在 JVM 报告内存不情况之前清除所有的软引用。关键之处在于,垃圾攉器在q行时可能会Q也可能不会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结束,可供攉了。这使您能够刚好在对象占用的内存被回收之前采取行动?nbsp;
垃圾攉器和引用交互
垃圾攉器每ơ运行时都可以随意地释放不再是强可及的对象占用的内存。如果垃圾收集器发现了Y可及对象Q就会出C列情况:
SoftReference 对象?nbsp;referent 域被讄?nbsp;nullQ从而该对象不再引?nbsp;heap 对象?br />
SoftReference 引用q的 heap 对象被声明ؓ finalizable?br />
?nbsp;heap 对象?nbsp;finalize() Ҏ被运行而且该对象占用的内存被释放,SoftReference 对象pd到它?nbsp;ReferenceQueueQ如果后者存在的话)?br />
如果垃圾攉器发C弱可及对象,׃出现下列情况Q?br />
WeakReference 对象?nbsp;referent 域被讄?nbsp;nullQ从而该对象不再引?nbsp;heap 对象?br />
WeakReference 引用q的 heap 对象被声明ؓ finalizable?br />
?nbsp;heap 对象?nbsp;finalize() Ҏ被运行而且该对象占用的内存被释放时QWeakReference 对象pd到它?nbsp;ReferenceQueueQ如果后者存在的话)?br />
如果垃圾攉器发C虚可及对象,׃出现下列情况Q?br />
PhantomReference 引用q的 heap 对象被声明ؓ finalizable?br />
与Y引用和弱引用有所不同QPhantomReference 在堆对象被释放之前就被添加到它的 ReferenceQueue。(误住,所有的 PhantomReference 对象都必ȝl过兌?nbsp;ReferenceQueue 来创建。)q您能够在堆对象被回收之前采取行动?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,您必d消对 MyObject 对象的强引用Q方法是?nbsp;obj 讄?nbsp;null。前面说q,如果不这样做Q对?nbsp;MyObject 永远都不会被回收Q引用类的Q何优炚w会被削弱?br />
每个引用c都有一?nbsp;get() ҎQ?nbsp;ReferenceQueue cL一?nbsp;poll() Ҏ。get() Ҏq回对被引用对象的引用。在 PhantomReference 上调?nbsp;get() L会返?nbsp;null。这是因?nbsp;PhantomReference 只用于跟t收集。poll() Ҏ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() Ҏ返回同L|obj 仍然保持对该对象q行强引用。实际上Q对象布局q是没有改变Q和?nbsp;1 所C的差不多。然而,误虑下面的代码:
obj = null;
System.gc(); //run the collector
现在Q调?nbsp;get() ?nbsp;poll() 生与前面不同的结果:
wr.get(); //returns null
rq.poll(); //returns a reference to the WeakReference object
q种情况表明QMyObject 对象Q对它的引用原来是由 WeakReference 对象q行的)不再可用。这意味着垃圾攉器释放了 MyObject 占用的内存,从而 WeakReference 对象可以被放在它?nbsp;ReferenceQueue 上。这P您就可以知道?nbsp;WeakReference ?nbsp;SoftReference cȝ get() Ҏq回 null Ӟ有一个对象被声明?nbsp;finalizableQ而且可能Q不q不一定)被收集。只有当 heap 对象完全l束而且其内存被回收后,WeakReference ?nbsp;SoftReference 才会被放C其关联的 ReferenceQueue 上。清?nbsp;2 昄了一个完整的可运行程序,它展CZq些原理中的一部分。这D代码本w就颇具说明性,它含有很多注释和打印语句Q可以帮助您理解?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背后的原理是避免在应用程序执行期间将对象留在内存中。相反,您以软引用、弱引用或虚引用的方式引用对象,q样垃圾攉器就能够随意地释攑֯象。当您希望尽可能减小应用E序在其生命周期中用的堆内存大时Q这U用途就很有好处。您必须CQ要使用q些c,您就不能保留对对象的强引用。如果您q么做了Q那׃费q些cL提供的Q何好处?br />
另外Q您必须使用正确的编E风g查收集器在用对象之前是否已l回收了它,如果已经回收了,您首先必重新创对象。这个过E可以用不同的编E风格来完成。选择错误的风gD出问题。请考虑清单 3 中从 WeakReference 索被引用对象的代码风|
清单 3. 索被引用对象的风?br />
obj = wr.get();
if (obj == null)
{
wr = new WeakReference(recreateIt()); //1
obj = wr.get(); //2
}
//code that works with obj
研究了这D代码之后,L看清?nbsp;4 中从 WeakReference 索被引用对象的另一U代码风|
清单 4. 索被引用对象的另一U风?br />
obj = wr.get();
if (obj == null)
{
obj = recreateIt(); //1
wr = new WeakReference(obj); //2
}
//code that works with obj
h较这两种风格Q看看您能否定哪种风格一定可行,哪一U不一定可行。清?nbsp;3 中体现出的风g一定在所有情况下都可行,但清?nbsp;4 的风格就可以。清?nbsp;3 中的风格不够好的原因在于Qif 块的Ml束之后 obj 不一定是非空倹{请考虑一下,如果垃圾攉器在清单 3 的行 //1 之后但在?nbsp;//2 执行之前q行会怎样。recreateIt() Ҏ重新创对象Q但它会?nbsp;WeakReference 引用Q而不是强引用。因此,如果攉器在?nbsp;//2 在重新创建的对象上施加一个强引用之前q行Q对象就会丢失,wr.get() 则返?nbsp;null?br />
清单 4 不会出现q种问题Q因 //1 重新创徏了对象ƈ为其指定了一个强引用。因此,如果垃圾攉器在该行之后Q但在行 //2 之前Q运行,该对象就不会被回收。然后,?nbsp;//2 创建对 obj ?nbsp;WeakReference。在使用q个 if 块之后的 obj 之后Q您应该?nbsp;obj 讄?nbsp;nullQ从而让垃圾攉器能够回收这个对象以充分利用弱引用。清?nbsp;5 昄了一个完整的E序Q它展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是很有用的。然而,׃它们所依赖的垃圾收集器行ؓ有时候无法预知,所以其实用性就会受到媄响。能否有效地使用它们q取决于是否应用了正的~程风格Q关键在于您要理解这些类是如何实现的以及如何对它们进行编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何情况下都不会被回收? 软可?Softly Reachable)
定义:~An object is softly reachable if it is not strongly reachable but can be reached by traversing a soft reference.~
含义?当对象不处于强可辄? q且可以通过软引用进行访问时, 卛_于Y可达状?
当程序申请内存的时? 垃圾攉器会判断是否开始回收处于Y可达状态的对象, 如果军_回收某个对象, 那么垃圾攉器会清除所有指向该对象的Y引用, 如果M处于其它软可辄态的对象可以通过强引用访问该对象, 那么指向q些对象的Y引用也会被清除掉. 垃圾攉器在军_哪些软可辄态的对象被收集时, 采用"最久未被?原则, 或称"最不常使用"原则. 垃圾攉器也保证在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.~
垃圾攉器会一ơ清除所有弱引用. 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
]]> SELECT C.Name
, E.NameLast
, E.NameFirst
, E.Number
, ISNULL(I.Description,'NA') AS Description
FROM tblCompany AS C
JOIN tblEmployee AS E
ON C.CompanyID = E.CompanyID
LEFT JOIN tblCoverage AS V
ON E.EmployeeID = V.EmployeeID
LEFT JOIN tblInsurance AS I
ON V.InsuranceID = I.InsuranceID
WHERE C.Name LIKE @Name
AND V.CreateDate > CONVERT(smalldatetime,
'01/01/2000')
ORDER BY C.Name
, E.NameLast
, E.NameFirst
, E.Number
, ISNULL(I.Description,'NA')
SELECT @Retain = @@ERROR, @Rows = @@ROWCOUNT
IF @Status = 0 SET @Status = @Retain
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
]]>
]]>
l l 数据库回滚段是否_Q?
l l 是否需要徏立ORACLE数据库烦引、聚集、散列?
l l pȝ全局区(SGAQ大是否够?
l l SQL语句是否高效Q?
2?、数据仓库系l(Data WarehousingQ,q种信息pȝ的主要Q务是从ORACLE的v量数据中q行查询Q得到数据之间的某些规律。数据库理员需要ؓq种cd的ORACLE数据库着重考虑下述参数Q?
l l 是否采用B*-索引或者bitmap索引Q?
l l 是否采用q行SQL查询以提高查询效率?
l l 是否采用PL/SQL函数~写存储q程Q?
l l 有必要的话,需要徏立ƈ行数据库提高数据库的查询效率
SQL语句的调整原?
SQL语言是一U灵zȝ语言Q相同的功能可以使用不同的语句来实现Q但是语句的执行效率是很不相同的。程序员可以使用EXPLAIN PLAN语句来比较各U实现方案,q出最优的实现Ҏ。d来讲Q程序员写SQL语句需要满虑如下规则Q?
1?、尽量用烦引。试比较下面两条SQL语句Q?
语句AQSELECT dname, deptno FROM dept WHERE deptno NOT IN
(SELECT deptno FROM emp);
语句BQSELECT dname, deptno FROM dept WHERE NOT EXISTS
(SELECT deptno FROM emp WHERE dept.deptno = emp.deptno);
q两条查询语句实现的l果是相同的Q但是执行语句A的时候,ORACLE会对整个emp表进行扫描,没有使用建立在emp表上的deptno索引Q执行语句B的时候,׃在子查询中用了联合查询QORACLE只是对emp表进行的部分数据扫描Qƈ利用了deptno列的索引Q所以语句B的效率要比语句A的效率高一些?
2?、选择联合查询的联合次序。考虑下面的例子:
SELECT stuff FROM taba a, tabb b, tabc c
WHERE a.acol between :alow and :ahigh
AND b.bcol between :blow and :bhigh
AND c.ccol between :clow and :chigh
AND a.key1 = b.key1
AMD a.key2 = c.key2;
q个SQL例子中,E序员首先需要选择要查询的主表Q因Z表要q行整个表数据的扫描Q所以主表应该数据量最,所以例子中表A的acol列的范围应该比表B和表C相应列的范围?
3?、在子查询中慎重使用IN或者NOT IN语句Q用where (NOT) exists的效果要好的多?
4?、慎重用视囄联合查询Q尤其是比较复杂的视图之间的联合查询。一般对视图的查询最好都分解为对数据表的直接查询效果要好一些?
5?、可以在参数文g中设|SHARED_POOL_RESERVED_SIZE参数Q这个参数在SGA׃n池中保留一个连l的内存I间Q连l的内存I间有益于存攑֤的SQLE序包?
6?、ORACLE公司提供的DBMS_SHARED_POOLE序可以帮助E序员将某些l常使用的存储过E?#8220;?#8221;在SQLZ而不被换出内存,E序员对于经怋用ƈ且占用内存很多的存储q程“?#8221;到内存中有利于提高最l用L响应旉?
CPU参数的调?
CPU是服务器的一w要资源,服务器良好的工作状态是在工作高峰时CPU的用率?0Q以上。如果空闲时间CPU使用率就?0Q以上,说明服务器缺乏CPU资源Q如果工作高峰时CPU使用率仍然很低,说明服务器CPU资源q比较富余?
使用操作相同命o可以看到CPU的用情况,一般UNIX操作pȝ的服务器Q可以用sar –u命o查看CPU的用率QNT操作pȝ的服务器Q可以用NT的性能理器来查看CPU的用率?
数据库管理员可以通过查看v$sysstat数据字典?#8220;CPU used by this session”l计得知ORACLE数据库用的CPU旉Q查?#8220;OS User level CPU time”l计得知操作系l用h下的CPU旉Q查?#8220;OS System call CPU time”l计得知操作系l系l态下的CPU旉Q操作系lȝCPU旉是用户态和pȝ态时间之和,如果ORACLE数据库用的CPU旉占操作系lȝCPU旉90Q以上,说明服务器CPU基本上被ORACLE数据库用着Q这是合理,反之Q说明服务器CPU被其它程序占用过多,ORACLE数据库无法得到更多的CPU旉?
数据库管理员q可以通过查看v$sesstat数据字典来获得当前连接ORACLE数据库各个会话占用的CPU旉Q从而得知什么会话耗用服务器CPU比较多?
出现CPU资源不的情冉|很多的:SQL语句的重解析、低效率的SQL语句、锁冲突都会引vCPU资源不?
1、数据库理员可以执行下q语句来查看SQL语句的解析情况:
SELECT * FROM V$SYSSTAT
WHERE NAME IN
('parse time cpu', 'parse time elapsed', 'parse count (hard)');
q里parse time cpu是系l服务时_parse time elapsed是响应时_用户{待旉
waite time = parse time elapsed – parse time cpu
由此可以得到用户SQL语句q_解析{待旉Qwaite time / parse count。这个^均等待时间应该接q于0Q如果^均解析等待时间过长,数据库管理员可以通过下述语句
SELECT SQL_TEXT, PARSE_CALLS, EXECUTIONS FROM V$SQLAREA
ORDER BY PARSE_CALLS;
来发现是什么SQL语句解析效率比较低。程序员可以优化q些语句Q或者增加ORACLE参数SESSION_CACHED_CURSORS的倹{?
2、数据库理员还可以通过下述语句Q?
SELECT BUFFER_GETS, EXECUTIONS, SQL_TEXT FROM V$SQLAREA;
查看低效率的SQL语句Q优化这些语句也有助于提高CPU的利用率?
3?、数据库理员可以通过v$system_event数据字典中的“latch free”l计Ҏ看ORACLE数据库的冲突情况Q如果没有冲H的话,latch free查询出来没有l果。如果冲H太大的话,数据库管理员可以降低spin_count参数|来消除高的CPU使用率?
内存参数的调?
内存参数的调整主要是指ORACLE数据库的pȝ全局区(SGAQ的调整。SGA主要׃部分构成Q共享池、数据缓冲区、日志缓冲区?
1?1?׃n池由两部分构成:׃nSQL区和数据字典~冲区,׃nSQL区是存放用户SQL命o的区域,数据字典~冲区存放数据库q行的动态信息。数据库理员通过执行下述语句Q?
select (sum(pins - reloads)) / sum(pins) "Lib Cache" from v$librarycache;
来查看共享SQL区的使用率。这个用率应该?0Q以上,否则需要增加共享池的大。数据库理员还可以执行下述语句Q?
select (sum(gets - getmisses - usage - fixed)) / sum(gets) "Row Cache" from v$rowcache;
查看数据字典~冲区的使用率,q个使用率也应该?0Q以上,否则需要增加共享池的大?
2?2?数据~冲区。数据库理员可以通过下述语句Q?
SELECT name, value FROM v$sysstat WHERE name IN ('db block gets', 'consistent gets','physical reads');
来查看数据库数据~冲区的使用情况。查询出来的l果可以计算出来数据~冲区的使用命中率=1 - ( physical reads / (db block gets + consistent gets) )?
q个命中率应该在90Q以上,否则需要增加数据缓冲区的大?
3?3?日志~冲区。数据库理员可以通过执行下述语句Q?
select name,value from v$sysstat where name in ('redo entries','redo log space requests');查看日志~冲区的使用情况。查询出的结果可以计出日志~冲区的甌p|率:
甌p|率=requests/entriesQ申请失败率应该接近?Q否则说明日志缓冲区开讑֤,需要增加ORACLE数据库的日志~冲区?/span>
]]>
?
什么是查询优化E序Q?
查询优化对于关系数据库的性能Q特别是对于执行复杂SQL 语句的性能而言臛_重要。查询优化程序确定执行每一ơ查询的最佳策略?
例如Q查询优化程序选择对于指定的查询是否用烦引,以及在联接多个表旉用哪一U联接技术。这cd{对SQL 语句的执行性能有很大的影响Q查询优化对于每一U?span class="t_tag" onclick="tagshow(event)" href="tag.php?name=%D3%A6%D3%C3">应用E序都是关键技术,应用E序涉及的范围从操作pȝ到数据仓库,从分?span class="t_tag" onclick="tagshow(event)" href="tag.php?name=%CF%B5%CD%B3">pȝ到内?span class="t_tag" onclick="tagshow(event)" href="tag.php?name=%B9%DC%C0%ED">理pȝ。查询优化程序对?span class="t_tag" onclick="tagshow(event)" href="tag.php?name=%D3%A6%D3%C3">应用E序和最l用h完全透明的?
׃应用E序可能生成非常复杂的SQL 语句, 查询优化E序必须_ֿ构徏、功能强大,以保障良好的执行性能。例如,查询优化E序可{换SQL 语句Q复杂的语句{换成为等L但执行性能更好的SQL 语句。查询优化程序的典型特征是基于开销。在Z开销的优化策略中Q对于给定查询生成多个执行计划,然后Ҏ个计划估开销。查询优化程序选用估算开销最低的计划?
Oracle 在查询优化方面提供了什么?
Oracle 的优化程序可U是业界最成功的优化程序。基于开销的优化程序自1992 q随Oracle7 推出后,通过10 q的丰富的实际用L验,不断得到提高和改q。好的查询优化程序不是基于纯_的理论假设及谓词在实验室中开发出来的Q而是通过适合实际用户需求开发和合出来的?br />
Oracle 的查询优化程序比M其他查询优化E序在数据库应用E序的应用都要多Q而且Oracle 的优化程序一直由于实际应用的反馈而得到改q?
Oracle 的优化程序包? 大主要部分(本文在以下章节详细讨论q些部分Q:
SQL 语句转换Q在查询优化中Oracle 使用一pd_深技术对SQL 语句q行转换。查询优化的q一步骤的目的是原有的SQL 语句转换成ؓ语义相同而处理效率更高的SQL 语句?
执行计划选择Q对于每个SQL 语句, 优化E序选择一个执行计划(可用Oracle 的EXPLAIN PLAN 工具或通过Oracle ?#8220;v$sql_plan” 视图查看Q。执行计划描qC执行SQL 时的所有步骤,如访问表的顺序;如何这些表联接在一P以及是否通过索引来访问这些表。优化程序ؓ每个SQL 语句设计许多可能的执行计划,q出最好的一个?
开销模型与统计:Oracle 的优化程序依赖于执行SQL 语句的所有单个操作的开销估算。想要优化程序能选出最好的执行计划Q需要最好的开销估算Ҏ。开销估算需要详l了解某些知识,q些知识包括Q明白每个查询所需的I/O、CPU ?span class="t_tag" onclick="tagshow(event)" href="tag.php?name=%C4%DA%B4%E6">内存资源以及数据库对象相关的l计信息Q表、烦引和物化视图Q,q有有关g服务?/span>q_的性能信息。收集这些统计和性能信息的过E应高效q且高度自动化?
动态运行时间优化:q不是SQL 执行的每个方面都可以事先q行优化。Oracle 因此要根据当前数据库负蝲Ҏ询处理策略进行动态调整。该动态优化的目标是获得优化的执行性能Q即使每个查询可能不能够获得理想的CPU 或内存资源。Oracle 另有一个原来的优化E序Q即Z规则的优化程序。该优化E序仅向后兼容,在Oracle 的下个版本将不再得到支持。绝大多数Oracle 用户目前使用Z开销的优化程序。所有主要的应用E序供应商(如Oracle 应用E序、SAP 和PeoplesoftQ仅列出q几Ӟ以及大量q来开发的客户应用E序都用基于开销的优化程序来获得优良的执行性能Q故本文仅讲q基于开销的优化程序?
SQL 语句转换
使用SQL 语句表示复杂查询可以有多U方式。提交到数据库的SQL 语句cd通常是最l用h应用E序可以最单的方式生成的SQL cd。但是这些h工编写或机器生成的查询公式不一定是执行查询最高效的SQL 语句。例如,由应用程序生成的查询通常含有一些无关紧要的条gQ这些条件可以去掉。或者,有些从某查询谓词出的附加条g应当d到该SQL 语句中。SQL 转换语句的目的是给定的SQL 语句转换成语义相同(卌回相同结果的SQL 语句Qƈ且性能更好的SQL 语句?
所有的q些转换对应用程序及最l用户完全透明。SQL 语句转换在查询优化过E中自动实现?
Oracle 实现了多USQL 语句转换。这些{换大概可分成两类Q?
试探查询转换Q在可能的情况下对进来的SQL 语句都会q行q种转换。这U{换能够提供相同或较好的查询性能Q所以Oracle 知道实施q种转换不会降低执行性能?Z开销的查询{换:Oracle 使用Z开销的方法进行几cL询{换。借助q种ҎQ{换后的查询会与原查询相比较,然后Oracle 的优化程序从中选出最x行策略?
以下部分讨论Oracle 转换技术的几个CZ。这些示例ƈ非是权威的,仅用于帮助读者理解该关键转换技术及其益处?
试探查询转换
单视囑q?
可能最单的查询转换是视囑q。对于包含视囄查询Q通常可以通过把视囑֮义与查询“合ƈ”来将视图从查询中L。例如,L下面的非常简单的视图及查询?
CREATE VIEW TEST_VIEW AS SELECT ENAME, DNAME, SAL FROM EMP E, DEPT D WHERE E.DEPTNO = D.DEPTNO;
SELECT ENAME, DNAME FROM TEST_VIEW WHERE SAL > 10000;
如果不加M转换Q处理该查询的唯一Ҏ是将EMP 的所有行联接到DEPT 表的所有行Q然后筛选有适当的SAL 的值的那些行?
如果使用视图合ƈQ上q查询可以{换ؓQ?
SELECT ENAME, DNAME FROM EMP E, DEPT D WHERE E.DEPTNO = D.DEPTNO AND E.SAL > 10000;
处理该{换后的查询时Q可以在联接EMP 和DEPT 表前使用谓词‘SAL>10000’。这一转换׃减少了联接的数据量而大大提高了查询的执行性能。即便在q样一个非常简单的CZ里,查询转换的益处和重要性也显而易见?
复杂视图合ƈ
许多视图合ƈ操作都是直截了当的,如以上示例。但是,较复杂的视图Q如包含GROUP BY 或DISTINCT 操作W的视图合ƈh׃那么Ҏ了。Oracle 为合q这cd杂视图提供了一些高U技术?
L以下带有GROUP BY 语句的视图。在该示例中Q视图计每个部门的q_工资?
CREATE VIEW AVG_SAL_VIEW AS SELECT DEPTNO, AVG(SAL) AVG_SAL_DEPT FROM EMP GROUP BY DEPTNO
查询的目的是要找出Oakland 每个部门的^均工资:
SELECT DEPT.NAME, AVG_SAL_DEPT FROM DEPT, AVG_SAL_VIEW WHERE DEPT.DEPTNO = AVG_SAL_VIEW.DEPTNO AND DEPT.LOC = 'OAKLAND'
可以转换为:
SELECT DEPT.NAME, AVG(SAL) FROM DEPT, EMP WHERE DEPT.DEPTNO = EMP.DEPTNO AND DEPT.LOC = 'OAKLAND' GROUP BY DEPT.ROWID, DEPT.NAME
该特D{换的执行性能优点立即昄Q该转换把EMP 数据在分l聚合前q行联接和筛选,而不是在联接前将EMP 表的所有数据分l聚合?
子查?#8220;展^”
Oracle 有一些{换能不同类型的子查询{变ؓ联接、半联接或反联接。作领域内的技术示例,我们来看下面q个查询Q找出有工资过10000 的员工的那些部门Q?
SELECT D.DNAME FROM DEPT D WHERE D.DEPTNO IN (SELECT E.DEPTNO FROM EMP E WHERE E.SAL > 10000)
存在一pd可以优化本查询的执行计划。Oracle 会考虑q些可能的不同{换,Z开销选出最佌划?
如果不进行Q何{换,q一查询的执行计划如下:
OPERATION OBJECT_NAME OPTIONS
SELECT STATEMENT
FILTER
TABLE ACCESS DEPT FULL
TABLE ACCESS EMP FULL
按照该执行计划,扫描DEPT 表的每一行查找所有满_查询条g的EMP 记录。通常Q这不是一U高效的执行{略。然而,查询转换可以实现效率更高的计划?
该查询的可能计划之一是将查询作ؓ“半联?#8221;来执行?#8220;半联?#8221;是一U特D类型的联接Q它消除了联接中来自内表的冗余|q实际上是该子查询的原本的语义Q。在该示例中Q优化程序选择了一个散列半联接Q尽Oracle 也支持排?合ƈ以及嵌套-循环半联接:
OPERATION OBJECT_NAME OPTIONS
SELECT STATEMENT
HASH JOIN SEMI
TABLE ACCESS DEPT FULL
TABLE ACCESS EMP FULL
׃SQL 没有用于半联接的直接语法Q此转换q的查询不能使用标准的SQL 来表C。但是,转换后的伪SQL 是Q?br />
SELECT DNAME FROM EMP E, DEPT D WHERE D.DEPTNO E.DEPTNO AND E.SAL > 10000;
另一个可能的计划是优化程序可以决定将DEPT 表作接的内表。在q种情况下,查询作ؓ通常的联接来执行Q但对EMP 表进行特别排序,以消除冗余的部门P
OPERATION OBJECT_NAME OPTIONS
SELECT STATEMENT
HASH JOIN
SORT UNIQUE
TABLE ACCESS EMP FULL
TABLE ACCESS DEPT FULL
转换后的SQL 语句为:
SELECT D.DNAME FROM (SELECT DISTINCT DEPTNO FROM EMP) E, DEPT D WHERE E.DEPTNO = D.DEPTNO AND E.SAL > 10000;
与视囑q一P子查询展q也是获得良好查询执行性能的基本优化办法?
传递谓词生?
在某些查询中Q由于表间的联接关系Q一个表中的谓词可以转化为另一个表中的谓词。Oracle 会以q种方式演绎出新的谓词,q类谓词被称Z递谓词。例如,来看一个查询,扑և定货当天q出的所有商品:
SELECT COUNT(DISTINCT O_ORDERKEY) FROM ORDER, LINEITEM WHERE O_ORDERKEY = L_ORDERKEY AND O_ORDERDATE = L_SHIPDATE AND O_ORDERDATE BETWEEN '1-JAN-2002' AND '31-JAN-2002'
利用传递性,该ORDER 表中的谓词也可以用于LINEITEM 表:
SELECT COUNT(DISTINCT O_ORDERKEY) FROM ORDER, LINEITEM WHERE
]]>
(注:你对事情的看法,是不是也反映Z内心真正的态度Q)
2、晚饭后Q母亲和奛_一块儿z碗盘,父亲和儿子在客厅看电视。突Ӟ厨房里传来打破盘子的响声Q然后一片沉寂。是儿子望着他父Ԍ说道Q「一定是妈妈打破的。」「你怎么知道Q」「她没有骂h。?
Q注Q我们习惯以不同的标准来看h看己Q以致往往是责Z严,待己以宽。)
3、有两个台湾观光团到日本伊豆半岛旅游Q\况很坏,到处都是坑洞。其中一位导游连声抱歉,说\面简直像d一栗说而另一个导游却诗意盎然地对游客_怽先生奛_Q我们现在走的这条道路,正是赫赫有名的伊豆迷人酒H大道。?
Q注Q虽是同L情况Q然而不同的意念Q就会生不同的态度。思想是何{奇妙的事,如何LQ决定权在你。)
4、同h学三年U的学生Q在作文中说他们来的志愿是当小丑。中国的老师斥之为:「胸无大志,孺子不可教也Q」而外国的老师则会_「愿你把Ƣ笑带给全世界!?
Q注Qn为长辈的我们Q不但容易要求多于鼓励,更狭H的界定了成功的定义。)
5、在故宫博物院中Q有一个太太不耐烦地对她先生说Q「我说你为甚么走得这么慢。原来你老是停下来看q些东西。?
Q注Q有人只知道在h生的道\上狂奔,l果失去了观看两旁美丽花늚Z。)
6、妻子正在厨房炒菜。丈夫在Ҏ边一直唠叨不停:慢些。小心!火太大了。赶快把鱼翻q来。快铲v来,Ҏ太多了!把豆腐整q一下!「哎呀Q」妻子脱口而出Q「我懂得怎样炒菜。」「你当然懂,太太Q」丈夫^静地{道Q「我只是要让你知道,我在开车时Q你在旁边喋喋不休,我的感觉如何。?
Q注Q学会体谅他人ƈ不困难,只要你愿意认真地站在Ҏ的角度和立场看问题。)
7、一辆蝲满乘客的公共汽R沿着下坡路快速前q着Q有一个h後面紧紧地追赶着q辆车子。一个乘客从车窗中出头来对qR子的Q?#8220;老兄Q算啦,你追不上的!”“我必追上它Q?#8221;qh气喘吁吁地说Q?#8220;我是q辆车的司机Q?#8221;
(注:有些人必非常认真努力,因ؓ不这L话,後果十分悲惨了Q然而也正因为必d力以_潜在的本能和不ؓ人知的特质终充份展现出来。)
8、甲Q「新搬来的邻居好可恶Q昨天晚上三更半夜、夜׃h静之时然跑来猛按我家的门铃。?
乙:「的可Ӟ你有没有马上报警Q?
Ԍ「没有。我当他们是疯子Ql吹我的喇叭。?
Q事出必有因Q如果能先看到自q不是Q答案就会不一样在你面对冲H和争执Ӟ先想一x否心中有亏,或许很快p释怀了)
9、某日,张三在山间小路开车,正当他悠哉地ƣ赏丽风景ӞH然q面开来一辆货车,而且满囗黑牙的司摇下H户对他大骂一壎ͼ“猪!”
张三想纳P也越惌气,於是他也摇下车窗回头大骂Q?#8220;你才是猪Q?#8221;
才刚骂完Q他便迎头撞上一过马\的猪?
Q不要错误的诠释别h的好意,那只会让自己吃亏Qƈ且别h受i。在不明所以之前,先学会按捺情l,耐心观察Q以免事後生发悔意。)
10、小男孩问爸爸:“是不是做父亲的L做儿子的知道得多Q?#8221;
爸爸回答Q?#8220;当然啦!”
男孩问Q?#8220;늁是谁发明的?”
爸爸Q?#8220;是爱q生?#8221;
男孩又问:“那爱q生的爸爸怎麽没有发明늁Q?#8221;
Q很奇怪,喜欢倚老卖老的人,特别Ҏ栽跟斗。权威往往只是一个经不v考验的空壛_Q尤其在Cq个多元开攄时代。)
11、小明洗澡时不小心吞下一块肥皂Q他的妈妈慌慌张张地打电话向家庭ȝ求助。医生说Q?#8220;我现在还有几个病人在Q可能要半小时後才能赶过厅R?#8221;
明妈妈_“在你来之前,我该做甚麽?”
ȝ_“l小明喝一杯白开_然後用力跳一跻I你就可以让小明用嘴巴Ҏ泡消时间了?#8221;
Qtake it easyQ放L放轻松些Q生zM必太紧张Q事情既然已l发生了Q何不坦然自在的面对。担心不如宽心,IL张不如穷开心。)
12、一把坚实的大锁挂在大门上,一栚w杆费了九牛二虎之力,q是无法它撬开。钥匙来了,他瘦的w子钻进锁孔Q只轻轻一转,大锁?#8220;?#8221;C声打开了?
铁杆奇怪地问:“Z麽我费了那麽大力气也打不开Q而你却轻而易丑֜把它打开了呢Q?#8221;
钥匙_“因ؓ我最了解他的心?#8221;
如果你家附近有一安厅,东西又贵又难吃,桌上q爬着蟑螂Q你会因为它很近很方便,׃而再、再而三地光临吗Q?/span>
回答Q你一定会_q是什么烂问题Q谁那么W,花钱买罪受?
可同L情况换个场合Q自己或许就做类似的蠢事。不男女都曄抱怨过他们的情人或配偶品性不端,三心二意Q不负责仅R明知在一h什么好的结果,怨恨已经比爱q多Q但?/span>“不知道ؓ什?/span>”q是要和他搅和下去,分不了手。说I了Q只是ؓ了不甘,Z习惯Q这不也和光临餐厅一P
―?span style="font-family: 宋体">做hQؓ什么要q于执著Q!
问题?/span>
如果你不心丢掉100块钱Q只知道它好像丢在某个你走过的地方,你会?/span>200块钱的R费去把那100块找回来吗?
回答Q一个超U愚蠢的问题?/span>
可是Q相似的事情却在人生中不断发生。做错了一件事Q明知自己有问题Q却M不肯认错Q反而花加倍的旉来找藉口Q让别h对自q印象大打折扣。被人骂了一句话Q却׃无数旉难过Q道理相同。ؓ一件事情发火,不惜损h不利Ԍ不惜血本,不惜旉Q只为报复,不也一h聊?
失去一个h的感情,明知一切已无法挽回Q却q是那么伤心Q而且一伤心是好几q_q要借酒愁QŞ销骨立。其实这样一点用也没有,只是损失更多?/span>
―?span style="font-family: 宋体">做hQ干吗ؓ难自己?Q?/span>
问题?/span>
你会因ؓ打开报纸发现每天都有车祸Q就不敢出门吗?
回答Q这是个什么烂问题Q当然不会,那叫因噎废食?/span>
然而,有不h却曾_现在的离婚率那么高,让我都不敢谈恋爱了。说得还挺理所当然。也有不女人看到有关的诸多报道Q就对自q另一半忧心忡忡,q不也是cM的反应?
所谓乐观,是得相信:虽然道\多艰险,我还是那个会q_q马路的人,只要我小心一点,不必x过马\?/span>
―?span style="font-family: 宋体">做hQ先要相信自己?/span>
问题?/span>
你相信每个h随便都可以成功立业吗Q?/span>
回答Q当然不会相信?/span>
但据观察Q有人L在听完成功h士绞脑汁的Q比如说Q多MQ多l习之后Q问了另一个问题?那不是很难?
我们都想?/span>3分钟内学好英文,?/span>5分钟内解x有难题,N成功是那么容易的吗?改变当然是难的。成功只因不怕困难,所以才能出cL萃?/span>
有一ơ坐在出UR上,听见司机看到自己前后都是高档车,兀自感叹:“唉,Z么别人那么有钱,我的p么难赚?” 我心血来潮Q问他:“你认Z上有什么钱是好赚的Q?/span>”他答不出来,q了半晌才说Q好像都是别人的钱比较好赚?/span>
其实M一个成功者都是艰辛取得。我们实在不该抱怨命q?/span>
―?span style="font-family: 宋体">做hQ依靠自己!
问题?/span>
你认为完全没有打q篮球的人,可以当很好的球教练吗?
回答Q当然不可能Q外行不可能领导内行?/span>
可是Q有许多人,Ҏ个行业完全不了解Q只听到那个行业好赚钱,马上开起业来了?/span>
我看q对I着没有M口味、或Ҏ不在乎穿着的hQ梦惛_是开间服装店Q不知道电脑怎么开机的人,却想在网上赚钱,l果道听途说Q却不反省自己是否专业能力不I只抱怨时不我与?/span>
―?span style="font-family: 宋体">做hQ量力而行?/span>
问题?/span>
怼但不相同的问题:你是否认为,球教练不上球场,闭着眼睛也可以主g场完的利?
回答Q有病啊Q当然是不可能的?/span>
可是却有不少朋友Q自己没有时间打理,却拼命投资去开咖啡馆,开厅Q开自己Ҏ不懂的公司,火烧一h着把辛苦积攒的U蓄花掉Q去当一个稀里糊涂的投资人。亏的L比赚的多Q却觉得自己是因气不好,而不是想法出了问题?/span>
―?span style="font-family: 宋体">做hQ记得反省自己?/span>
问题?/span>
你宁可永q后悔,也不愿意试一试自p否{败ؓ胜?
解答Q恐怕没有h会说Q?/span>“对,我就是这L孬种”吧?/span>
然而,我们却常常在不该打退堂鼓时拼命打退堂鼓Qؓ了恐惧失败而不敢尝试成功?/span>
以关颖珊赢得2000q世界花h冰冠军时的精彩表Cؓ例:她一心想赢得W一名,然而在最后一场比赛前Q她的ȝ分只排名W三位,在最后的自选曲目上,奚w择了突_而不是少出错。在4分钟的长曲中Q结合了最高难度的三周跻Iq且q大胆地q蟩了两ơ。她也可能会败得很难看,但是Ҏ竟成功了?/span>
她说Q?/span>“因ؓ我不想等到失败,才后悔自p有潜力没发挥?/span>”
一个中国伟人曾_胜利的希望和有利情况的恢复,往往产生于再坚持一下的努力之中?/span>
―?span style="font-family: 宋体">做hQ何妨放手一搏?/span>
问题?/span>
你的旉无限Q长生不老,所以最惛_的事Q应该无限g期?
回答Q不Q傻瓜才会这栯为?/span>
然而我们却常说Q等我老了Q要ȝ怸界;{我退休,pd惛_的事情;{孩子长大了Q我可?/span>……
我们都以己有无限的时间与_֊。其实我们可以一步一步实现理惻I不必在等待中徒耗生命。如果现在就能一步一步努力接q,我们׃会活了半生,却出现自己最不想看到的结局?/span>
―?span style="font-family: 宋体">做hQ要zd当下?/span>
]]>
做ؓ一名大四的学生,我面试过不少的单位,有成功的也有p|的,但是Ҏ来说所有的p|在某U意义上都是一U成功,特别是我下面写的q些Q写q篇文章的时候,我已l签了南京的一家Y件公司,但是惌v今年2?1日我面试苏州TW的IT公司的经历联惛_我们现在学习~程的一些情冉|真的深有感触Q这ơ面试我深q体会Cp|但也收获了很多。我要说的将分成三部分,1.是我面试的具体经q?.是由面试惛_?.C我应该做的。当然这些话很大E度上是我个人的意见Q不可能完全得到大家的赞同,所以在某些观点上如果哪位朋友觉得跟我的有很大出入,请不要介意,也不要对我攻击,当我没有说q,Ƣ迎和我联系共同探讨q些问题Q我的E-Mail:fvifnmmmm@126.com
1.面试l过
大约在年前我接到了TW瑞晟(Realtek)苏州公司的面试通知Q通知??1日到苏州工业园区面试Q接到面试后的几天我把一些专业课温习了一遍,特别是C++和数据结构,׃大学几年里,我一直专研这些方面,加上通过了高U程序员的考试Q对于一些常用的法我差不多也达C烂熟于胸的地步,当时的感觉是如果问了我这些方面的问题我应该是没有问题的!
21日那天我被安排在4Q?0面试Q由一位技术h员单独给我面试,在问了一些简单的问题之后他给我出了一道编E题目,题目是这LQ?
(׃具体面试的题目比较烦琐,我将其核心思想提取出来分解成了两个独立的简单的问题Q有可能问题分解的不当,请大家见谅,实际面试了一个的问题但比其复杂很多,而且涉及一些高{数学变?
1) 写一个函数计当参数为n(n很大)时的?1-2+3-4+5-6+7......+n
|我的心里L一壎ͼ没想到这么简单,我有点紧张的心情时放松hQ?
于是很快我给出我的解法:
long fn(long n)
{
long temp=0;
int i,flag=1;
if(n<=0)
{
printf("error: n must > 0);
exit(1);
}
for(i=1;i<=n;i++)
{
temp=temp+flag*i;
flag=(-1)*flag;
}
return temp;
}
搞定Q当我用期待的目光看着面试官的时候,他微W着跟我_执行l果肯定是没有问题!但当n很大的时候我q个E序执行效率很低Q?在嵌入式pȝ的开发中Q程序的q行效率很重要,能让CPU执行一条指令都是好的,他让我看看这个程序还有什么可以修改的地方Q把E序优化一下!听了q些话,我的心情当时变的有点沉重Q没惛_他的要求很严|之后我对E序q行了严格的分析Q给Z改进了的ҎQ?nbsp;
long fn(long n)
{
long temp=0;
int j=1,i=1,flag=1;
if(n<=0)
{
printf("error: n must > 0);
exit(1);
}
while(j<=n)
{
temp=temp+i;
i=-i;
i>0?i++:i--;
j++;
}
return temp;
}
虽然我不敢保证我q个法是最优的Q但是比起上一个程序,我将所有涉及到乘法指o的语句改为执行加法指令,既达到要题目的要求而且q算旉上羃短了很多Q而代价仅仅是增加了一个整型变量!但是我现在的信心已经受了一Ҏ击,我将信将疑的看者面试官Q他q是微笑着跟我_“不错Q这个程序确实在效率上有的很大的提高Q?#8221;我心里一阉|喜!但他接着说这个程序仍然不能达C的要求,要我l出更优的方案!天啊Q还有优化!我当时真的有点崩溃了Q想了一会后Q我h他给Z的方案!然后他很爽快的给Z他的E序Q?
long fn(long n)
{
if(n<=0)
{
printf("error: n must > 0);
exit(1);
}
if(0==n%2)
return (n/2)*(-1);
else
return (n/2)*(-1)+n;
}
搞笑Q当时我目瞪口呆Q没惛_他是q个意思,q么单的代码我真的不会写吗,但是我ؓ什么没有往那方面上惛_Q他说的没有错,在n很大很大的时候这三个E序q行旉的差别简直是天壤之别Q当我刚惛_口说点什么的时候,他却先开口了Q?#8220;不要认ؓCPUq算速度快就把所有的问题都推l它dQ程序员应该代码优化再优化Q我们自p做的决不要让CPU做,因ؓCPU是ؓ用户服务的,不是为我们程序员服务的!”多么_辟的语aQ我已经不想再说什么了Q接着是第二个问题Q?nbsp;
2),他要求我用一U技巧性的~程Ҏ来用一个函数实C个函数的功能n为如Q?
fn1(n)=n/2!+n/3!+n/4!+n/5!+n/6!
fn2(n)=n/5!+n/6!+n/7!+n/8!+n/9!
现在用一个函数fn(int n,int flag)实现Q当flag?Ӟ实现fn1功能Q如果flag?时实现fn2功能Q?
他的要求q是效率Q效率,效率Q说实在话,如果我心情好的话我应该能l出一U比较好的算法,但我那时真的没有什么心思再想了Q我在纸上胡q了一些诸?!=6*5!的公式后直截了当的跟他说要他l出他的{案Q面试官也没有说什么,l出了他的思\Q?
定义一个二l数l?float t[2][5]存入[2!,3!,4!,5!,6!},{5!,6!,7!,8!,9!]然后l出一个@环:
for(i=0;i<6;i++)
{
temp=temp+n/t[flag];
}
最后得到计|呵呵Q典型的I间换时间的法Q?
q些d׃50分钟的时_q有十分钟我p他很随意的聊聊天Q聊了一些编E以及生zȝ问题Q那时的我已l很放松了,因ؓ我知道这ơ面试结果只有一个:p|?:30的时候面试官要我{通知Q于是我d了他们公司。这是面试的整个经q!
.由面试想到的
真的是很p|啊!我记得那天下好大的雨Q气温也很低Q我边走ҎQ从5:30一直走?:30Q全w都湉K了Q又冷又饿,但是我只是一直走Q脑子里面充满了疑惑Q我也想让雨把自己淋醒!看到q里有些朋友可能觉得那些面试题目不算什么如果让自己做的话肯定能全部{对Q我肯定怿你,因ؓ我从未怀疑过中国E序员的能力Q我认ؓ中国有世界上最好的E序员,我也从未认ؓ自己是高手,所以我做不出来不代表中国程序员比TW或者别的地方的E序员差Q所以我׃我的角度Q我的所见所x谈一些感惻I
不错全世界都有优U的程序员Q中国也不例外,但是我疑惑的是:到底中国和TW或者国外的优秀的程序员的比例到底是多少QTW我不知道Q中?00个程序员里有几个是优U的呢?我根本算不上Q从上面的表现就以说明一切了Q是1个?5个?10个?50个?q个数字我不敢ؕ猜,恐遭|友一痛骂,那么我们国内有多h学习计算机呢Q拿我们学校来说Q计机97U?个班Q?8U?个班Q?9U?0个班Q?000U?7个班Qh多了Q老师怎么办?我们学校的做法是让研I生上课Q然后呢Q补考一抓一大把Q大把大把的补考费落入了学校的口袋Q还说现在的学生素质低!真是好笑Q我都不知道学校q么做是Z什么,为国内培d量的E序员吗Q学生们能真正学到计机知识吗?好了Q我敢讲Q在我们学校学习~程学生和优UE序?注意我指的是优秀Q只会编几个p烂E序的h不?的比例应该是100:0.1 。在q种比例下虽然我们中国学习编E的人铺天盖圎ͼ但是x有多个真正Z国Y件业发展作出贡献Q有多少真正写出优秀的程序名扬v外!
我从学习~程以来Q不是自学q是老师指导Q从来都是解决问题就好,~出E序来就行,我的疑惑是:我们有真正的q程序的效率Q程序的质量吗?我们有仔l分析过我们写的东西Q看看有没有可以改进的地方,看看有没有简单的Ҏ来达到同L目的呢?我问心自问,我发玎ͼ我从来没有对我写出来的程序进行过优化Q最多就是进行详l的试Q然后DebugQ但是这p够了吗?q些天我偶尔发现我曾l写q的一个游戏,那是一q前我刚加入www.vcroad.net做ؓ其中一员时候,感觉应该拿点东西出来Q然后花了一个星期的旉写出来的Q程序不复杂,但是用到了不数据结构的东西Q也用到了一些精彩的法Q加上windows的界面和游戏的可玩性,写完后受C不少好评Q我当时真的很佩服自己!但是现在看呢Q没有一句注释,好多丑陋的函数名比如Qvoid chushihua()Q好多没有必要的变量Q可以用单语句完成工作的我用华丽的法Q大量用全局变量.....,说不好听的话Q六癑֤行的E序除了能运行之外就是一陀屎!如果一q前我能听到一些反面意见的话,大概我能早一点觉悟,但是自从原代码在|站发布以来听到的都是赞之词,没有一个h向我提出E序改进的意见,q又说明了一个什么问题呢Q很值得思考啊Q?
q有一个疑惑是Q我们说的和做的真的一样吗Q我在学校的时候曾l受学院指派承办q一个计机大赛Q请了一个老师出决赛的题目Q主要是一些算法题目,q个老师可能是我上大学以来唯一敬佩的老师了,从程序调试到打分Q对于每个程序都仔细分析其时间效率和I间效率Q然后综合打分,四十个h的卷子,老师从下午三点一直调试到晚上十点Q在有些写的_ֽ的语句后q加上批注。我真是高兴很遇到这L老师q且和他做深入的交流Q但在事后,却发生了一件不愉快的事Q在比赛中获得第二名的学生找到我Q说他程序全部调试成功应该给他满分,q且应该得第一Q我说不q他Q最后调Z他的原程序和W一名的原程序对比,不错Q两个程序都q行的很好,q时Q那个同学开口了Q?#8220;我的E序写的十分h了,仅仅数行完成了题目要求Q而他的却写了一大堆Qؓ什么给他的分多q给我的分?#8221;我当时很是气愤,如果不是老师负责的话Q那么现在第一名和W二名的位置真的要互调了Q拜托,不是E序的行数越程序的质量p高,我记得我跟他大谈q方面的道理Q最后说服他了!哈哈Q但是我Q只能说说而已Q我不知道还有多h一P说v来头头是道,但心里却压根׃未重视过它!
.我打做的!
其实那天我想到的q不止上面那么多Q但是我不想再说了,因ؓ我猜想看q篇文章的网友大概都有一肚子的感惻I一肚子的抱怨,借用q篇文章发泄可不是我惌到的目的Q在上面我把自己骂的一文不g不是妄自菲薄Q但是在某些斚w我真的做错了Q或者说是偏M正确方向Q现在是矫正方向和重整旗鼓的时候了Q就象我前面说过的,我相信中国有世界上最好的E序员,我也怿我的水^不会一直保持现Ӟ我现在就收拾L骚真正的实干hQ?nbsp;
真的很yQ就写到q里的时候我在网上偶发Cq篇手册Q我不知道这预示着什么,但是我想如果我照下面q个基本原则一直踏实做下去Q我一定会实现我的理想---一名优U的Y件设计师!
Q下面这些文字不是我的原创,是我偶尔在网上发现的Q我真的很幸q能看到q些Q这文章也随着下面的文字而结束,我真心的希望您能从这文章中得到启发,q篇文章Ƣ迎大家随意转蝲Q您可以不写作者是谁,但是h写上www.vcroad.net原创Q谢谢您的支持)
作者:金蝶中间件公司CTO袁红?nbsp;
不知不觉做Y件已l做了十q_有成功的喜悦Q也有失败的痛苦Q但M敢称自己是高手,因ؓ和我心目中真正的高手们比hQ还差的太远。世界上q没有成为高手的捷径Q但一些基本原则是可以遵@的?nbsp;
1. 扎实的基。数据结构、离散数学、编译原理,q些是所有计机U学的基Q如果不掌握他们Q很隑ֆ出高水^的程序。据我的观察Q学计算Z业的人比学其他专业的人更能写出高质量的Y件。程序h人都会写Q但当你发现写到一定程度很隑ֆ提高的时候,应该想x不是要回q头来学学这些最基本的理论。不要一开始就dOOPQ即使你再精通OOPQ遇C些基本算法的时候可能也会束手无{?nbsp;
2. 丰富的想象力。不要拘泥于固定的思维方式Q遇到问题的时候要多想几种解决问题的方案,试试别h从没惌的方法。丰富的惌力是建立在丰富的知识的基上,除计机以外Q多涉猎其他的学U,比如天文、物理、数学等{。另外,多看U电媄也是一个很好的途径?nbsp;
3. 最单的是最好的。这也许是所有科学都遵@的一条准则,如此复杂的质能互换原理在爱因斯坦眼里不过是一个简单得不能再简单的公式QE=mc2。简单的Ҏ更容易被人理解,更容易实玎ͼ也更Ҏl护。遇到问题时要优先考虑最单的ҎQ只有简单方案不能满求时再考虑复杂的方案?nbsp;
4. 不钻牛角。当你遇到障的时候,不妨暂时q离电脑Q看看窗外的风景Q听听轻音乐Q和朋友聊聊天。当我遇到难题的时候会ȝ游戏Q而且是那U极暴力的打斗类游戏Q当负责游戏的那部分大脑l胞极度亢奋的时候,负责~程的那部分大脑l胞得C充分的休息。当重新开始工作的时候,我会发现那些N现在竟然可以q刃而解?nbsp;
5. 对答案的渴求。hc自然科学的发展史就是一个求得到答案的q程Q即使只能知道答案的一部分也值得我们M出。只要你坚定信念Q一定要扑ֈ问题的答案,你才会付出精力去探烦Q即使最后没有得到答案,在过E中你也会学到很多东ѝ?nbsp;
6. 多与别h交流。三必有我师Q也许在一ơ和别h不经意的谈话中,可以迸出灵感的火花。多上上|,看看别h对同一问题的看法,会给你很大的启发?nbsp;
7. 良好的编E风根{注意养成良好的习惯Q代码的~进~排Q变量的命名规则要始l保持一致。大安知道如何排除代码中错误,却往往忽视了对注释的排错。注释是E序的一个重要组成部分,它可以你的代码更容易理解,而如果代码已l清楚地表达了你的思想Q就不必再加注释了,如果注释和代码不一_那就更加p糕?nbsp;
8. 韧性和毅力。这也许?高手"和一般程序员最大的区别。A good programming is 99 weat and 1?ffee。高手们q不是天才,他们是在无数个日日夜夜中练出来的。成功能l我们带来无比的喜悦Q但q程却是无比的枯燥乏呟뀂你不妨做个试Q找?0000以内的素数表Q把它们全都抄下来,然后再检查三遍,如果能够不间断地完成q一工作Q你可以满一条?/p>
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过三个步骤来完成:装蝲、链接和初始化,其中链接又可以分成校验、准备和解析三步Q除了解析外Q其它步骤是严格按照序完成的,各个步骤的主要工作如下:
装蝲Q查扑֒导入cL接口的二q制数据Q?
链接Q执行下面的校验、准备和解析步骤Q其中解析步骤是可以选择的;
校验Q检查导入类或接口的二进制数据的正确性;
准备Q给cȝ静态变量分配ƈ初始化存储空_
解析Q将W号引用转成直接引用Q?
初始化:Ȁzȝ的静态变量的初始化Java代码和静态Java代码块?
至于在类装蝲和虚拟机启动的过E中的具体细节和可能会抛出的错误Q请参看《Java虚拟范》以及《深入Java虚拟机》,它们在网l上面的资源地址是:
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中的一些重要的ҎQ再l合上面所介绍的JVM中类装蝲的具体的q程Q对动态装载类q项技术就有了一个比较大概的掌握Q这些重要的Ҏ包括以下几个:
①loadCassҎ loadClass(String name ,boolean resolve)其中name参数指定了JVM需要的cȝ名称,该名UC包表C法表示,如Java.lang.ObjectQresolve参数告诉Ҏ是否需要解析类Q在初始化类之前,应考虑c解析,q不是所有的c都需要解析,如果JVM只需要知道该cL否存在或扑և该类的超c?那么׃需要解析。这个方法是ClassLoader 的入口点?
②defineClassҎ q个Ҏ接受cL件的字节数组q把它{换成Class对象。字节数l可以是从本地文件系l或|络装入的数据。它把字节码分析成运行时数据l构、校验有效性等{?
③findSystemClassҎ findSystemClassҎ从本地文件系l装入文件。它在本地文件系l中LcL?如果存在,׃用defineClass字节数l{换成Class对象,以将该文件{换成cR当q行Java应用E序?q是JVM 正常装入cȝ~省机制?
④resolveClassҎ resolveClass(Class c)Ҏ解析装入的类,如果该类已经被解析过那么不做处理。当调用loadClassҎ?通过它的resolve 参数军_是否要进行解析?
⑤findLoadedClassҎ 当调用loadClassҎ装入cL,调用findLoadedClass Ҏ来查看ClassLoader是否已装入这个类,如果已装?那么q回Class对象,否则q回NULL。如果强行装载已存在的类,会抛出链接错误?
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ؓ根装载器(bootstrap ClassLoader)。根装蝲器的Ҏ之处是它只能够装载在设计时刻已知的类,因此虚拟机假定由根装载器所装蝲的类都是安全的、可信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的类装蝲器:根装载器(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:
Java的类装蝲模型是一U代?delegation)模型。当JVM 要求c装载器CL(ClassLoader)装蝲一个类?CL首先这个类装蝲h转发l他的父装蝲器。只有当父装载器没有装蝲q无法装载这个类?CL才获得装载这个类的机会。这? 所有类装蝲器的代理关系构成了一U树状的关系。树的根是类的根装蝲?bootstrap ClassLoader) , 在JVM 中它?null"表示。除根装载器以外的类装蝲器有且仅有一个父装蝲器。在创徏一个装载器? 如果没有昑ּ地给出父装蝲? 那么JVM默认系l装载器为其父装载器。Java的基本类装蝲器代理结构如?所C:
下面针对各种c装载器分别q行详细的说明?
?Bootstrap) 装蝲?该装载器没有父装载器Q它是JVM实现的一部分Q从sun.boot.class.path装蝲q行时库的核心代码?
扩展(Extension) 装蝲?l承的父装蝲器ؓ根装载器Q不像根装蝲器可能与q行时的操作pȝ有关Q这个类装蝲器是用纯Java代码实现的,它从java.ext.dirs (扩展目录)中装载代码?
pȝ(System or Application) 装蝲?装蝲器ؓ扩展装蝲器,我们都知道在安装JDK的时候要讄环境变量(CLASSPATH )Q这个类装蝲器就是从java.class.path(CLASSPATH 环境变量)中装载代码的Q它也是用纯Java代码实现的,同时q是用户自定义类装蝲器的~省父装载器?
应用程?Applet) 装蝲? 装蝲器ؓ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装载器的信息。类Ҏ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来装载java.applet.AppletcRACL又再ơ按上面的顺序装载Appletc? l果ACL发现他的父装载器已经装蝲了这个类, 所以ACLq接将q个已经装蝲的类q回l了JVM , 完成了Appletcȝ装蝲。接下来,Appletcȝ类也一样处理。最? TestApplet及所有有关的c都装蝲CJVM中?
四、结?
cȝ动态装载机制是JVM的一Ҏ心技? 也是Ҏ被忽视而引起很多误解的地方。本文介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>