ffmmx's Blog

Happy coding

转 数据库SQL性能优化之详解

一、问题的提出

在应用系统开发初期,由于开发数据库数据比较少,对于查询SQL语句,复杂视图的的编写等体会不出SQL语句各种写法的性能优劣,但是如果将应用系统提交 实际应用后,随着数据库中数据的增加,系统的响应速度就成为目前系统需要解决的最主要的问题之一。系统优化中一个很重要的方面就是SQL语句的优化。对于 海量数据,劣质SQL语句和优质SQL语句之间的速度差别可以达到上百倍,可见对于一个系统不是简单地能实现其功能就可,而是要写出高质量的SQL语句, 提高系统的可用性。

在多数情况下,Oracle使用索引来更快地遍历表,优化器主要根据定义的索引来提高性能。但是,如果在SQL语句的where子句中写的SQL代码不合理,就会造成优化器删去索引而使用全表扫描,一般就这种SQL语句就是所谓的劣质SQL语句。在编写SQL语句时我们应清楚优化器根据何种原则来删除索引,这有助于写出高性能的SQL语句。

二、SQL语句编写注意问题

下面就某些SQL语句的where子句编写中需要注意的问题作详细介绍。在这些where子句中,即使某些列存在索引,但是由于编写了劣质的SQL,系统在运行该SQL语句时也不能使用该索引,而同样使用全表扫描,这就造成了响应速度的极大降低。

1. 操作符优化

(a) IN 操作符

用IN写出来的SQL的优点是比较容易写及清晰易懂,这比较适合现代软件开发的风格。但是用IN的SQL性能总是比较低的,从Oracle执行的步骤来分析用IN的SQL与不用IN的SQL有以下区别:

ORACLE试图将其转换成多个表的连接,如果转换不成功则先执行IN里面的子查询,再查询外层的表记录,如果转换成功则直接采用多个表的连接方式查询。 由此可见用IN的SQL至少多了一个转换的过程。一般的SQL都可以转换成功,但对于含有分组统计等方面的SQL就不能转换了。

推荐方案:在业务密集的SQL当中尽量不采用IN操作符,用EXISTS 方案代替。

(b) NOT IN操作符

此操作是强列不推荐使用的,因为它不能应用表的索引。

推荐方案:用NOT EXISTS 方案代替

(c) IS NULL 或IS NOT NULL操作(判断字段是否为空)

判断字段是否为空一般是不会应用索引的,因为索引是不索引空值的。不能用null作索引,任何包含null值的列都将不会被包含在索引中。即使索引有多列 这样的情况下,只要这些列中有一列含有null,该列就会从索引中排除。也就是说如果某列存在空值,即使对该列建索引也不会提高性能。任何在where子 句中使用is null或is not null的语句优化器是不允许使用索引的。

推荐方案:用其它相同功能的操作运算代替,如:a is not null 改为 a>0 或a>’’等。不允许字段为空,而用一个缺省值代替空值,如申请中状态字段不允许为空,缺省为申请。

(d) > 及 < 操作符(大于或小于操作符)

大于或小于操作符一般情况下是不用调整的,因为它有索引就会采用索引查找,但有的情况下可以对它进行优化,如一个表有100万记录,一个数值型字段 A,30万记录的A=0,30万记录的A=1,39万记录的A=2,1万记录的A=3。那么执行A>2与A>=3的效果就有很大的区别了,因 为A>2时ORACLE会先找出为2的记录索引再进行比较,而A>=3时ORACLE则直接找到=3的记录索引。

(e) LIKE操作符

LIKE操作符可以应用通配符查询,里面的通配符组合可能达到几乎是任意的查询,但是如果用得不好则会产生性能上的问题,如LIKE ‘%5400%’ 这种查询不会引用索引,而LIKE ‘X5400%’则会引用范围索引。

一个实际例子:用YW_YHJBQK表中营业编号后面的户标识号可来查询营业编号 YY_BH LIKE ‘%5400%’ 这个条件会产生全表扫描,如果改成YY_BH LIKE ’X5400%’ OR YY_BH LIKE ’B5400%’ 则会利用YY_BH的索引进行两个范围的查询,性能肯定大大提高。

带通配符(%)的like语句:

同样以上面的例子来看这种情况。目前的需求是这样的,要求在职工表中查询名字中包含cliton的人。可以采用如下的查询SQL语句:

select * from employee where last_name like '%cliton%';

这里由于通配符(%)在搜寻词首出现,所以Oracle系统不使用last_name的索引。在很多情况下可能无法避免这种情况,但是一定要心中有底,通 配符如此使用会降低查询速度。然而当通配符出现在字符串其他位置时,优化器就能利用索引。在下面的查询中索引得到了使用:

select * from employee where last_name like 'c%';

(f) UNION操作符

UNION在进行表链接后会筛选掉重复的记录,所以在表链接后会对所产生的结果集进行排序运算,删除重复的记录再返回结果。实际大部分应用中是不会产生重复的记录,最常见的是过程表与历史表UNION。如:
select * from gc_dfys
union
select * from ls_jg_dfys
这个SQL在运行时先取出两个表的结果,再用排序空间进行排序删除重复的记录,最后返回结果集,如果表数据量大的话可能会导致用磁盘进行排序。

推荐方案:采用UNION ALL操作符替代UNION,因为UNION ALL操作只是简单的将两个结果合并后就返回。

select * from gc_dfys
union all
select * from ls_jg_dfys

(g) 联接列

对于有联接的列,即使最后的联接值为一个静态值,优化器是不会使用索引的。我们一起来看一个例子,假定有一个职工表(employee),对于一个职工的 姓和名分成两列存放(FIRST_NAME和LAST_NAME),现在要查询一个叫比尔.克林顿(Bill Cliton)的职工。

下面是一个采用联接查询的SQL语句:

select * from employss where first_name||''||last_name ='Beill Cliton';

上面这条语句完全可以查询出是否有Bill Cliton这个员工,但是这里需要注意,系统优化器对基于last_name创建的索引没有使用。当采用下面这种SQL语句的编写,Oracle系统就可以采用基于last_name创建的索引。

*** where first_name ='Beill' and last_name ='Cliton';

(h) Order by语句

ORDER BY语句决定了Oracle如何将返回的查询结果排序。Order by语句对要排序的列没有什么特别的限制,也可以将函数加入列中(象联接或者附加等)。任何在Order by语句的非索引项或者有计算表达式都将降低查询速度。

仔细检查order by语句以找出非索引项或者表达式,它们会降低性能。解决这个问题的办法就是重写order by语句以使用索引,也可以为所使用的列建立另外一个索引,同时应绝对避免在order by子句中使用表达式。

(i) NOT

我们在查询时经常在where子句使用一些逻辑表达式,如大于、小于、等于以及不等于等等,也可以使用and(与)、or(或)以及not(非)。NOT可用来对任何逻辑运算符号取反。下面是一个NOT子句的例子:

... where not (status ='VALID')

如果要使用NOT,则应在取反的短语前面加上括号,并在短语前面加上NOT运算符。NOT运算符包含在另外一个逻辑运算符中,这就是不等于(<>)运算符。换句话说,即使不在查询where子句中显式地加入NOT词,NOT仍在运算符中,见下例:

... where status <>'INVALID';

对这个查询,可以改写为不使用NOT:

select * from employee where salary<3000 or salary>3000;

虽然这两种查询的结果一样,但是第二种查询方案会比第一种查询方案更快些。第二种查询允许Oracle对salary列使用索引,而第一种查询则不能使用索引。

2. SQL书写的影响

(a) 同一功能同一性能不同写法SQL的影响。

如一个SQL在A程序员写的为  Select * from zl_yhjbqk

B程序员写的为 Select * from dlyx.zl_yhjbqk(带表所有者的前缀)

C程序员写的为 Select * from DLYX.ZLYHJBQK(大写表名)

D程序员写的为 Select *  from DLYX.ZLYHJBQK(中间多了空格)

以上四个SQL在ORACLE分析整理之后产生的结果及执行的时间是一样的,但是从ORACLE共享内存SGA的原理,可以得出ORACLE对每个SQL 都会对其进行一次分析,并且占用共享内存,如果将SQL的字符串及格式写得完全相同,则ORACLE只会分析一次,共享内存也只会留下一次的分析结果,这 不仅可以减少分析SQL的时间,而且可以减少共享内存重复的信息,ORACLE也可以准确统计SQL的执行频率。

(b) WHERE后面的条件顺序影响

WHERE子句后面的条件顺序对大数据量表的查询会产生直接的影响。如:
Select * from zl_yhjbqk where dy_dj = '1KV以下' and xh_bz=1
Select * from zl_yhjbqk where xh_bz=1 and dy_dj = '1KV以下'
以上两个SQL中dy_dj(电压等级)及xh_bz(销户标志)两个字段都没进行索引,所以执行的时候都是全表扫描,第一条SQL的dy_dj = '1KV以下'条件在记录集内比率为99%,而xh_bz=1的比率只为0.5%,在进行第一条SQL的时候99%条记录都进行dy_dj及xh_bz的 比较,而在进行第二条SQL的时候0.5%条记录都进行dy_dj及xh_bz的比较,以此可以得出第二条SQL的CPU占用率明显比第一条低。

(c) 查询表顺序的影响

在FROM后面的表中的列表顺序会对SQL执行性能影响,在没有索引及ORACLE没有对表进行统计分析的情况下,ORACLE会按表出现的顺序进行链 接,由此可见表的顺序不对时会产生十分耗服物器资源的数据交叉。(注:如果对表进行了统计分析,ORACLE会自动先进小表的链接,再进行大表的链接)

3. SQL语句索引的利用

(a) 对条件字段的一些优化

采用函数处理的字段不能利用索引,如:

substr(hbs_bh,1,4)=’5400’,优化处理:hbs_bh like ‘5400%’

trunc(sk_rq)=trunc(sysdate), 优化处理:sk_rq>=trunc(sysdate) and sk_rq<trunc(sysdate+1)

进行了显式或隐式的运算的字段不能进行索引,如:ss_df+20>50,优化处理:ss_df>30

‘X’ || hbs_bh>’X5400021452’,优化处理:hbs_bh>’5400021542’

sk_rq+5=sysdate,优化处理:sk_rq=sysdate-5

hbs_bh=5401002554,优化处理:hbs_bh=’ 5401002554’,注:此条件对hbs_bh 进行隐式的to_number转换,因为hbs_bh字段是字符型。

条件内包括了多个本表的字段运算时不能进行索引,如:

ys_df>cx_df,无法进行优化
qc_bh || kh_bh=’5400250000’,优化处理:qc_bh=’5400’ and kh_bh=’250000’

4. 更多方面SQL优化资料分享

(1) 选择最有效率的表名顺序(只在基于规则的优化器中有效):

ORACLE 的解析器按照从右到左的顺序处理FROM子句中的表名,FROM子句中写在最后的表(基础表 driving table)将被最先处理,在FROM子句中包含多个表的情况下,你必须选择记录条数最少的表作为基础表。如果有3个以上的表连接查询, 那就需要选择交叉表(intersection table)作为基础表, 交叉表是指那个被其他表所引用的表.

(2) WHERE子句中的连接顺序:

ORACLE采用自下而上的顺序解析WHERE子句,根据这个原理,表之间的连接必须写在其他WHERE条件之前, 那些可以过滤掉最大数量记录的条件必须写在WHERE子句的末尾.

(3) SELECT子句中避免使用 ‘ * ‘:

ORACLE在解析的过程中, 会将'*' 依次转换成所有的列名, 这个工作是通过查询数据字典完成的, 这意味着将耗费更多的时间。

(4) 减少访问数据库的次数:

ORACLE在内部执行了许多工作: 解析SQL语句, 估算索引的利用率, 绑定变量 , 读数据块等。

(5) 在SQL*Plus , SQL*Forms和Pro*C中重新设置ARRAYSIZE参数, 可以增加每次数据库访问的检索数据量 ,建议值为200。

(6) 使用DECODE函数来减少处理时间:

使用DECODE函数可以避免重复扫描相同记录或重复连接相同的表.

(7) 整合简单,无关联的数据库访问:

如果你有几个简单的数据库查询语句,你可以把它们整合到一个查询中(即使它们之间没有关系) 。

(8) 删除重复记录:

最高效的删除重复记录方法 ( 因为使用了ROWID)例子:
DELETE  FROM  EMP E  WHERE  E.ROWID > (SELECT MIN(X.ROWID) FROM  EMP X  WHERE  X.EMP_NO = E.EMP_NO)。

(9) 用TRUNCATE替代DELETE:

当删除表中的记录时,在通常情况下, 回滚段(rollback segments ) 用来存放可以被恢复的信息. 如果你没有COMMIT事务,ORACLE会将数据恢复到删除之前的状态(准确地说是恢复到执行删除命令之前的状况) 而当运用TRUNCATE时, 回滚段不再存放任何可被恢复的信息.当命令运行后,数据不能被恢复.因此很少的资源被调用,执行时间也会很短. (译者按: TRUNCATE只在删除全表适用,TRUNCATE是DDL不是DML) 。

(10) 尽量多使用COMMIT:

只要有可能,在程序中尽量多使用COMMIT, 这样程序的性能得到提高,需求也会因为COMMIT所释放的资源而减少,COMMIT所释放的资源:
a. 回滚段上用于恢复数据的信息.
b. 被程序语句获得的锁
c. redo log buffer 中的空间
d. ORACLE为管理上述3种资源中的内部花费

(11) 用Where子句替换HAVING子句:

避免使用HAVING子句, HAVING 只会在检索出所有记录之后才对结果集进行过滤. 这个处理需要排序,总计等操作. 如果能通过WHERE子句限制记录的数目,那就能减少这方面的开销. (非oracle中)on、where、having这三个都可以加条件的子句中,on是最先执行,where次之,having最后,因为on是先把不 符合条件的记录过滤后才进行统计,它就可以减少中间运算要处理的数据,按理说应该速度是最快的,where也应该比having快点的,因为它过滤数据后 才进行sum,在两个表联接时才用on的,所以在一个表的时候,就剩下where跟having比较了。在这单表查询统计的情况下,如果要过滤的条件没有 涉及到要计算字段,那它们的结果是一样的,只是where可以使用rushmore技术,而having就不能,在速度上后者要慢如果要涉及到计算的字 段,就表示在没计算之前,这个字段的值是不确定的,根据上篇写的工作流程,where的作用时间是在计算之前就完成的,而having就是在计算后才起作 用的,所以在这种情况下,两者的结果会不同。在多表联接查询时,on比where更早起作用。系统首先根据各个表之间的联接条件,把多个表合成一个临时表 后,再由where进行过滤,然后再计算,计算完后再由having进行过滤。由此可见,要想过滤条件起到正确的作用,首先要明白这个条件应该在什么时候 起作用,然后再决定放在那里。

(12) 减少对表的查询:

在含有子查询的SQL语句中,要特别注意减少对表的查询.例子:
SELECT  TAB_NAME FROM TABLES WHERE (TAB_NAME,DB_VER) = ( SELECT TAB_NAME,DB_VER FROM  TAB_COLUMNS  WHERE  VERSION = 604)

(13) 通过内部函数提高SQL效率:

复杂的SQL往往牺牲了执行效率. 能够掌握上面的运用函数解决问题的方法在实际工作中是非常有意义的。

(14) 使用表的别名(Alias):

当在SQL语句中连接多个表时, 请使用表的别名并把别名前缀于每个Column上.这样一来,就可以减少解析的时间并减少那些由Column歧义引起的语法错误。

(15) 用EXISTS替代IN、用NOT EXISTS替代NOT IN:

在许多基于基础表的查询中,为了满足一个条件,往往需要对另一个表进行联接.在这种情况下, 使用EXISTS(或NOT EXISTS)通常将提高查询的效率. 在子查询中,NOT IN子句将执行一个内部的排序和合并. 无论在哪种情况下,NOT IN都是最低效的 (因为它对子查询中的表执行了一个全表遍历). 为了避免使用NOT IN ,我们可以把它改写成外连接(Outer Joins)或NOT EXISTS。
例子:
(高效)SELECT * FROM  EMP (基础表)  WHERE  EMPNO > 0  AND  EXISTS (SELECT ‘X'  FROM DEPT  WHERE  DEPT.DEPTNO = EMP.DEPTNO  AND  LOC = ‘MELB')
(低效)SELECT  * FROM  EMP (基础表)  WHERE  EMPNO > 0  AND  DEPTNO IN(SELECT DEPTNO  FROM  DEPT  WHERE  LOC = ‘MELB')

(16) 识别'低效执行'的SQL语句:

虽然目前各种关于SQL优化的图形化工具层出不穷,但是写出自己的SQL工具来解决问题始终是一个最好的方法:
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;

(17) 用索引提高效率:

索引是表的一个概念部分,用来提高检索数据的效率,ORACLE使用了一个复杂的自平衡B-tree结构. 通常,通过索引查询数据比全表扫描要快. 当ORACLE找出执行查询和Update语句的最佳路径时, ORACLE优化器将使用索引. 同样在联结多个表时使用索引也可以提高效率. 另一个使用索引的好处是,它提供了主键(primary key)的唯一性验证.。那些LONG或LONG RAW数据类型, 你可以索引几乎所有的列. 通常, 在大型表中使用索引特别有效. 当然,你也会发现, 在扫描小表时,使用索引同样能提高效率. 虽然使用索引能得到查询效率的提高,但是我们也必须注意到它的代价. 索引需要空间来存储,也需要定期维护, 每当有记录在表中增减或索引列被修改时, 索引本身也会被修改. 这意味着每条记录的INSERT , DELETE , UPDATE将为此多付出4 , 5 次的磁盘I/O . 因为索引需要额外的存储空间和处理,那些不必要的索引反而会使查询反应时间变慢.。定期的重构索引是有必要的:
ALTER  INDEX <INDEXNAME> REBUILD <TABLESPACENAME>

(18) 用EXISTS替换DISTINCT:

当提交一个包含一对多表信息(比如部门表和雇员表)的查询时,避免在SELECT子句中使用DISTINCT. 一般可以考虑用EXIST替换, EXISTS 使查询更为迅速,因为RDBMS核心模块将在子查询的条件一旦满足后,立刻返回结果. 例子:
(低效):
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);

(19) sql语句用大写的;因为oracle总是先解析sql语句,把小写的字母转换成大写的再执行。

(20) 在java代码中尽量少用连接符“+”连接字符串!

(21) 避免在索引列上使用NOT,通常我们要避免在索引列上使用NOT, NOT会产生在和在索引列上使用函数相同的影响. 当ORACLE”遇到”NOT,他就会停止使用索引转而执行全表扫描。

(22) 避免在索引列上使用计算
WHERE子句中,如果索引列是函数的一部分.优化器将不使用索引而使用全表扫描.举例:
低效:
SELECT … FROM  DEPT  WHERE SAL * 12 > 25000;
高效:
SELECT … FROM DEPT WHERE SAL > 25000/12;

(23) 用>=替代>
高效:
SELECT * FROM  EMP  WHERE  DEPTNO >=4
低效:
SELECT * FROM EMP WHERE DEPTNO >3
两者的区别在于, 前者DBMS将直接跳到第一个DEPT等于4的记录而后者将首先定位到DEPTNO=3的记录并且向前扫描到第一个DEPT大于3的记录。

(24) 用UNION替换OR (适用于索引列)

通常情况下, 用UNION替换WHERE子句中的OR将会起到较好的效果. 对索引列使用OR将造成全表扫描. 注意, 以上规则只针对多个索引列有效. 如果有column没有被索引, 查询效率可能会因为你没有选择OR而降低. 在下面的例子中, LOC_ID 和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”
如果你坚持要用OR, 那就需要返回记录最少的索引列写在最前面.

(25) 用IN来替换OR

这是一条简单易记的规则,但是实际的执行效果还须检验,在ORACLE8i下,两者的执行路径似乎是相同的.
低效:
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);

(26) 避免在索引列上使用IS NULL和IS NOT NULL

避免在索引中使用任何可以为空的列,ORACLE将无法使用该索引.对于单列索引,如果列包含空值,索引中将不存在此记录. 对于复合索引,如果每个列都为空,索引中同样不存在此记录. 如果至少有一个列不为空,则记录存在于索引中.举例: 如果唯一性索引建立在表的A列和B列上, 并且表中存在一条记录的A,B值为(123,null) , ORACLE将不接受下一条具有相同A,B值(123,null)的记录(插入). 然而如果所有的索引列都为空,ORACLE将认为整个键值为空而空不等于空. 因此你可以插入1000 条具有相同键值的记录,当然它们都是空! 因为空值不存在于索引列中,所以WHERE子句中对索引列进行空值比较将使ORACLE停用该索引.
低效: (索引失效)
SELECT … FROM  DEPARTMENT  WHERE  DEPT_CODE IS NOT NULL;
高效: (索引有效)
SELECT … FROM  DEPARTMENT  WHERE  DEPT_CODE >=0;

(27) 总是使用索引的第一个列:

如果索引是建立在多个列上, 只有在它的第一个列(leading column)被where子句引用时,优化器才会选择使用该索引. 这也是一条简单而重要的规则,当仅引用索引的第二个列时,优化器使用了全表扫描而忽略了索引。

(28) 用UNION-ALL 替换UNION ( 如果有可能的话):

当SQL 语句需要UNION两个查询结果集合时,这两个结果集合会以UNION-ALL的方式被合并, 然后在输出最终结果前进行排序. 如果用UNION ALL替代UNION, 这样排序就不是必要了. 效率就会因此得到提高. 需要注意的是,UNION ALL 将重复输出两个结果集合中相同记录. 因此各位还是要从业务需求分析使用UNION ALL的可行性. UNION 将对结果集合排序,这个操作会使用到SORT_AREA_SIZE这块内存. 对于这块内存的优化也是相当重要的. 下面的SQL可以用来查询排序的消耗量
低效:
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'

(29) 用WHERE替代ORDER BY:

ORDER BY 子句只在两种严格的条件下使用索引.
ORDER BY中所有的列必须包含在相同的索引中并保持在索引中的排列顺序.
ORDER BY中所有的列必须定义为非空.
WHERE子句使用的索引和ORDER BY子句中所使用的索引不能并列.
例如:
表DEPT包含以下列:
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

(30) 避免改变索引列的类型:

当比较不同数据类型的数据时, ORACLE自动对列进行简单的类型转换.
假设 EMPNO是一个数值类型的索引列.
SELECT …  FROM EMP  WHERE  EMPNO = ‘123'
实际上,经过ORACLE类型转换, 语句转化为:
SELECT …  FROM EMP  WHERE  EMPNO = TO_NUMBER(‘123')
幸运的是,类型转换没有发生在索引列上,索引的用途没有被改变.
现在,假设EMP_TYPE是一个字符类型的索引列.
SELECT …  FROM EMP  WHERE EMP_TYPE = 123
这个语句被ORACLE转换为:
SELECT …  FROM EMP  WHERE TO_NUMBER(EMP_TYPE)=123
因为内部发生的类型转换, 这个索引将不会被用到! 为了避免ORACLE对你的SQL进行隐式的类型转换, 最好把类型转换用显式表现出来. 注意当字符和数值比较时, ORACLE会优先转换数值类型到字符类型。

分析select   emp_name   form   employee   where   salary   >   3000   在此语句中若salary是Float类型的,则优化器对其进行优化为Convert(float,3000),因为3000是个整数,我们应在编程时使 用3000.0而不要等运行时让DBMS进行转化。同样字符和整型数据的转换。


(31) 需要当心的WHERE子句:

某些SELECT 语句中的WHERE子句不使用索引. 这里有一些例子.
在下面的例子里, (1)‘!=' 将不使用索引. 记住, 索引只能告诉你什么存在于表中, 而不能告诉你什么不存在于表中. (2) ‘ ¦ ¦'是字符连接函数. 就象其他函数那样, 停用了索引. (3) ‘+'是数学函数. 就象其他数学函数那样, 停用了索引. (4)相同的索引列不能互相比较,这将会启用全表扫描.

(32) a. 如果检索数据量超过30%的表中记录数.使用索引将没有显著的效率提高. b. 在特定情况下, 使用索引也许会比全表扫描慢, 但这是同一个数量级上的区别. 而通常情况下,使用索引比全表扫描要块几倍乃至几千倍!

(33) 避免使用耗费资源的操作:

带有DISTINCT,UNION,MINUS,INTERSECT,ORDER BY的SQL语句会启动SQL引擎执行耗费资源的排序(SORT)功能. DISTINCT需要一次排序操作, 而其他的至少需要执行两次排序. 通常, 带有UNION, MINUS , INTERSECT的SQL语句都可以用其他方式重写. 如果你的数据库的SORT_AREA_SIZE调配得好, 使用UNION , MINUS, INTERSECT也是可以考虑的, 毕竟它们的可读性很强。

(34) 优化GROUP BY:

提高GROUP BY 语句的效率, 可以通过将不需要的记录在GROUP BY 之前过滤掉.下面两个查询返回相同结果但第二个明显就快了许多.
低效:
SELECT JOB , AVG(SAL)
FROM EMP
GROUP by JOB
HAVING JOB = ‘PRESIDENT'
OR JOB = ‘MANAGER'
高效:
SELECT JOB , AVG(SAL)
FROM EMP
WHERE JOB = ‘PRESIDENT'
OR JOB = ‘MANAGER'
GROUP by JOB

 

[蛋疼][备忘]皮尔士定律

什么是皮尔士定律? 皮尔斯定律就是指的"如果你能使得P蕴含Q,强制P为真,那么P就必定为真". 数学符号表达式: ((P→Q)→P)→P .

 

[蛋疼][备忘] pynepomukindexer 简单封装,手动的文件索引.

其实没啥什么好说的,我kde环境里面的nepomukfilewatch 服务进程启动会crash,暂时没有找到错误的原因,这样会造成文件改动以后触发的somethingChange 信号无法被正确的捕捉,并且调用对应索引程序来重新丢该资源进行处理.所以只要弄个手动的,暂时应急一下.

pynepomukindexer.py

#!/usr/bin/env python2
#coding:utf8
#filename:pynepomukindexer.py
#description:一个支持多文件和文件夹递归的nepomukindexer的封装.

'''
一个支持多文件和文件夹递归的nepomukindexer文件索引的封装.
'''
import os
import sys
from PyKDE4.nepomuk import Nepomuk


class PyNepomukIndexer(object):
    def __init__(self,currentPath):
        self.currentPath = currentPath
        self.indexerCmd = 'nepomukindexer'

    def index(self,path):
        if os.path.isdir(path):
            for f in [ os.path.realpath(path)+'/'+tf for tf in os.listdir(path) if tf[0] != '.' ]:
                self.index(f)
        else:
            print path
            os.system(self.indexerCmd+" '"+path+"'")


if __name__ == '__main__':
    pyindexer = PyNepomukIndexer(os.path.realpath('.'))
    
    if len(sys.argv) == 1:
        pyindexer.index(os.path.realpath('.'))
    else:
        for file in sys.argv[1:]:
            pyindexer.index(file)

[笔记]pykde的nepomuk 资源操作以及标签和评分建立.

pykde 允许对nepomuk的操作是十分有限的(实际上通过dbus的方式来走还要好些), 下面是一个用pykde 创建一个NEPOMUK资源的非常简单的例子.我在pykde的api中没有找到创建索引的相关的函数,以至于即便是有了对应的资源,可以在dolphin里面完全展示,但是通过nepomuk.query去搜索还是找不到资源信息.

如果有知道的朋友,希望分享一下.谢谢.

nepomuk资源创建和设置代码:

#!/usr/bin/env python2
# coding:utf8
# filename:nepomuk_test.py
# descption:a example for nepomuk


import sys
from PyQt4 import QtCore
from PyKDE4 import kdecore
from PyKDE4 import kdeui
from PyKDE4.nepomuk import Nepomuk


dummy_file = open('fummy.txt','w')
dummy_file.write('测试\n')
dummy_file.close()

result = Nepomuk.ResourceManager.instance().init() # create resoucemanager and connect to nepomuk resource database ,return 0 is ok,not is error.
if result != 0:
    sys.exit(result)

file_info = QtCore.QFileInfo("fummy.txt")
absolute_path = file_info.absoluteFilePath()
resource = Nepomuk.Resource(kdecore.KUrl(absolute_path)) # create or load a resource 

tagText = "tagtest"
tag = Nepomuk.Tag(tagText)
tag.setLabel(tagText)

resource.addTag(tag) # add tag
resource.setDescription("This is an example comment.") # set description
resource.setRating(4); # set rating

print "resource.exists() == ",resource.exists()
print "resource.isVaid() == ",resource.isValid()
print "resource.identifiers().length == ", len(resource.identifiers())
print "resource.type() == ",resource.type()
print "resource.types() == ",resource.types()
print "resource.resourceUri() == ",resource.resourceUri()
print "resource.resourceType () == ",resource.resourceType() 
print "resource.identifierUri() == ",resource.identifierUri()

vimperator ffmmx的插件屋

最近在用vimperator,被它的可以完全自定义扩展特性吸引了,再加上现在的ff支持很多HTML5的新功能,基本上可以通过JAVASCRIPT代码做到很多以前做不到的事情,比如文件系统操作。但是默认提供的功能还是很有限,所以为了方面还是需要自己扩展一些功能或者插件(这里说的这些东西只是给自己做一个备忘,没有什么特别的含义

插件项目地址https://github.com/firefoxmmx2/vimperatorPlugins ,使用了github,速度比较快。

虽然vimperator 的; extend hint 已经提供很多功能,例如复制匹配的链接地址等功能,但是目前还没有 触发鼠标事件的功能,所以我自己写了一个作为补充。

文件 simlateMouseEvents.js

代码:

//
// simulate mouse events
// 
//
(function(){
    let self=liberator.plugins.simulateMouseEvents=(function(){

        var fireMouseEvt=function(elem,evtString){
            var doc=content.document.wrappedJSObject;

            if(doc.createEvent){
                var evt=doc.createEvent('MouseEvents');
                evt.initEvent(evtString,true,false);
                elem.dispatchEvent(evt);
            }
            else if(doc.createEventObject){
                elem.fireEvent('on'+evtString);
            }
        };

        var PUBLICS={
            mouseover:function(elem){
                if(elem){
                    fireMouseEvt(elem,'mouseover');
                    elem.setAttribute('isMouseover',true);
                }
                else{
                    var activeElement=content.document.wrappedJSObject.activeElement;
                    fireMouseEvt(activeElement,'mouseover');
                    activeElement.setAttribute('isMouseover',true);
                }
                
                    
            },
            mouseout:function(elem){
                if(elem){
                    fireMouseEvt(elem,'mouseout');
                    elem.setAttribute('isMouseover',false);
                }
                else{
                    var activeElement=content.document.wrappedJSObject.activeElement;
                    fireMouseEvt(activeElement,'mouseout');

                    activeElement.setAttribute('isMouseover',false);
 
                }
            }
        };

        //registe extend hint command like ;m
        hints.addMode(
                'm',
                'active element trigge mouseover/mouseout',
                function(node){
                    if(node.getAttribute('isMouseover') && node.getAttribute('isMouseover') == 'true'){
                        self.mouseout(node);
                    }
                    else{
                        self.mouseover(node);
                        //全局的上一个触发过over事件的元素
                        if(self.overElem && self.overElem!=node){
                            try{
                                self.overElem.setAttribute('isMouseover',false);
                            }
                            catch(e){}
                        }
                        self.overElem=node;
                    }


        });
        return PUBLICS;
    })();
})();

实现输入法的共享词库

linux下的输入法大多没有提供像 搜狗输入法和QQ输入法之类的用户词库功能,可能也是没有必要吧。但是,他可以有。这里以FCITX为例简单的说一下这个的实现。

首先你需要一个基于文件系统的网络同步工具比如DROPBOX,金山快盘什么的。实际上你就是用过这个来储存你的个人词库信息。这里用DROPBOX举例

先备份你的输入法配置,cp ~/.config/fcitx ~/.config/fcitx-bak

然后移动你的输入法文件夹到 ~/dropbox/下面,在通过符号链接链接到以前的配置目录 ln -s ~/dropbox/fcitx ~/.config/fctix

这样走到哪里你的输入法设置都会同步。

 

java 使用tomcat服务器解决文件下载乱码

文件下载乱码原因主要是服务器URI编码和文件名字符串编码不一致,或者没有把响应头信息里面的附件属性的文件名使用URLCODE进行编码。

解决这个问题,需要使用 org.apache.catalina.util.URLEncoder,对文件名进行处理:

org.apache.catalina.util.URLEncoder encoder = new org.apache.catalina.util.URLEncoder();
			
String filename = encoder.encode(“附件文件名”);
response.setContentType(attachment.getAttachmentContentType());
response.addHeader("Content-Disposition",
					"attachment; filename="+filename
					);

这样下载文件就不会乱码了。另外一种方法是在TOMCAT配置文件里面配置URICODE=你页面编码 这样也可以阻止乱码

云计算,网格计算,分布式计算,集群计算的区别?

 

其实如果从应用的有状态和无状态两个分类去看,这些分布式技术都是分别提升有态应用和无态应用的性能和可扩展性Scalable。

应用架构设计的三个类型中,我也谈到了:并发是解决有态应用;并行是解决无态应用。

而集群和网格计算属于一种并发计算,主要解决有态应用;而云计算则是解决有态和无态两种。

过去我们都是围绕数据库编程,企业应用大部分都是数据库系统,数据库为核心的系统大部分是有状态应用,因为数据库保存的是数据,这个数据大部分是结果状态数据,比如交易后的数量和金额,发票的数量和金额,单据的状态等等。

企业中数据能够使用传统数据库装载存储,说明其数据量不是很大,但是随着互联网普及,特别是facebook等这些拥有几亿用户数据的系统诞生,大数据Big Data诞生,大数据和海量数据差不多,但是好像应该再多些。

大数据诞生后,传统有态并发方案已经失效,脱离数据状态进行裸奔的并行计算得到重视,因为数据太大,干脆抛弃它们,从函数编程这个思维去重新看待大数据计算,这样云计算的新特性:并行计算,如Hadoop等诞生,并成为云计算区别于传统集群的重要区别。

当然,云计算不只是解决并行计算,也提供并发计算能力,是以前分布式技术的一种总括,所以,刚开始有人怀疑云计算是旧瓶装新酒,这是没有深刻理解云计算的原因所在。

oracle 备忘录

oracle备忘录 主要用于记录一些不太常用的 oracle 命令或者操作。自己的记性越来越不好了。

正文.............

获取当前错误信息代码:sqlcode

获取当前错误信息内容:sqlerrm

JVM调优的陷阱1【转】

原帖地址: http://hllvm.group.iteye.com/group/topic/27945 作者:RednaxelaFX

开个帖大家来讨论下自己遇到过的情况吧?我在顶楼举几个例子。

开这帖的目的是想让大家了解到,所谓“标准参数”是件很微妙的事情。确实有许多前辈经过多年开发积累下了许多有用的调优经验,但向他们问“标准参数”并照单全收是件危险的事情。

前辈们提供的“标准参数”或许适用于他们的应用场景,他们或许也知道这些参数里隐含的陷阱;但听众却不一定知道各种参数背后的缘由。

原则上说,在生产环境使用非标准参数(这里指的是在各JDK/JRE实现特有的、相互之间不通用的参数)应该尽量避免。这些参数与具体实现密切相 关,不是光了解很抽象的“JVM原理”就足以理解的;即便在同一系列的JDK/JRE实现中,非标准参数也不保证在各版本间有一样的作用;而且许多人只看 名字就猜想参数的左右,做“调优”却适得其反。

非标准参数的默认值在不同版本间或许会悄然发生变化。这些变化的背后多半有合理的理由。设了一大堆非标准参数、不明就里的同学在升级JDK/JRE的时候也容易掉坑里。

下面用Oracle/Sun JDK 6来举几个例子。这帖顶楼里的讨论如果没明确指出JDK版本的都是指Oracle/Sun JDK 6(OpenJDK 6也可以算在内)。
经验不一定适用于Sun JDK 1.4.2、Sun JDK 5、Oracle JDK 7。

======================================================================

0、各参数的默认值

在讨论HotSpot VM的各参数的陷阱前,大家应该先了解HotSpot VM到底有哪些参数可以设置,这些参数的默认值都是什么。

有几种办法可以帮助大家获取参数的信息。首先为了大致了解都有些什么参数可以设置,可以参考HotSpot VM里的各个globals.hpp文件:(以下链接取自HotSpot 20.0,与JDK 6 update 25对应)
globals.hpp
globals_extension.hpp
c1_globals.hpp
c1_globals_linux.hpp
c1_globals_solaris.hpp
c1_globals_sparc.hpp
c1_globals_windows.hpp
c1_globals_x86.hpp
c2_globals.hpp
c2_globals_linux.hpp
c2_globals_solaris.hpp
c2_globals_sparc.hpp
c2_globals_windows.hpp
c2_globals_x86.hpp
g1_globals.hpp
globals_linux.hpp
globals_linux_sparc.hpp
globals_linux_x86.hpp
globals_linux_zero.hpp
globals_solaris.hpp
globals_solaris_sparc.hpp
globals_solaris_x86.hpp
globals_sparc.hpp
globals_windows.hpp
globals_windows_x86.hpp
globals_x86.hpp
globals_zero.hpp
shark_globals.hpp
shark_globals_zero.hpp
arguments.cpp

然后是 -XX:+PrintCommandLineFlags 。这个参数的作用是显示出VM初始化完毕后所有跟最初的默认值不同的参数及它们的值。
这个参数至少在Sun JDK 5上已经开始支持,Oracle/Sun JDK 6以及Oracle JDK 7上也可以使用。Sun JDK 1.4.2还不支持这个参数。
例子:

Command prompt代码  收藏代码
  1. $ java -XX:+PrintCommandLineFlags  
  2. VM option '+PrintCommandLineFlags'  
  3. -XX:InitialHeapSize=57344000 -XX:MaxHeapSize=917504000 -XX:ParallelGCThreads=4 -XX:+PrintCommandLineFlags -XX:+UseCompressedOops -XX:+UseParallelGC   

《Java Performance》一书主要是用这个参数来介绍HotSpot VM各参数的效果的。

接着是 -XX:+PrintFlagsFinal 。前一个参数只显示跟默认值不同的,而这个参数则可以显示所有可设置的参数及它们的值。不过这个参数本身只从JDK 6 update 21开始才可以用,之前的Oracle/Sun JDK则用不了。
可以设置的参数默认是不包括diagnostic或experimental系的。要在-XX:+PrintFlagsFinal的输出里看到这两种参数的信息,分别需要显式指定-XX:+UnlockDiagnosticVMOptions / -XX:+UnlockExperimentalVMOptions

再下来,-XX:+PrintFlagsInitial 。这个参数显示在处理参数之前所有可设置的参数及它们的值,然后直接退出程序。“参数处理”包括许多步骤,例如说检查参数之间是否有冲突,通过ergonomics调整某些参数的值,之类的。
结合-XX:+PrintFlagsInitial与-XX:+PrintFlagsFinal,对比两者的差异,就可以知道ergonomics对哪些参数做了怎样的调整。
这两个参数的例子:


Command prompt代码  收藏代码
  1. $ java -version  
  2. java version "1.6.0_29"  
  3. Java(TM) SE Runtime Environment (build 1.6.0_29-b11)  
  4. Java HotSpot(TM) 64-Bit Server VM (build 20.4-b02, mixed mode)  
  5. $ java -XX:+PrintFlagsInitial | grep UseCompressedOops  
  6.      bool UseCompressedOops                         = false           {lp64_product}        
  7. $ java -XX:+PrintFlagsFinal | grep UseCompressedOops  
  8.      bool UseCompressedOops                        := true            {lp64_product}        

Oracle/Sun JDK 6从update 23开始会由ergonomics在合适的条件下默认启用压缩指针功能。这个例子演示的是UseCompressedOops的初始默认值是false,由PrintFlagsInitial的输出可以看到;然后经过ergonomics自动调整后,最终采用的默认值是true,由PrintFlagsFinal的输出可以看到。
输出里“=”表示使用的是初始默认值,而“:=”表示使用的不是初始默认值,可能是命令行传进来的参数、配置文件里的参数或者是ergonomics自动选择了别的值。


除了在VM启动时传些特殊的参数让它打印出自己的各参数外,jinfo -flag 可以用来查看某个参数的值,也可以用来设定manageable系参数的值。请参考这帖的例子:通过jinfo工具在full GC前后做heap dump

之前发过某些环境中HotSpot VM的各参数的默认值,可以参考一下。

======================================================================

1、-XX:+DisableExplicitGC 与 NIO的direct memory

很多人都见过JVM调优建议里使用这个参数,对吧?但是为什么要用它,什么时候应该用而什么时候用了会掉坑里呢?

首先要了解的是这个参数的作用。在Oracle/Sun JDK这个具体实现上,System.gc()的默认效果是引发一次stop-the-world的full GC,对整个GC堆做收集。有几个参数可以改变默认行为,之前发过一帖简单描述过,这里就不重复了。关键点是,用了-XX:+DisableExplicitGC参数后,System.gc()的调用就会变成一个空调用,完全不会触发任何GC(但是“函数调用”本身的开销还是存在的哦~)。

为啥要用这个参数呢?最主要的原因是为了防止某些手贱的同学在代码里到处写System.gc()的调用而干扰了程序的正常运行吧。有些应用程序 本来可能正常跑一天也不会出一次full GC,但就是因为有人在代码里调用了System.gc()而不得不间歇性被暂停。也有些时候这些调用是在某些库或框架里写的,改不了它们的代码但又不想 被这些调用干扰也会用这参数。

OK。看起来这参数应该总是开着嘛。有啥坑呢?

其中一种情况是下述三个条件同时满足时会发生的:
1、应用本身在GC堆内的对象行为良好,正常情况下很久都不发生full GC;
2、应用大量使用了NIO的direct memory,经常、反复的申请DirectByteBuffer
3、使用了-XX:+DisableExplicitGC

能观察到的现象是:


Log代码  收藏代码
  1. java.lang.OutOfMemoryError: Direct buffer memory  
  2.     at java.nio.Bits.reserveMemory(Bits.java:633)  
  3.     at java.nio.DirectByteBuffer.<init>(DirectByteBuffer.java:98)  
  4.     at java.nio.ByteBuffer.allocateDirect(ByteBuffer.java:288)  
  5. ...  


做个简单的例子来演示这现象:

Java代码  收藏代码
  1. import java.nio.*;  
  2.   
  3. public class DisableExplicitGCDemo {  
  4.   public static void main(String[] args) {  
  5.     for (int i = 0; i < 100000; i++) {  
  6.       ByteBuffer.allocateDirect(128);  
  7.     }  
  8.     System.out.println("Done");  
  9.   }  
  10. }  

然后编译、运行之:

Command prompt代码  收藏代码
  1. $ java -version  
  2. java version "1.6.0_25"  
  3. Java(TM) SE Runtime Environment (build 1.6.0_25-b06)  
  4. Java HotSpot(TM) 64-Bit Server VM (build 20.0-b11, mixed mode)  
  5. $ javac DisableExplicitGCDemo.java   
  6. $ java -XX:MaxDirectMemorySize=10m -XX:+PrintGC -XX:+DisableExplicitGC DisableExplicitGCDemo  
  7. Exception in thread "main" java.lang.OutOfMemoryError: Direct buffer memory  
  8.     at java.nio.Bits.reserveMemory(Bits.java:633)  
  9.     at java.nio.DirectByteBuffer.<init>(DirectByteBuffer.java:98)  
  10.     at java.nio.ByteBuffer.allocateDirect(ByteBuffer.java:288)  
  11.     at DisableExplicitGCDemo.main(DisableExplicitGCDemo.java:6)  
  12. $ java -XX:MaxDirectMemorySize=10m -XX:+PrintGC DisableExplicitGCDemo  
  13. [GC 10996K->10480K(120704K), 0.0433980 secs]  
  14. [Full GC 10480K->10415K(120704K), 0.0359420 secs]  
  15. Done  

可以看到,同样的程序,不带-XX:+DisableExplicitGC时能正常完成运行,而带上这个参数后却出现了OOM。
例子里用-XX:MaxDirectMemorySize=10m限制了DirectByteBuffer能分配的空间的限额,以便问题更容易展现出来。不用这个参数就得多跑一会儿了。

在这个例子里,main()里的循环不断申请DirectByteBuffer但并没有引用、使用它们,所以这些DirectByteBuffer应该刚创建出来就已经满足被GC的条件,等下次GC运行的时候就应该可以被回收。

实际上却没这么简单。DirectByteBuffer是种典型的“冰山”对象,也就是说它的Java对象虽然很小很无辜,但它背后却会关联着一 定量的native memory资源,而这些资源并不在GC的控制之下,需要自己注意控制好。对JVM如何使用native memory不熟悉的同学可以参考去年JavaOne上IBM的一个演讲,“Where Does All the Native Memory Go”。

Oracle/Sun JDK的实现里,DirectByteBuffer有几处值得注意的地方。
1、DirectByteBuffer没有finalizer,它的native memory的清理工作是通过sun.misc.Cleaner自动完成的。

2、sun.misc.Cleaner是一种基于PhantomReference的清理工具,比普通的finalizer轻量些。对PhantomReference不熟悉的同学请参考Bob Lee最近几年在JavaOne上做的演讲,"The Ghost in the Virtual Machine: A Reference to References"今年的JavaOne上他也讲了同一个主题,内容比前几年的稍微更新了些。PPT可以从链接里的页面下载到。

Java代码  收藏代码
  1. /** 
  2.  * General-purpose phantom-reference-based cleaners. 
  3.  * 
  4.  * <p> Cleaners are a lightweight and more robust alternative to finalization. 
  5.  * They are lightweight because they are not created by the VM and thus do not 
  6.  * require a JNI upcall to be created, and because their cleanup code is 
  7.  * invoked directly by the reference-handler thread rather than by the 
  8.  * finalizer thread.  They are more robust because they use phantom references, 
  9.  * the weakest type of reference object, thereby avoiding the nasty ordering 
  10.  * problems inherent to finalization. 
  11.  * 
  12.  * <p> A cleaner tracks a referent object and encapsulates a thunk of arbitrary 
  13.  * cleanup code.  Some time after the GC detects that a cleaner's referent has 
  14.  * become phantom-reachable, the reference-handler thread will run the cleaner. 
  15.  * Cleaners may also be invoked directly; they are thread safe and ensure that 
  16.  * they run their thunks at most once. 
  17.  * 
  18.  * <p> Cleaners are not a replacement for finalization.  They should be used 
  19.  * only when the cleanup code is extremely simple and straightforward. 
  20.  * Nontrivial cleaners are inadvisable since they risk blocking the 
  21.  * reference-handler thread and delaying further cleanup and finalization. 
  22.  * 
  23.  * 
  24.  * @author Mark Reinhold 
  25.  * @version %I%, %E% 
  26.  */  

重点是这两句:"A cleaner tracks a referent object and encapsulates a thunk of arbitrary cleanup code.  Some time after the GC detects that a cleaner's referent has become phantom-reachable, the reference-handler thread will run the cleaner."
Oracle/Sun JDK 6中的HotSpot VM只会在old gen GC(full GC/major GC或者concurrent GC都算)的时候才会对old gen中的对象做reference processing,而在young GC/minor GC时只会对young gen里的对象做reference processing。
(死在young gen中的DirectByteBuffer对象会在young GC时被处理的例子,请参考这里:https://gist.github.com/1614952
也就是说,做full GC的话会对old gen做reference processing,进而能触发Cleaner对已死的DirectByteBuffer对象做清理工作。而如果很长一段时间里没做过GC或者只做了 young GC的话则不会在old gen触发Cleaner的工作,那么就可能让本来已经死了的、但已经晋升到old gen的DirectByteBuffer关联的native memory得不到及时释放。

3、为DirectByteBuffer分配空间过程中会显式调用System.gc(),以期通过full GC来强迫已经无用的DirectByteBuffer对象释放掉它们关联的native memory:

Java代码  收藏代码
  1. // These methods should be called whenever direct memory is allocated or  
  2. // freed.  They allow the user to control the amount of direct memory  
  3. // which a process may access.  All sizes are specified in bytes.  
  4. static void reserveMemory(long size) {  
  5.   
  6.     synchronized (Bits.class) {  
  7.         if (!memoryLimitSet && VM.isBooted()) {  
  8.             maxMemory = VM.maxDirectMemory();  
  9.             memoryLimitSet = true;  
  10.         }  
  11.         if (size <= maxMemory - reservedMemory) {  
  12.             reservedMemory += size;  
  13.             return;  
  14.         }  
  15.     }  
  16.   
  17.     System.gc();  
  18.     try {  
  19.         Thread.sleep(100);  
  20.     } catch (InterruptedException x) {  
  21.         // Restore interrupt status  
  22.         Thread.currentThread().interrupt();  
  23.     }  
  24.     synchronized (Bits.class) {  
  25.         if (reservedMemory + size > maxMemory)  
  26.             throw new OutOfMemoryError("Direct buffer memory");  
  27.         reservedMemory += size;  
  28.     }  
  29.   
  30. }  


这几个实现特征使得Oracle/Sun JDK 6依赖于System.gc()触发GC来保证DirectByteMemory的清理工作能及时完成。如果打开了 -XX:+DisableExplicitGC,清理工作就可能得不到及时完成,于是就有机会见到direct memory的OOM,也就是上面的例子演示的情况。我们这边在实际生产环境中确实遇到过这样的问题。

教训是:如果你在使用Oracle/Sun JDK 6,应用里有任何地方用了direct memory,那么使用-XX:+DisableExplicitGC要小心。如果用了该参数而且遇到direct memory的OOM,可以尝试去掉该参数看是否能避开这种OOM。如果担心System.gc()调用造成full GC频繁,可以尝试下面提到 -XX:+ExplicitGCInvokesConcurrent 参数

======================================================================

2、-XX:+DisableExplicitGC 与 Remote Method Invocation (RMI) 与 -Dsun.rmi.dgc.client.gcInterval=

看了上一个例子有没有觉得-XX:+DisableExplicitGC参数用起来很危险?那干脆完全不要用这个参数吧。又有什么坑呢?

前段时间有个应用的开发来抱怨,说某次升级JDK之前那应用的GC状况都很好,很长时间都不会发生full GC,但升级后发现每一小时左右就会发生一次。经过对比发现,升级的同时也吧启动参数改了,把原本有的-XX:+DisableExplicitGC给去掉了。

观察到的日志有明显特征。一位同事表示:

引用
线上机器出现一个场景;每隔1小时出现一次Full GC,用btrace看了一下调用地:
who call system.gc :
sun.misc.GC$Daemon.run(GC.java:92)

预发机没什么流量,也会每一小时一次Full GC
频率正好是一小时一次

Gc log代码  收藏代码
  1. 2011-09-23T10:49:38.071+0800327692.227: [Full GC (System) 327692.227: [CMS: 75793K->75759K(2097152K), 0.6923690 secs] 430298K->75759K(3984640K), [CMS Perm : 104136K->104124K(173932K)], 0.6925570 secs]  


实际上这里在做的是分布式GC。Sun JDK的分布式GC是用纯Java实现的,为RMI服务。
RMI DGC相关参数的介绍文档:http://docs.oracle.com/javase/6/docs/technotes/guides/rmi/sunrmiproperties.html

(后面回头再补…先睡觉去了)

资料:
jGuru: Distributed Garbage Collection
http://mail.openjdk.java.net/pipermail/hotspot-dev/2011-December/004909.html

Tony Printezis 写道
RMI has a distributed GC that relies on reference processing to allow
each node to recognize that some objects are unreachable so it can
notify a remote node (or nodes) that some remote references to them do
not exist any more. The remote node might then be able to reclaim
objects that are only remotely reachable. (Or this is how I understood
it at least.)

RMI used to call System.gc() once a minute (!!!) but after some
encouragement from yours truly they changed the default to once an hour
(this is configurable using a property). Note that a STW Full GC is not
really required as long as references are processed. So, in CMS (and
G1), a concurrent cycle is fine which is why we recommend to use
-XX:+ExplicitGCInvokesConcurrent in this case.

I had been warned by the RMI folks against totally disabling those
System.gc()'s (e.g., using -XX:+DisableExplicitGC) given that if Full
GCs / concurrent cycles do not otherwise happen at a reasonable
frequency then remote nodes might experience memory leaks since they
will consider that some otherwise unreachable remote references are
still live. I have no idea how severe such memory leaks would be. I
guess they'd be very application-dependent.

An additional thought that just occurred to me: instead of calling
System.gc() every hour what RMI should really be doing is calling
System.gc() every hour provided no old gen GC has taken place during the
last hour. This would be relatively easy to implement by accessing the
old GC counter through the GC MXBeans.

Tony

再加俩链接:http://mail.openjdk.java.net/pipermail/hotspot-dev/2012-January/004929.html
http://mail.openjdk.java.net/pipermail/hotspot-dev/2012-January/004946.html

《Java Performance》的303和411页正好也提到了-XX:+DisableExplicitGC与RMI之间的干扰的事情,有兴趣可以读一下,虽然只有一小段。

======================================================================

3、-XX:+ExplicitGCInvokesConcurrent 或 -XX:+ExplicitGCInvokesConcurrentAndUnloadsClasses
 

C++代码  收藏代码
  1. product(bool, ExplicitGCInvokesConcurrent, false,                  \  
  2.   "A System.gc() request invokes a concurrent collection;"         \  
  3.   " (effective only when UseConcMarkSweepGC)")                     \  
  4.                                    \  
  5. product(bool, ExplicitGCInvokesConcurrentAndUnloadsClasses, false, \  
  6.   "A System.gc() request invokes a concurrent collection and "     \  
  7.   "also unloads classes during such a concurrent gc cycle "        \  
  8.   "(effective only when UseConcMarkSweepGC)")                      \  


跟上面的第一个例子的-XX:+DisableExplicitGC一样,这两个参数也是用来改变System.gc()的默认行为用的;不同的 是这两个参数只能配合CMS使用(-XX:+UseConcMarkSweepGC),而且System.gc()还是会触发GC的,只不过不是触发一个 完全stop-the-world的full GC,而是一次并发GC周期。

CMS GC周期中也会做reference processing。所以如果用这两个参数的其中一个,而不是用-XX:+DisableExplicitGC的话,就避开了由full GC带来的长GC pause,同时NIO direct memory的OOM也不会那么容易发生。

做了个跟第一个例子类似的例子,在这里:https://gist.github.com/1344251

《Java Performance》的303页有讲到这俩参数。

相关bug:6919638 CMS: ExplicitGCInvokesConcurrent misinteracts with gc locker
<< JDK6u23修复了这个问题

======================================================================

4、-XX:+GCLockerInvokesConcurrent
 

C++代码  收藏代码
  1. product(bool, GCLockerInvokesConcurrent, false,        \  
  2.   "The exit of a JNI CS necessitating a scavenge also" \  
  3.   " kicks off a bkgrd concurrent collection")          \  


(内容回头补…)

======================================================================

5、MaxDirectMemorySize 与 NIO direct memory 的默认上限

-XX:MaxDirectMemorySize 是用来配置NIO direct memory上限用的VM参数。

C++代码  收藏代码
  1. product(intx, MaxDirectMemorySize, -1,                         \  
  2.         "Maximum total size of NIO direct-buffer allocations") \  

但如果不配置它的话,direct memory默认最多能申请多少内存呢?这个参数默认值是-1,显然不是一个“有效值”。所以真正的默认值肯定是从别的地方来的。

在Sun JDK 6和OpenJDK 6里,有这样一段代码,sun.misc.VM:

Java代码  收藏代码
  1. // A user-settable upper limit on the maximum amount of allocatable direct  
  2. // buffer memory.  This value may be changed during VM initialization if  
  3. // "java" is launched with "-XX:MaxDirectMemorySize=<size>".  
  4. //  
  5. // The initial value of this field is arbitrary; during JRE initialization  
  6. // it will be reset to the value specified on the command line, if any,  
  7. // otherwise to Runtime.getRuntime().maxMemory().  
  8. //  
  9. private static long directMemory = 64 * 1024 * 1024;  
  10.   
  11. // If this method is invoked during VM initialization, it initializes the  
  12. // maximum amount of allocatable direct buffer memory (in bytes) from the  
  13. // system property sun.nio.MaxDirectMemorySize.  The system property will  
  14. // be removed when it is accessed.  
  15. //  
  16. // If this method is invoked after the VM is booted, it returns the  
  17. // maximum amount of allocatable direct buffer memory.  
  18. //  
  19. public static long maxDirectMemory() {  
  20.     if (booted)  
  21.         return directMemory;  
  22.   
  23.     Properties p = System.getProperties();  
  24.     String s = (String)p.remove("sun.nio.MaxDirectMemorySize");  
  25.     System.setProperties(p);  
  26.   
  27.     if (s != null) {  
  28.         if (s.equals("-1")) {  
  29.             // -XX:MaxDirectMemorySize not given, take default  
  30.             directMemory = Runtime.getRuntime().maxMemory();  
  31.         } else {  
  32.             long l = Long.parseLong(s);  
  33.             if (l > -1)  
  34.                 directMemory = l;  
  35.         }  
  36.     }  
  37.   
  38.     return directMemory;  
  39. }  

(代码里原本的注释有个写错的地方,上面有修正)
当MaxDirectMemorySize参数没被显式设置时它的值就是-1,在Java类库初始化时maxDirectMemory()被java.lang.System的静态构造器调用,走的路径就是这条:

Java代码  收藏代码
  1. if (s.equals("-1")) {  
  2.     // -XX:MaxDirectMemorySize not given, take default  
  3.     directMemory = Runtime.getRuntime().maxMemory();  
  4. }  

而Runtime.maxMemory()在HotSpot VM里的实现是:

C++代码  收藏代码
  1. JVM_ENTRY_NO_ENV(jlong, JVM_MaxMemory(void))  
  2.   JVMWrapper("JVM_MaxMemory");  
  3.   size_t n = Universe::heap()->max_capacity();  
  4.   return convert_size_t_to_jlong(n);  
  5. JVM_END  

这个max_capacity()实际返回的是 -Xmx减去一个survivor space的预留大小(G1除外)。

结论:MaxDirectMemorySize没显式配置的时候,NIO direct memory可申请的空间的上限就是-Xmx减去一个survivor space的预留大小
大家感兴趣的话可以试试在不同的-Xmx的条件下不设置MaxDirectMemorySize,并且调用一下sun.misc.VM.maxDirectMemory()看得到的值的相关性。

该行为在JDK7里没变,虽然具体实现的代码有些变化。请参考http://hg.openjdk.java.net/jdk7/jdk7/jdk/rev/b444f86c4abe

======================================================================

6、-verbose:gc 与 -XX:+PrintGCDetails

经常能看到在推荐的标准参数里这两个参数一起出现。实际上它们有啥关系?

在Oracle/Sun JDK 6里,"java"这个启动程序遇到"-verbosegc"会将其转换为"-verbose:gc",将启动参数传给HotSpot VM后,HotSpot VM遇到"-verbose:gc"则会当作"-XX:+PrintGC"来处理。
也就是说 -verbosegc、-verbose:gc、-XX:+PrintGC 三者的作用是完全一样的。

而当HotSpot VM遇到 -XX:+PrintGCDetails 参数时,会顺带把 -XX:+PrintGC 给设置上。
也就是说 -XX:+PrintGCDetails 包含 -XX:+PrintGC,进而也就包含 -verbose:gc。

既然 -verbose:gc 都被包含了,何必在命令行参数里显式设置它呢?

======================================================================

7、-XX:+UseFastEmptyMethods 与 -XX:+UseFastAccessorMethods

虽然不常见,但偶尔也会见到推荐的标准参数上有这俩的身影。

empty method顾名思义就是空方法,也就是方法体只包含一条return指令、返回值类型为void的Java方法。
accessor method在这里则有很具体的定义:

C++代码  收藏代码
  1. bool methodOopDesc::is_accessor() const {  
  2.   if (code_size() != 5) return false;  
  3.   if (size_of_parameters() != 1) return false;  
  4.   if (java_code_at(0) != Bytecodes::_aload_0 ) return false;  
  5.   if (java_code_at(1) != Bytecodes::_getfield) return false;  
  6.   if (java_code_at(4) != Bytecodes::_areturn &&  
  7.       java_code_at(4) != Bytecodes::_ireturn ) return false;  
  8.   return true;  
  9. }  

如果从Java源码的角度来理解,accessor method就是形如这样的:

Java代码  收藏代码
  1. public class Foo {  
  2.   private int value;  
  3.     
  4.   public int getValue() {  
  5.     return this.value;  
  6.   }  
  7. }  

关键点是:
1、必须是成员方法;静态方法不行
2、返回值类型必须是引用类型或者int,其它都不算
3、方法体的代码必须满足aload_0; getfield #index; areturn或ireturn这样的模式。
留意:方法名是什么都没关系,是不是get、is、has开头都不重要。

那么这俩有啥问题?

取自JDK 6 update 27:

C++代码  收藏代码
  1. product(bool, UseFastEmptyMethods, true,                   \  
  2.         "Use fast method entry code for empty methods")    \  
  3.                                                            \  
  4. product(bool, UseFastAccessorMethods, true,                \  
  5.         "Use fast method entry code for accessor methods") \  

看到这俩参数的默认值都是true了么?也就是说,在Oracle/Sun JDK 6上设置这参数其实也是没意义的,跟默认一样,一直到最新的JDK 6 update 29都是如此。

不过在Oracle/Sun JDK 7里,情况有变化。
Bug ID: 6385687 UseFastEmptyMethods/UseFastAccessorMethods considered harmful
上述bug对应的代码变更后,这俩参数的默认值改为了false。

本来想多写点这块的…算,还是长话短说。

Oracle JDK 7里的HotSpot VM已经开始有比较好的多层编译(tiered compilation)支持,可以预见在不久的将来该模式将成为HotSpot VM默认的执行模式。当前该模式尚未默认开启;可以通过 -XX:+TieredCompilation 来开启。

有趣的是,在使用多层编译模式时,如果UseFastAccessorMethods/UseFastEmptyMethods是开着的,有些多态方法调用点的性能反而会显著下降。所以,为了适应多层编译模式,JDK 7里这两个参数的默认值就被改为false了。
在邮件列表上有过相关讨论:review for 6385687: UseFastEmptyMethods/UseFastAccessorMethods considered harmful

======================================================================

8、-XX:+UseCMSCompactAtFullCollection

这个参数在Oracle/Sun JDK 6里一直都默认是true,完全没必要显式设置,设了也不会有啥不同的效果。

C++代码  收藏代码
  1. product(bool, UseCMSCompactAtFullCollection, true,    \  
  2.         "Use mark sweep compact at full collections") \  

我不认为显式设置一个跟默认值相同的参数有什么维护上的好处。要维护的参数多了反而更容易成为维护的噩梦吧。后面的人会不知道到底当初为什么要设置这个参数。

相关的有个 CMSFullGCsBeforeCompaction 参数,请参考另一帖里的讨论:http://hllvm.group.iteye.com/group/topic/28854#209294

同样,在Oracle/Sun JDK 6和OpenJDK 6里,CMSParallelRemarkEnabled 也一直默认是true,没必要显式设置-XX:+CMSParallelRemarkEnabled。
有很多bool类型的参数默认都是true,显式设置它们之前最好先用这帖开头介绍的办法看看默认值是否已经是想要的值了。
 

C++代码  收藏代码
  1. product(bool, CMSScavengeBeforeRemark, false,          \  
  2.         "Attempt scavenge before the CMS remark step") \  

这个默认倒是false。如果一个应用统计到的young GC时间都比较短而CMS remark的时间比较长,那么可以试试打开这个参数,在做remark之前先做一次young GC。是否能有效缩短remark的时间视应用情况而异,所以开这个参数的话请一定做好测试。

======================================================================

9、-XX:CMSMaxAbortablePrecleanTime=5000

同上…默认就是5000

C++代码  收藏代码
  1. product(intx, CMSMaxAbortablePrecleanTime, 5000,    \  
  2.         "(Temporary, subject to experimentation)"   \  
  3.         "Maximum time in abortable preclean in ms") \  

还是不要设跟默认值一样的参数了吧。

======================================================================

10、-Xss 与 -XX:ThreadStackSize

参考我之前发过的两帖:
What the difference between -Xss and -XX:ThreadStackSize is?
Inconsistency between -Xss and -XX:ThreadStackSize in the java launcher

(详情回头补~)

======================================================================

11、-Xmn 与 -XX:NewSize、-XX:MaxNewSize

如果同时设置了-XX:NewSize与-XX:MaxNewSize遇到“Could not reserve enough space for object heap”错误的话,请看看是不是这帖所说的问题。早期JDK 6似乎都受这问题影响,一直到JDK 6 update 14才修复。

======================================================================

12、-Xmn 与 -XX:NewRatio

======================================================================

13、-XX:NewRatio 与 -XX:NewSize、-XX:OldSize

======================================================================

14、jmap -heap看到的参数值与实际起作用的参数的关系?

发了几个例子在这里:https://gist.github.com/1363195
其中有个看起来很恐怖的值:

Java代码  收藏代码
  1. MaxNewSize       = 17592186044415 MB  

这是啥来的?
 

C++代码  收藏代码
  1. product(uintx, MaxNewSize, max_uintx,                                  \  
  2.         "Maximum new generation size (in bytes), max_uintx means set " \  
  3.         "ergonomically")     


在HotSpot VM里,intx是跟平台字长一样宽的带符号整型,uintx是其无符号版。
max_uintx是(uintx) -1,也就是说在32位平台上是无符号的0xFFFFFFFF,64位平台上则是0xFFFFFFFFFFFFFFFF。

jmap -heap显示的部分参数是以MB为单位来显示的,而MaxNewSize的单位是byte。我跑例子的平台是64位的,于是算一下 0xFFFFFFFFFFFFFFFF / 1024 / 1024 = 17592186044415 MB 。

参数的说明告诉我们,当MaxNewSize的值等于max_uintx时,意思就是交由ergonomics来自动选择young gen的最大大小。并不是说young gen的最大大小真的有0xFFFFFFFFFFFFFFFF这么大

要注意的是,HotSpot VM有大量可调节的参数,并不是所有参数在某次运行的时候都有效。

例如说设置了-Xmn的话,NewRatio就没作用了。
又例如说,

C++代码  收藏代码
  1. product(uintx, OldSize, ScaleForWordSize(4*M),        \  
  2.         "Initial tenured generation size (in bytes)") \  

-XX:OldSize参数的默认值在32位平台上是4M,在64位平台上是5M多。但如果这个参数没有被显式设置过,那它实际上是没作用 的;old gen的大小会通过Java heap的整体大小与young gen的大小配置计算出来,但OldSize参数却没有被更新(因为根本没用它)。于是这个参数的值与实际运行的状况就可能会不相符。
一种例外的情况是,如果-Xmx非常小,比NewSize+OldSize的默认值还小,那这个OldSize的默认值就会起作用,把MaxHeapSize给撑大。

C++代码  收藏代码
  1. void TwoGenerationCollectorPolicy::initialize_flags() {  
  2.   GenCollectorPolicy::initialize_flags();  
  3.   
  4.   OldSize = align_size_down(OldSize, min_alignment());  
  5.   if (NewSize + OldSize > MaxHeapSize) {  
  6.     MaxHeapSize = NewSize + OldSize;  
  7.   }  
  8.   MaxHeapSize = align_size_up(MaxHeapSize, max_alignment());  
  9. //...  
  10. }  

可以看这边的一个例子:https://gist.github.com/1375782

======================================================================

15、-XX:+AlwaysTenure、-XX:+NeverTenure、-XX:MaxTenuringThreshold=0 或 "-XX:MaxTenuringThreshold=markOopDesc::max_age + 1"

ParNew的时候,设定-XX:+AlwaysTenure隐含-XX:MaxTenuringThreshold=0;不过-XX:+NeverTenure却没啥特别的作用。

-XX:+AlwaysTenure 与 -XX:+NeverTenure 是互斥的,最后一个出现的那个会同时决定这两个参数的值。

======================================================================

16、-XX:MaxTenuringThreshold 的默认值?
 

C++代码  收藏代码
  1. product(intx, MaxTenuringThreshold,    15, \  
  2.   "Maximum value for tenuring threshold")  \  

Oracle/Sun JDK 6中,选择CMS之外的GC时,MaxTenuringThreshold(以下简称MTT)的默认值是15;而选择了CMS的时候,MTT的默认值是4 而不是15。设定是在 Arguments::set_cms_and_parnew_gc_flags() 里做的。

在Sun JDK 6之前(1.4.2、5),选择CMS的时候MTT的默认值则是0,也就是等于设定了-XX:+AlwaysTenure——所有eden里的活对象在经 历第一次minor GC的时候就会直接晋升到old gen,而survivor space直接就没用了。
 

Command prompt代码  收藏代码
  1. $ java -version  
  2. java version "1.6.0_25"  
  3. Java(TM) SE Runtime Environment (build 1.6.0_25-b06)  
  4. Java HotSpot(TM) 64-Bit Server VM (build 20.0-b11, mixed mode)  
  5. $ java -XX:+PrintFlagsFinal | egrep 'MaxTenuringThreshold|UseParallelGC|UseConcMarkSweepGC'  
  6.      intx MaxTenuringThreshold                      = 15              {product}             
  7.      bool UseConcMarkSweepGC                        = false           {product}             
  8.      bool UseParallelGC                            := true            {product}             
  9. $ java -XX:+PrintFlagsFinal -XX:+UseConcMarkSweepGC | egrep 'MaxTenuringThreshold|UseParallelGC|UseConcMarkSweepGC'  
  10.      intx MaxTenuringThreshold                     := 4               {product}             
  11.      bool UseConcMarkSweepGC                       := true            {product}             
  12.      bool UseParallelGC                             = false           {product}             


======================================================================

17、-XX:+CMSClassUnloadingEnabled

CMS remark暂停时间会增加,所以如果类加载并不频繁、String的intern也没有大量使用的话,这个参数还是不开的好。

======================================================================

18、-XX:+AggressiveHeap

======================================================================

19、-XX:+UseCompressedOops 有益?有害?

先把微博上回复别人问题的解答放这边。

本来如果功能没bug的话,Oracle/Sun JDK 6的64位HotSpot上,GC堆在26G以下(-Xmx + -XX:MaxPermSize)的时候用多数都是有益的。
开启压缩指针后,从代码路径(code path)和CPI(cycles per instruction)两个角度看,情况是不一样的:
·开启压缩指针会使代码路径变长,因为所有在GC堆里的、指向GC堆内对象的指针都会被压缩,这些指针的访问就需要更多的代码才可以实现。不要以为只是读写字段才受影响,其实实例方法调用、子类型检查等操作也受影响——“klass”也是一个指针,也被压缩了。
·但从CPI的角度看,由于压缩指针使需要拷贝的数据量变小了,cache miss的几率随之降低,结果CPI可能会比压缩前降低。综合来看,开了压缩指针通常能大幅降低GC堆内存的消耗,同时维持或略提高Java程序的速度。

但,JDK6u23之前那个参数的bug实在太多,最好别用;而6u23之后它就由ergonomics自动开启了,不用自己设。如果在6u23或更高版本碰到压缩指针造成的问题的话,显式设置 -XX:-UseCompressedOops

我能做的建议是如果在64位Oracle/Sun JDK 6/7上,那个参数不要显式设置。

关于HotSpot VM的ergonomics自动开启压缩指针功能,请参考之前的一帖

有些库比较“聪明”,会自行读取VM参数来调整自己的一些参数,例如Berkeley DB Java Edition。但这些库实现得不好的时候反而会带来一些麻烦:BDB JE要求显式指定-XX:+UseCompressedOops才能有效的调整它的缓存大小。所以在用BDB JE并且Java堆+PermGen大小小于32GB的时候,请显式指定-XX:+UseCompressedOops吧。参考Warning on Compressed Oops

======================================================================

20、-XX:LargePageSizeInBytes=128m ?

或者是 -XX:LargePageSizeInBytes=256m ?
其实这个参数的值是多少不是问题,问题是这个参数到底有没有起作用。

或许有人读过很老的调优建议资料,例如这个:
(2005) Java Tuning White Paper - 4.2.3 Tuning Example 3: Try 256 MB pages
或者是别的一些内容很老的资料。它们提到了-XX:LargePageSizeInBytes=参数。这些老资料也没说错,在Sun JDK 5里 -XX:LargePageSizeInBytes= 参数只在Solaris上有效,使用的时候没有别的参数保护。

但是,实际上这个参数在Oracle/Sun JDK 6里不配合-XX:+UseLargePages的话是不会起任何作用的。

JDK 6里的JVM的Linux版上初始化large page的地方:

C++代码  收藏代码
  1. bool os::large_page_init() {  
  2.   if (!UseLargePages) return false;  
  3.   
  4.   if (LargePageSizeInBytes) {  
  5.     _large_page_size = LargePageSizeInBytes;  
  6.   } else {  
  7.     // ...  
  8.   }  
  9.     
  10.   // ...  
  11.   
  12.   // Large page support is available on 2.6 or newer kernel, some vendors  
  13.   // (e.g. Redhat) have backported it to their 2.4 based distributions.  
  14.   // We optimistically assume the support is available. If later it turns out  
  15.   // not true, VM will automatically switch to use regular page size.  
  16.   return true;  
  17. }  

看到了么,没有将UseLargePages设置为true的话,LargePageSizeInBytes根本没机会被用上。

对应的,Solaris版:

C++代码  收藏代码
  1. bool os::large_page_init() {  
  2.   if (!UseLargePages) {  
  3.     UseISM = false;  
  4.     UseMPSS = false;  
  5.     return false;  
  6.   }  
  7.   
  8.   // ...  
  9. }  


以及Windows版:

C++代码  收藏代码
  1. bool os::large_page_init() {  
  2.   if (!UseLargePages) return false;  
  3.   
  4.   // ...  
  5. }  



在Oracle/Sun JDK 6以及Oracle JDK 7上要使用 -XX:LargePageSizeInBytes= 的话,请务必也设置上 -XX:+UseLargePages 。使用这两个参数之前最好先确认操作系统是否真的只是large pages;操作系统不支持的话,设置这两个参数也没作用,只会退回到使用regular pages而已。

======================================================================

21、-XX:+AlwaysPreTouch

会把commit的空间跑循环赋值为0以达到“pretouch”的目的。开这个参数会增加VM初始化时的开销,但后面涉及虚拟内存的开销可能降低。

在另一个讨论帖里有讲该参数:http://hllvm.group.iteye.com/group/topic/28839#209144

======================================================================

22、-XX:+UseTLAB 与 Runtime.freeMemory()

======================================================================

23、-XX:+ParallelRefProcEnabled

这个功能可以加速reference processing,但在JDK6u25和6u26上不要使用,有bug:
Bug ID 7028845: CMS: 6984287 broke parallel reference processing in CMS

======================================================================

24、-XX:+UseConcMarkSweepGC 与 -XX:+UseAdaptiveSizePolicy

这两个选项在现有的Oracle/Sun JDK 6和Oracle JDK 7上都不要搭配在一起使用——CMS用的adaptive size policy还没实现完,用的话可能会crash。

目前HotSpot VM上只有ParallelScavenge系的GC才可以配合-XX:+UseAdaptiveSizePolicy使用;也就是只有-XX:+UseParallelGC或者-XX:+UseParallelOldGC。

======================================================================

25、-XX:+UseAdaptiveGCBoundary

JDK 6里不要用这个选项,有bug。

======================================================================

26、-XX:HeapDumpPath 与 -XX:+HeapDumpBeforeFullGC、-XX:+HeapDumpAfterFullGC、-XX:+HeapDumpOnOutOfMemoryError

-XX:+HeapDumpBeforeFullGC、-XX:+HeapDumpAfterFullGC、 -XX:+HeapDumpOnOutOfMemoryError 这几个参数可以在不同条件下做出HPROF格式的heap dump。但很多人都会疑惑:做出来的heap dump存到哪里去了?

如果不想费神去摸索到底各种环境被配置成什么样、“working directory”到底在哪里的话,就在VM启动参数里加上 -XX:HeapDumpPath=一个绝对路径 吧。这样,自动做出的heap dump就会被存到指定的目录里去。
当然相对路径也支持,不过用了相对路径就又得弄清楚当前的“working directory”在哪里了。

======================================================================

26、UseDepthFirstScavengeOrder

以前有过这样一个参数可以设置young gen遍历对象图的顺序,深度还是广度优先不过高于JDK 6 update 22就没用了,ParallelScavenge变为只用深度优先而不用广度优先。
具体的changeset在这里:http://hg.openjdk.java.net/hsx/hotspot-main/hotspot/rev/9d7a8ab3736b

HotSpot VM里的arguments.cpp文件里有obsolete_jvm_flags数组,那边声明的参数都要留意是已经没用的。