随着Hibernate在Java开发中的广泛应用,我们在使用Hibernate进行对象持久化操作中也遇到了各种各样的问题。这些问题往往都是我们对Hibernate缺乏了解所致,这里我讲个我从前遇到的问题及一些想法,希望能给大家一点借鉴。 这是在一次事务提交时遇到的异常。 an assertion failure occured (this may indicate a bug in Hibernate, but is more likely due to unsafe use of the session) net.sf.hibernate.AssertionFailure: possible nonthreadsafe access to session 注:非possible non-threadsafe access to the session (那是另外的错误,类似但不一样) 这个异常应该很多的朋友都遇到过,原因可能各不相同。但所有的异常都应该是在flush或者事务提交的过程中发生的。这一般由我们在事务开始至事务提交的过程中进行了不正确的操作导致,也会在多线程同时操作一个Session时发生,这里我们仅仅讨论单线程的情况,多线程除了线程同步外基本与此相同。 至于具体是什么样的错误操作那?我给大家看一个例子(假设Hibernate配置正确,为保持代码简洁,不引入包及处理任何异常)
SessionFactory sf = new Configuration().configure().buildSessionFactory() ; Session s = sf.openSession(); Cat cat = new Cat();
Transaction tran = s.beginTransaction(); (1) s.save(cat); (2)(此处同样可以为update delete) s.evict(cat); (3) tran.commit(); (4) s.close();(5)
这就是引起此异常的典型错误。我当时就遇到了这个异常,检查代码时根本没感觉到这段代码出了问题,想当然的认为在Session上开始一个事务,通过Session将对象存入数据库,再将这个对象从Session上拆离,提交事务,这是一个很正常的流程。如果这里正常的话,那问题一定在别处。 问题恰恰就在这里,我的想法也许没有错,但是一个错误的论据所引出的观点永远都不可能是正确的。因为我一直以为直接在对数据库进行操作,忘记了在我与数据库之间隔了一个Hibernate,Hibernate在为我们提供持久化服务的同时,也改变了我们对数据库的操作方式,这种方式与我们直接的数据库操作有着很多的不同,正因为我们对这种方式没有一个大致的了解造成了我们的应用并未得到预先设想的结果。 那Hibernate的持久化机制到底有什么不同那?简单的说,Hibernate在数据库层之上实现了一个缓存区,当应用save或者update一个对象时,Hibernate并未将这个对象实际的写入数据库中,而仅仅是在缓存中根据应用的行为做了登记,在真正需要将缓存中的数据flush入数据库时才执行先前登记的所有行为。 在实际执行的过程中,每个Session是通过几个映射和集合来维护所有与该Session建立了关联的对象以及应用对这些对象所进行的操作的,与我们这次讨论有关的有entityEntries(与Session相关联的对象的映射)、insertions(所有的插入操作集合)、deletions(删除操作集合)、updates(更新操作集合)。下面我就开始解释在最开始的例子中,Hibernate到底是怎样运作的。 (1)生成一个事务的对象,并标记当前的Session处于事务状态(注:此时并未启动数据库级事务)。 (2)应用使用s.save保存cat对象,这个时候Session将cat这个对象放入entityEntries,用来标记cat已经和当前的会话建立了关联,由于应用对cat做了保存的操作,Session还要在insertions中登记应用的这个插入行为(行为包括:对象引用、对象id、Session、持久化处理类)。 (3)s.evict(cat)将cat对象从s会话中拆离,这时s会从entityEntries中将cat这个对象移出。 (4)事务提交,需要将所有缓存flush入数据库,Session启动一个事务,并按照insert,update,……,delete的顺序提交所有之前登记的操作(注意:所有insert执行完毕后才会执行update,这里的特殊处理也可能会将你的程序搞得一团糟,如需要控制操作的执行顺序,要善于使用flush),现在cat不在entityEntries中,但在执行insert的行为时只需要访问insertions就足够了,所以此时不会有任何的异常。异常出现在插入后通知Session该对象已经插入完毕这个步骤上,这个步骤中需要将entityEntries中cat的existsInDatabase标志置为true,由于cat并不存在于entityEntries中,此时Hibernate就认为insertions和entityEntries可能因为线程安全的问题产生了不同步(也不知道Hibernate的开发者是否考虑到例子中的处理方式,如果没有的话,这也许算是一个bug吧),于是一个net.sf.hibernate.AssertionFailure就被抛出,程序终止。 我想现在大家应该明白例子中的程序到底哪里有问题了吧,我们的错误的认为s.save会立即的执行,而将cat对象过早的与Session拆离,造成了Session的insertions和entityEntries中内容的不同步。所以我们在做此类操作时一定要清楚Hibernate什么时候会将数据flush入数据库,在未flush之前不要将已进行操作的对象从Session上拆离。 对于这个错误的解决方法是,我们可以在(2)和(3)之间插入一个s.flush()强制Session将缓存中的数据flush入数据库(此时Hibernate会提前启动事务,将(2)中的save登记的insert语句登记在数据库事务中,并将所有操作集合清空),这样在(4)事务提交时insertions集合就已经是空的了,即使我们拆离了cat也不会有任何的异常了。 前面简单的介绍了一下Hibernate的flush机制和对我们程序可能带来的影响以及相应的解决方法,Hibernate的缓存机制还会在其他的方面给我们的程序带来一些意想不到的影响。看下面的例子:
(name为cat表的主键) Cat cat = new Cat(); cat.setName(“tom”); s.save(cat);
cat.setName(“mary”); s.update(cat);(6)
Cat littleCat = new Cat(); littleCat.setName(“tom”); s.save(littleCat);
s.flush();
这个例子看起来有什么问题?估计不了解Hibernate缓存机制的人多半会说没有问题,但它也一样不能按照我们的思路正常运行,在flush过程中会产生主键冲突,可能你想问:“在save(littleCat)之前不是已经更改cat.name并已经更新了么?为什么还会发生主键冲突那?”这里的原因就是我在解释第一个例子时所提到的缓存flush顺序的问题,Hibernate按照insert,update,……,delete的顺序提交所有登记的操作,所以你的s.update(cat)虽然在程序中出现在s.save(littleCat)之前,但是在flush的过程中,所有的save都将在update之前执行,这就造成了主键冲突的发生。 这个例子中的更改方法一样是在(6)之后加入s.flush()强制Session在保存littleCat之前更新cat的name。这样在第二次flush时就只会执行s.save(littleCat)这次登记的动作,这样就不会出现主键冲突的状况。 再看一个例子(很奇怪的例子,但是能够说明问题)
Cat cat = new Cat(); cat.setName(“tom”); s.save(cat); (7) s.delete(cat);(8)
cat.id=null;(9) s.save(cat);(10) s.flush();
这个例子在运行时会产生异常net.sf.hibernate.HibernateException: identifier of an instance of Cat altered from 8b818e920a86f038010a86f03a9d0001 to null 这里例子也是有关于缓存的问题,但是原因稍有不同: (7)和(2)的处理相同。 (8)Session会在deletions中登记这个删除动作,同时更新entityEntries中该对象的登记状态为DELETED。 (9)Cat类的标识符字段为id,将其置为null便于重新分配id并保存进数据库。 (10)此时Session会首先在entityEntries查找cat对象是否曾经与Session做过关联,因为cat只改变了属性值,引用并未改变,所以会取得状态为DELETED的那个登记对象。由于第二次保存的对象已经在当前Session中删除,save会强制Session将缓存flush才会继续,flush的过程中首先要执行最开始的save动作,在这个save中检查了cat这个对象的id是否与原来执行动作时的id相同。不幸的是,此时cat的id被赋为null,异常被抛出,程序终止(此处要注意,我们在以后的开发过程尽量不要在flush之前改变已经进行了操作的对象的id)。 这个例子中的错误也是由于缓存的延时更新造成的(当然,与不正规的使用Hibernate也有关系),处理方法有两种: 1、在(8)之后flush,这样就可以保证(10)处save将cat作为一个全新的对象进行保存。 2、删除(9),这样第二次save所引起的强制flush可以正常的执行,在数据库中插入cat对象后将其删除,然后继续第二次save重新插入cat对象,此时cat的id仍与从前一致。 这两种方法可以根据不同的需要来使用,呵呵,总觉得好像是很不正规的方式来解决问题,但是问题本身也不够正规,只希望能够在应用开发中给大家一些帮助,不对的地方也希望各位给与指正。 总的来说,由于Hibernate的flush处理机制,我们在一些复杂的对象更新和保存的过程中就要考虑数据库操作顺序的改变以及延时flush是否对程序的结果有影响。如果确实存在着影响,那就可以在需要保持这种操作顺序的位置加入flush强制Hibernate将缓存中记录的操作flush入数据库,这样看起来也许不太美观,但很有效。 |
posted @
2006-05-24 13:47 java小记 阅读(319) |
评论 (0) |
编辑 收藏
public
static
void
main(String[] args)
throws
Exception
{
System.out.println(isWrapClass(Long.
class
));
System.out.println(isWrapClass(String.
class
));
}
public
static
boolean
isWrapClass(Class clz)
{
try
{
return
((Class) clz.getField(
"
TYPE
"
).get(
null
)).isPrimitive();
}
catch
(Exception e)
{
return
false
;
}
}
posted @
2006-05-22 16:44 java小记 阅读(887) |
评论 (0) |
编辑 收藏
<script language="javascript">
g_blnCheckUnload = true;
function RunOnBeforeUnload() {
if (g_blnCheckUnload) {window.event.returnValue = 'You will lose any unsaved content';
}
}
</script>
<body onbeforeunload="RunOnBeforeUnload()">
</body>
posted @
2006-05-15 09:27 java小记 阅读(251) |
评论 (0) |
编辑 收藏
import
java.io.FileOutputStream;
import
java.util.List;
import
org.jdom.Attribute;
import
org.jdom.Document;
import
org.jdom.Element;
import
org.jdom.input.SAXBuilder;
import
org.jdom.output.Format;
import
org.jdom.output.XMLOutputter;
public
class
XML
{
public
static
void
main(String[] args)
throws
Exception
{
SAXBuilder builder
=
new
SAXBuilder();
Document doc
=
builder.build(
"
d:\\destination.xml
"
);
Element root
=
doc.getRootElement();
root.removeChild(
"
Target
"
);
Element items
=
root.getChild(
"
Items
"
);
List item
=
items.getChildren(
"
item
"
);
for
(
int
i
=
0
; i
<
item.size(); i
++
)
{
Element elem
=
(Element) item.get(i);
Attribute attr
=
elem.getAttribute(
"
displayName
"
);
attr.setValue(attr.getValue()
+
"
中国
"
);
}
//
保存
Format format
=
Format.getCompactFormat();
format.setEncoding(
"
gb2312
"
);
format.setIndent(
"
"
);
XMLOutputter XMLOut
=
new
XMLOutputter(format);
XMLOut.output(doc,
new
FileOutputStream(
"
d:\\destination_tmp.xml
"
));
}
}
posted @
2006-05-13 09:22 java小记 阅读(219) |
评论 (0) |
编辑 收藏
STUN (Simple Traversal of UDP through NATs (Network Address Translation)) is a protocol for assisting devices behind a NAT firewall or router with their packet routing.
- STUN enables a device to find out its public IP address and the type of NAT service its sitting behind.
- STUN operates on TCP and UDP port 3478.
- STUN is not widely supported by VOIP devices yet.
- STUN may use DNS SRV records to find STUN servers attached to a domain. The service name is _stun._udp or _stun._tcp
Definitions (from the RFC)
- STUN Client: A STUN client (also just referred to as a client) is an entity that generates STUN requests. A STUN client can execute on an end system, such as a user's PC, or can run in a network element, such as a conferencing server.
- STUN Server: A STUN Server (also just referred to as a server) is an entity that receives STUN requests, and sends STUN responses. STUN servers are generally attached to the public Internet.
Various types of NAT (still according to the RFC)- Full Cone: A full cone NAT is one where all requests from the same internal IP address and port are mapped to the same external IP address and port. Furthermore, any external host can send a packet to the internal host, by sending a packet to the mapped external address.
- Restricted Cone: A restricted cone NAT is one where all requests from the same internal IP address and port are mapped to the same external IP address and port. Unlike a full cone NAT, an external host (with IP address X) can send a packet to the internal host only if the internal host had previously sent a packet to IP address X.
- Port Restricted Cone: A port restricted cone NAT is like a restricted cone NAT, but the restriction includes port numbers. Specifically, an external host can send a packet, with source IP address X and source port P, to the internal host only if the internal host had previously sent a packet to IP address X and port P.
- Symmetric: A symmetric NAT is one where all requests from the same internal IP address and port, to a specific destination IP address and port, are mapped to the same external IP address and port. If the same host sends a packet with the same source address and port, but to a different destination, a different mapping is used. Furthermore, only the external host that receives a packet can send a UDP packet back to the internal host.
posted @
2006-05-12 16:19 java小记 阅读(315) |
评论 (0) |
编辑 收藏
1、密码由6-32位字母、数字或下划线构成;
2、至少需要一位小写字母;
3、至少需要一位大写字母;
4、至少需要一位数字。
String password = "password";
System.out.println(password != null && password.length() >= 6
&& password.length() <= 32
&& Pattern.compile("[a-z]+").matcher(password).find()
&& Pattern.compile("[A-Z]+").matcher(password).find()
&& Pattern.compile("[\\d]+").matcher(password).find());
posted @
2006-05-10 09:10 java小记 阅读(403) |
评论 (0) |
编辑 收藏
JList组件有一个单独的显示模式ListModel来表示JList的显示数据.
JList创建以后,JList数据元素的值及数据元素的数量可以动态地改变.
JList在它的数据模式ListModel中观察数据的改变.因此,一个ListModel 的正确实现应当在每次数据发生改变时,通知事件的监听者.
当使用构造函数JList(Object[])创建一个JList的实例时,系统将自动 创建一个DefaultListModel的实例来存储JList的显示数据, 可以调用 DefaultListModel中定义的简便方法来动态地修改JList的数据,如 removeElementAt(index),addElement(Object)等. DefaultListModel 在修改数据的同时,将通知JList关于数据的改变.
posted @
2006-04-28 08:28 java小记 阅读(468) |
评论 (0) |
编辑 收藏
import
java.awt.
*
;
import
java.awt.event.
*
;
public
class
MyFrame
extends
Frame
{
{
public
MyFrame()
{
setSize(
500
,
400
);
setResizable(
false
);
this
.addWindowStateListener(
new
WindowStateListener()
{
//
状态监听器
public
void
windowStateChanged(WindowEvent e)
{
if
(getState()
==
1
)
{
//
最小化状态
setState(
0
);
//
切换成正常状态
}
}
}
);
}
public
static
void
main(String[] args)
{
new
MyFrame().setVisible(
true
);
}
}
posted @
2006-04-28 08:28 java小记 阅读(2699) |
评论 (0) |
编辑 收藏
简而言之:
Big endian machine: It thinks the first byte it reads is the biggest.
Little endian machine: It thinks the first byte it reads is the littlest.
举个例子,从内存地址0x0000开始有以下数据
0x0000 0x12
0x0001 0x34
0x0002 0xab
0x0003 0xcd
如果我们去读取一个地址为0x0000的四个字节变量,若字节序为big-endian,则读出
结果为0x1234abcd;若字节序位little-endian,则读出结果为0xcdab3412.
如果我们将0x1234abcd写入到以0x0000开始的内存中,则结果为
big-endian little-endian
0x0000 0x12 0xcd
0x0001 0x23 0xab
0x0002 0xab 0x34
0x0003 0xcd 0x12
x86系列CPU都是little-endian的字节序.
posted @
2006-04-25 15:21 java小记 阅读(320) |
评论 (0) |
编辑 收藏
VoIP applications like Skype, GoogleTalk and Others
Internet Voice, also known as Voice over Internet Protocol (VoIP), is a technology that allows you to make telephone calls using a broadband Internet connection instead of a regular (or analog) phone line. Some services using VoIP may only allow you to call other people using the same service, but others may allow you to call anyone who has a telephone number - including local, long distance, mobile, and international numbers. Also, while some services only work over your computer or a special VoIP phone, other services allow you to use a traditional phone through an adaptor.
posted @
2006-04-05 09:59 java小记 阅读(260) |
评论 (1) |
编辑 收藏
import
java.security.MessageDigest;
import
java.security.NoSuchAlgorithmException;
public
class
PasswordManager
{
//
***************************************************************
//
Public Interface
//
***************************************************************
/** */
/**
* Creates a one-way has of the specified password. This allows passwords to be
* safely stored in the database without any way to retrieve the original value.
*
*
@param
password the string to encrypt.
*
*
@return
the encrypted password, or null if encryption failed.
*/
public
static
String encryptPassword( String password )
{
try
{
MessageDigest md
=
MessageDigest.getInstance(
"
SHA
"
);
//
Create the encrypted Byte[]
md.update( password.getBytes() );
byte
[] hash
=
md.digest();
//
Convert the byte array into a String
StringBuffer hashStringBuf
=
new
StringBuffer();
String byteString;
int
byteLength;
for
(
int
index
=
0
; index
<
hash.length; index
++
)
{
byteString
=
String.valueOf( hash[index ]
+
128
);
//
Pad string to 3. Otherwise hash may not be unique.
byteLength
=
byteString.length();
switch
( byteLength )
{
case
1
:
byteString
=
"
00
"
+
byteString;
break
;
case
2
:
byteString
=
"
0
"
+
byteString;
break
;
}
hashStringBuf.append( byteString );
}
return
hashStringBuf.toString();
}
catch
( NoSuchAlgorithmException nsae )
{
System.out.println(
"
Error getting password hash -
"
+
nsae.getMessage() );
return
null
;
}
}
}
posted @
2006-03-28 15:45 java小记 阅读(273) |
评论 (0) |
编辑 收藏
偶尔eclipse3.1的web项目有时候不能自动编译/WEB-INF/src,即使选择工程的自动编译开关也不好使,不知道是不是Bug
我这样解决的:
1. project->properties->java build path->source->.../WEB-INF/src的output folder不要默认,编辑让它指向../WEB-INF/classes
然后重新点击build工程即可自动编译。
2. 再就是最重要的要看工程下面是否缺少了work目录,由于CVS控制时不把work加如版本,所以checkout后没有这个目录,要手工加上有的工程就能自动编译了
posted @
2006-03-21 10:50 java小记 阅读(1318) |
评论 (0) |
编辑 收藏
XP系统一般情况下在装完系统后会有一个计算机管理员权限的用户,以后登陆时就显示这个用户
进入系统后在控制面板中的用户帐号下看不到Administrator用户,就好象丢失了一样,如何看到呢?
里面有另外一个问题涉及XP自动登陆
单击"开始/运行",输入"rundll32 netplwiz.dll,UsersRunDll",按回车键后弹出“用户帐户”窗口,
这和“控制面板”中打开的“用户账户”窗口不同!
然后取消选定"要使用本机,用户必须输入用户名和密码"选项,单击确定.
在弹出的对话框中输入你想让电脑每次自动登录的账户(默认Administrator)和密码即可。
下一次开机自动用Administrator登陆到系统,再看控制面板就有了Administrator。
Xp默认把Administrator隐藏,虽然都是计算机管理员Administrator和有计算机管理员权限的用户还是有
细微差别的。但是一般情况下使用系统用计算机管理员权限的用户就足够了
posted @
2006-02-13 21:40 java小记 阅读(5285) |
评论 (3) |
编辑 收藏
安全域是Tomcat的内置功能,主要有以下几种安全域:
JDBCRealm
DataSourceRealm
JNDIRealm
MemoryRealm
JAASRealm
在conf/server.xml中配置应用的<Context......>下面的<Realm className="org.apache.catalina.realm.MemoryRealm" />
从一个XML文件中读取用户信息,默认的就是tomcat-users.xml
tomcat-users.xml中的角色定义
<?xml version='1.0' encoding='utf-8'?>
<tomcat-users>
<role rolename="tomcat"/>
<role rolename="role1"/>
<role rolename="guest"/>
<user username="lee" password="lee" roles="guest"/>
<user username="suoxin" password="suoxin" roles="tomcat,role1"/>
</tomcat-users>
在应用下的web.xml中加入<security-constraint>元素
<security-constraint>
<web-resource-collection>
<url-pattern>/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>admin</role-name>
<role-name>guest</role-name>
</auth-constraint>
</security-constraint>
在应用下的web.xml中加入<login-config>元素
<login-config>
<auth-method>FORM</auth-method>
<!--这里FORM是基于表单的验证,会跳转到mylogin.jsp,如果出错就到myerror.jsp,
还有基于对话筐的是BASIC关键字,但是不安全在网络传输中。摘要验证DIGEST会采
用MD5(Message Digest Algorithm)加密传输-->
<form-login-config>
<form-login-page>/mylogin.jsp</form-login-page>
<form-error-page>/myerror.jsp</form-error-page>
</form-login-config>
</login-config>
mylogin.jsp的action和name必须严格规范写
<form name="form1" id="form1" method="post" action="j_security_check">
<input type="text" name="j_username"/>
<input type="text" name="j_password"/>
<input type="submit" name="Submit" value="提交" />
</form>
posted @
2006-02-11 09:11 java小记 阅读(217) |
评论 (0) |
编辑 收藏
1,下载OSCache, oscache-2.2-full.zip
2,解压缩oscache-2.2-full.zip后把oscache-2.2.jar拷贝到应用的WEB-INF/lib下 ,
并把etc目下下的oscache.properties拷贝到应用的WEB-INF/classes下.
3, 在应用的web.xml中加入缓存过滤器
<filter>
<filter-name>CacheFilter</filter-name>
<filter-class>com.opensymphony.oscache.web.filter.CacheFilter</filter-class>
<init-param>
<param-name>time</param-name>
<param-value>600</param-value>
</init-param>
<init-param>
<param-name>scope</param-name>
<param-value>application</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CacheFilter</filter-name>
<url-pattern>/servlets/UserAllProducts</url-pattern>
</filter-mapping> 以上的/servlets/UserAllProducts访问需要操作数据库,并且这些内容一段时间内很少改变,这样在内存缓存这个URL很有必要
它可以降低数据库访问量。
经过以上配置后,当第一次访问/servlets/UserAllProducts时,从数据库中查出所有产品列表并缓存到application中600秒。
在600秒内的任何访问/servlets/UserAllProducts都不会真正执行这个servlet,而直接从application中取出缓存的内容。这样
就减少了数据库的访问量。
posted @
2006-02-09 18:58 java小记 阅读(287) |
评论 (0) |
编辑 收藏
今天遇到了这样的问题,就是浮点运算后数据比较出现错误,郁闷了半天,网上查了资料才发现浮点数直接用双目运算符连接会出现结果不准确问题。解决方法如下:
1。所有浮点运算都在数据库内做好,也就是都用sql实现了
2。用BigDecimal实现,方法如下(仅仅是个例子):
import java.math.BigDecimal;
public class tt {
/** *//**
* @param args
*/
public static void main(String[] args) {
float a = 1.1f;
float b = 2.2f;
tt t = new tt();
System.out.println(t.add(a,b));
System.out.println(t.sub(a,b));
System.out.println(t.mul(a,b));
System.out.println(t.div(a,b));
System.out.println(t.round(a));
}
public float add(float v1,float v2){//加法
BigDecimal b1 = new BigDecimal(Float.toString(v1));
BigDecimal b2 = new BigDecimal(Float.toString(v2));
return b1.add(b2).floatValue();
}
public float sub(float v1,float v2){//减法
BigDecimal b1 = new BigDecimal(Float.toString(v1));
BigDecimal b2 = new BigDecimal(Float.toString(v2));
return b1.subtract(b2).floatValue();
}
public float mul(float v1,float v2){//乘法
BigDecimal b1 = new BigDecimal(Float.toString(v1));
BigDecimal b2 = new BigDecimal(Float.toString(v2));
return b1.multiply(b2).floatValue();
}
public float div(float v1,float v2){//除法
BigDecimal b1 = new BigDecimal(Float.toString(v1));
BigDecimal b2 = new BigDecimal(Float.toString(v2));
return b1.divide(b2,3,BigDecimal.ROUND_HALF_UP).floatValue();
}
public float round(float v){//截取3位
BigDecimal b = new BigDecimal(Float.toString(v));
BigDecimal one = new BigDecimal("1");
return b.divide(one,3,BigDecimal.ROUND_HALF_UP).floatValue();
}
}
posted @
2006-02-08 16:43 java小记 阅读(275) |
评论 (0) |
编辑 收藏
问题:Spring+Hibernate的应用中,定义了两个业务Service,这里分别称它们为serivceA,ServiceB。
它们的关系简单点来说是这样的:
serviceA需要引用serviceB,在serviceB中定义了一个接口列表,serverA必须在serviceB初始化时设置进列表。
在纯bean的情况下,也就是这两个类不需要设置其他bean的情况下,循环引用是正常的,可以通过的。例如下面配置所表示:
<bean id="serviceA" class="A" autowire="byName" lazy-init="true">
<property name="serviceB"><ref local="serviceB"/></property>
</bean>
<bean id="serviceB" class="B" autowire="byName" lazy-init="true">
<property name="serviceA"><ref bean="serviceA"/></property>
</bean>
但是作为一个业务接口,它应该是不需要关心事务,回滚这些无关的东西,
但现实又有这样的需求,所以我们必须保证透明的实现这个功能,于是引
入了AOP方式解决该问题,利用的是Spring自带的org.springframework.t
ransaction.interceptor.TransactionProxyFactoryBean.
重新声明文件如下:
<bean id="baseTxProxy" lazy-init="true"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="proxyTargetClass"><value>true</value></property>
<property name="transactionAttributes">
<props>
<prop key="*">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
<bean id="serviceA" parent="baseTxProxy">
<property name="target"><ref local="serviceAImpl"/></property>
</bean>
<bean id="serviceAImpl" class="serviceA" autowire="byName" lazy-init="true">
<property name="serviceB">
<ref bean="serviceB"/>
</property>
</bean>
<bean id="serviceB" parent="baseTxProxy" lazy-init="true">
<property name="target"><ref local="serviceBImpl"/></property>
</bean>
<bean id="serviceBImpl" class="D" lazy-init="true">
<property name="serviceA">
<ref bean="serviceA"/>
</property>
</bean>
于是问题就出现了,Spring报了FactoryBeanCircularReferenceException,无法继续完成设置工作。
查看TransactionProxyFactoryBean源码,其实现了FactoryBean和InitializingBean接口,应该是
做了代理之后,两个代理Bean需要等待所有Bean设置完成后才会标识状态为初始化完毕,于是造成了
冲突。
由于两个业务服务互相调用的路径是不相交的,所以采用了一种变通的方法,在声明serviceA时,
直接定义serviceB:
<bean id="serviceAImpl" class="serviceA" autowire="byName" lazy-init="true">
<property name="serviceB">
<bean class="B" autowire="byName"/>
</property>
</bean>
相当于serviceB和serviceA中使用的serviceB不是同一个实例。
但是如果确实调用重合时怎么办?
解决方法是这样的:
<bean id="serviceAImpl" class="serviceA" autowire="byName" lazy-init="true">
<property name="serviceB">
<ref bean="serviceBImpl"/>
</property>
</bean>
非常简单,serviceAImpl调用时,可能已经在事务环境中了,不需再使用serviceB代理的事务支持,
于是直接引用serviceB实例。这个方法是我写这篇文章时想到的,-_-!!!,看来知识果真还是好好
整理呀。
posted @
2006-02-08 16:32 java小记 阅读(896) |
评论 (0) |
编辑 收藏
关于网站开发中连接的可移植性总结
网络主机地址http://localhost:8080/
1. 相对于根目录的连接,形如: "/another.jsp"
/DIR/目录下的
<a href="/another.jsp">Link</a>
<html:link href="/another.jsp">Link</html:link>
<html:link page="/another.jsp">Link</html:link>
在默认ROOT应用中,分别连接到地址:
http://localhost:8080/another.jsp
http://localhost:8080/another.jsp
http://localhost:8080/another.jsp
在应用test中,分别连接到地址:
http://localhost:8080/another.jsp
http://localhost:8080/another.jsp
http://localhost:8080/test/another.jsp
2. 相对于当前目录的连接,形如: "./another.jsp" 或 "another.jsp"
/DIR/目录下的
<a href="./another.jsp">Link</a>
<html:link href="./another.jsp">Link</html:link>
<html:link page="./another.jsp">Link</html:link>
在默认ROOT应用中,都分别连接到地址:
http://localhost:8080//DIR/another.jsp
http://localhost:8080//DIR/another.jsp
http://localhost:8080//DIR/another.jsp
在应用test中,分别连接到地址:
http://localhost:8080//DIR/another.jsp
http://localhost:8080//DIR/another.jsp
http://localhost:8080/test./another.jsp 错误连接(而且与DIR无关,都连接到此地址)
/DIR/目录下的
<a href="another.jsp">Link</a>
<html:link href="another.jsp">Link</html:link>
<html:link page="another.jsp">Link</html:link>
在默认ROOT应用中,都分别连接到地址:
http://localhost:8080//DIR/another.jsp
http://localhost:8080//DIR/another.jsp
http://localhost:8080//DIR/another.jsp
在应用test中,分别连接到地址:
http://localhost:8080//DIR/another.jsp
http://localhost:8080//DIR/another.jsp
http://localhost:8080/testanother.jsp 错误连接(而且与DIR无关,都连接到此地址)
3总结
由于在网站开发时经常不使用默认的WEB应用,而可能把一个模块开发成一个WEb应用(可能多人合作),
但是在发布的时候要把所有模块发布为一个WEB应用,并通常是默认的WEB应用,为了使URL不依赖于Web应用
和是否是默认WEB应用,建议如下
a.对于相对于WEB应用根目录的连接,形如: "/another.jsp"
使用<html:link page="/..">Link </html:link>
b.对于相对于当前目录的连接,形如: "./another.jsp" 或 "another.jsp"
使用<html:link href="./another.jsp">Link </html:link>
<a href="./another.jsp">Link </a>或
<html:link href="another.jsp">Link </html:link>
<a href="another.jsp">Link </a>
不要使用<html:link page="./another.jsp">Link </html:link>
<html:link page="another.jsp">Link </html:link> 此二者不可移植
c.建议使用struts的html标签库<html:link..标签,因为当用户关闭Cookie时会自动重写URL,所以概括
两句话:相对应用根目录用<html:link page="/.."
相对当前的目录用<html:link href="./XXXXX.jsp" 或 <html:link href="XXXXX.jsp"
4.补充
还有一个标签<html:link forward="forwardname"> Link </html:link> 上面没有提到,现做以说明。
forward属性和struts配置文件<global-forwards>中的一个<forward>元素匹配,不能和<action>中<forward>匹配。
例子:<global-forwards>
<forward name="forwardname" path="//another.jsp"/>
</global-forwards>
<html:link forward="forwardname"> Link </html:link>
相当于<html:link page="//another.jsp"> Link </html:link>
需要注意的是:<global-forwards>中<forward>的path要使用相对于WEB应用的根目录路径,包括<action>中也是。
在struts1.1多应用模块时<html:link forwad. 有时不好使,不知道为什么????(好像是模块切换状态
不对)所以最好不用。
替代方法;
网页中用连接
<html:link page="/forwardaction.do">forward</html:link>
在action-mapping配置如下action
<action path="/forwardaction" forward="/index.jsp" />
posted @
2006-02-06 20:58 java小记 阅读(207) |
评论 (0) |
编辑 收藏
package aa;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
//for test6
import static java.lang.Math.*;
public class Test {
public static void main(String[] args) {
// 1
Double d = 123.45;
d += 20.601;
System.out.println(d);
double dd = d + 45.123;
System.out.println(dd);
// 2,3
List<List<String>> list = new ArrayList<List<String>>();
List<String> line1 = new ArrayList<String>();
line1.add("hello");
line1.add(",");
line1.add("world");
// line1.add(new Integer(123));//complie error
list.add(line1);
List<String> line2 = new ArrayList<String>();
line2.add("hello2");
line2.add(",");
line2.add("world2");
list.add(line2);
for (List<String> g : list) {
for (String str : g) {
System.out.print(str);
}
System.out.println();
}
// 4
Color bg = Color.Red;
System.out.println(bg);
for (Color c : Color.values()) {
System.out.println(c);
}
// 5
print("hello", ",", "World", new Date(), 123456);
//6
double i=2*PI;
print(i);
}
public static void print(Object objects) {
for (Object obj : objects) {
System.out.println(obj);
}
}
}
enum Color {
Red, Green, Blue
}
posted @
2006-02-05 10:55 java小记 阅读(229) |
评论 (0) |
编辑 收藏
eclipse2不支持jdk1.5支持1.4
eclipse3.0好象也不支持jdk1.5
eclipse3.1支持jdk1.5
jbuilder9不支持jdk1.5支持1.4
jbuilder2006好象集成的就是jdk1.5,没用过
posted @
2006-02-05 10:52 java小记 阅读(236) |
评论 (0) |
编辑 收藏
创建一个密钥文件,
%JAVA_HOME%/bin/keytool -genkey -alias myalias -keyalg RSA -validity 3650 -keystore ./mykeystorefilename
修改conf/server.xml,打开Tomcat的HTTPS端口,
<Connector port="8443"
maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
enableLookups="false" disableUploadTimeout="true"
acceptCount="100" debug="0" scheme="https" secure="true"
keystoreFile="mykeystorefilepath"
keystorePass="mykeystorepassword" >
<Factory clientAuth="false" protocol="TLS" />
</Connector>
在应用中修改web.xml,增加授权区
<security-constraint>
<web-resource-collection>
<url-pattern>/*</url-pattern>
<http-method>GET</http-method>
<http-method>POST</http-method>
</web-resource-collection>
<user-data-constraint>
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
</user-data-constraint>
</security-constraint>
posted @
2006-01-21 21:32 java小记 阅读(166) |
评论 (0) |
编辑 收藏
指定网络名称不可用
The specified network name is no longer available. : winnt_accept: Asynchronous AcceptEx failed.
或
An operation was attempted on something that is not a socket.:winnt_accept: AcceptEx failed. Attempting to recover.
出现这样的问题,解决方法是:禁止使用AcceptEx,方法为在httpd.conf文件中增加Win32DisableAcceptEx标记,例如:
<IfModule mpm_winnt.c>
..........
Win32DisableAcceptEx
.........
</IfModule>
虽然这样会使性能降低一些.
from apache docs2.0:
AcceptEx() is a Microsoft WinSock v2 API that provides some performance improvements over the use of
the BSD style accept() API in certain circumstances. Some popular Windows products, typically virus
scanning or virtual private network packages, have bugs that interfere with the proper operation of
AcceptEx(). If you encounter an error condition like:
[error] (730038)An operation was attempted on something that is not a socket.: winnt_accept: AcceptEx
failed. Attempting to recover.
you should use this directive to disable the use of AcceptEx().
posted @
2006-01-21 21:07 java小记 阅读(364) |
评论 (0) |
编辑 收藏
在tomcat配置文件server.xml的<Connector ... />配置中,有和连接数相关的参数:
<Connector
port="8080" maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
enableLookups="false" redirectPort="8443" acceptCount="100"
debug="0" connectionTimeout="20000"
disableUploadTimeout="true" />
web server允许的最大连接数还受制于操作系统的内核参数设置,通常Windows是2000个左右,Linux是1000个左右。
posted @
2006-01-05 13:29 java小记 阅读(642) |
评论 (0) |
编辑 收藏
在tomcat_home /conf/web.xml中,把listings参数设置成false即可
<servlet>
...
<init-param>
<param-name>listings</param-name>
<param-value>false</param-value>
</init-param>
...
</servlet>
posted @
2006-01-05 13:21 java小记 阅读(160) |
评论 (0) |
编辑 收藏
下面的这两个文件,尺寸差别很大.
%JAVA_HOME%/jre/bin/client/jvm.dll
%JAVA_HOME%/jre/bin/server/jvm.dll
Jvm动态库有client和server两个版本,分别针对桌面应用和服务器应用做了相应的优化,
client版本加载速度较快,server版本加载速度较慢但运行起来较快。
让Tomcat 使用Server版本的jvm吧,在开始菜单 tomcat5 ->tomcat config的java属性中有一项 jvm路径指向server目录下的jvm就行了。
更改默认java.exe调用的jvm.dll,这个由jvm.cfg决定。
编辑%JAVA_HOME%\jre\lib\i386\jvm.cfg
里面第一行写的是 -client 默认就是client版本 ,把第二行的-server KNOWN 放到第一行, 如下面所示
-server KNOWN
-client KNOWN
-hotspot ALIASED_TO -client
-classic WARN
-native ERROR
-green ERROR
改完保存,然后看看默认版本:
C:\java -version
java version "1.4.2_07"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.2_07-b05)
Java HotSpot(TM) Server VM (build 1.4.2_07-b05, mixed mode)
看到有 Server VM 字样
posted @
2006-01-05 13:09 java小记 阅读(244) |
评论 (0) |
编辑 收藏
内存不足 (OOM) - 由于 java 堆或本地内存中的内存耗尽,应用程序显示“内存不足”错误。
内存泄漏 - java 堆或本地内存的持续内存增长,最终将导致内存不足状态。调试内存泄漏状态的技术与调试内存不足状态的技术相同。
Java 堆 - 这是 JVM 用来分配 java 对象的内存。java 堆内存的最大值用 java 命令行中的 -Xmx 标志来指定。
如果未指定最大的堆大小,那么该极限值由 JVM 根据诸如计算机中的物理内存量和该时刻的可用空闲内存量这类
因素来决定。始终建议您指定最大的 java 堆值。
本地内存 - 这是 JVM 用于其内部操作的内存。JVM 将使用的本地内存堆数量取决于生成的代码量、创建的线程、
GC 期间用于保存 java 对象信息的内存,以及在代码生成、优化等过程中使用的临时空间。
如果有一个第三方本地模块,那么它也可能使用本地内存。例如,本地 JDBC 驱动程序将分配本地内存。
最大本地内存量受到任何特定操作系统上的虚拟进程大小限制的约束,也受到用 -Xmx 标志指定用于 java 堆的内存量的限制。
例如,如果应用程序能分配总计为 3 GB 的内存量,并且最大 java 堆的大小为 1 GB,那么本地内存量的最大值可能在 2 GB 左右。
进程大小 - 进程大小将是 java 堆、本地内存与加载的可执行文件和库所占用内存的总和。在 32 位操作系统上,进程的虚拟地址
空间最大可达到 4 GB。从这 4 GB 内存中,操作系统内核为自己保留一部分内存(通常为 1 - 2 GB)。剩余内存可用于应用程序。
Windows缺省情况下,2 GB 可用于应用程序,剩余 2 GB 保留供内核使用。但是,在 Windows 的一些变化版本中,有一个 /3GB 开关
可用于改变该分配比率,使应用程序能够获得 3 GB。有关 /3GB 开关的详细信息,可以在以下网址中找到:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/ddtools/hh/ddtools/bootini_1fcj.asp
RH Linux AS 2.1 - 3 GB 可用于应用程序。
进程地址空间和物理内存之间的差异:
每个进程都获得其自有的地址空间。在 32 位操作系统中,此地址空间范围为 0 到 4 GB。此范围与计算机的可用随机存取内存 (RAM)
或交换空间无关。计算机中的可用物理内存总量是该计算机上的可用 RAM 和交换空间之和。所有运行的进程共享这些物理内存。
进程内的存储地址是虚拟地址。内核将此虚拟地址映射到物理地址上。物理地址指向物理内存中的某个位置。在任一给定时间,计算机
中运行进程所使用的全部虚拟内存的总和不能超过该计算机上可用物理内存的总量。
为什么会发生 OOM 问题,JVM 在这种情况下如何处理?
如果 JVM 不能在 java 堆中获得更多内存来分配更多 java 对象,将会抛出 java 内存不足 (java OOM) 错误。如果 java 堆充满了活
动对象,并且 JVM 无法再扩展 java 堆,那么它将不能分配更多 java 对象。
在这种情况下,JVM 让应用程序决定在抛出 java.lang.OutOfMemoryError 后该执行什么操作。例如,应用程序可以处理此错误,并决定
以安全方式自行关闭或决定忽略此错误。如果应用程序不处理此错误,那么抛出此错误的线程将退出(如果您进行 java Thread Dump,那
么将看不到该线程)。
在使用 Weblogic Server 的情况下,如果此错误是由某个执行线程抛出的,则会处理此错误并将其记录在日志中。如果连续抛出此错误,
那么核心运行状况监视器线程将关闭 Weblogic Server。
如果 JVM 无法获得更多本地内存,它将抛出本地内存不足(本地 OOM)错误。当进程到达操作系统的进程大小限值,或者当计算机用完
RAM 和交换空间时,通常会发生这种情况。当发生这种情况时,JVM 处理本地 OOM 状态,记录说明它已用完本地内存或无法获得内存的
消息,然后退出。如果 JVM 或加载的任何其它模块(如 libc 或第三方模块)不处理这个本地 OOM 状态,那么操作系统将给 JVM 发送
命令 JVM 退出的 sigabort 信号。通常情况下,JVM 收到 sigabort 信号时将会生成一个核心文件。
排除故障的步骤:
确定是 Java OOM 还是本地 OOM:
如果 stdout/stderr 消息说明这是一个 java.lang.OutOfMemoryError,那么这就是 Java OOM
如果 stdout/stderr 消息说明无法获得内存,那么这就是本地 OOM
请注意,上述消息仅发送到 stdout 或 stderr 中,而不发送到应用程序特定的日志文件(如 weblogic.log)
对于 Java OOM,收集和分析 verbose:gc 输出 在 java 命令行中添加“-verbose:gc”标志。这样将会把 GC 活动信息打印到
stdout/stderr。将 stdout/stderr 重定向到一个文件。运行应用程序,直到该问题重现。 确保 JVM 在抛出 java OOM 之
前完成下列任务,执行一次完整 GC 运行,并且删除了所有不可及对象以及虚可及、弱可及、软可及对象,并回收了那些空间。
有关不同级别的对象可及性的详细信息,可以在以下网址中可找到:
http://java.sun.com/developer/technicalArticles/ALT/RefObj
您可以检查是否在发出 OOM 消息之前执行了完整 GC 运行。当完成一次完整 GC 运行时,将会打印类似如下消息(格式取决
于 JVM - 请查看 JVM 帮助信息以了解有关格式)
[memory ] 7.160: GC 131072K->130052K (131072K) in 1057.359 ms
以上输出的格式如下(备注:在此模式下将全部使用相同的格式):
[memory ] <start>: GC <before>K-><after>K (<heap>K), <total> ms
[memory ] <start> - start time of collection (seconds since jvm start)
[memory ] <before> - memory used by objects before collection (KB)
[memory ] <after> - memory used by objects after collection (KB)
[memory ] <heap> - size of heap after collection (KB)
[memory ] <total> - total time of collection (milliseconds)
但是,没有办法断定是否使用 verbose 消息删除了软/弱/虚可及的对象。如果您怀疑在抛出 OOM 时这些对象仍然存在,请
与 JVM 供应商联系。
如果垃圾回收算法是一种按代回收算法(对于 Jrockit 为 gencopy 或 gencon,对于其它 JDK 则是缺省算法),您也将看
到类似如下的 verbose 输出:
[memory ] 2.414: Nursery GC 31000K->20760K (75776K), 0.469 ms
以上是 nursery GC(即 young GC)周期,它将把活动对象从 nursery(或 young 空间)提升到 old 空间。这个周期对我
们的分析不重要。有关按代回收算法的详细信息,可以在 JVM 文档中找到。
如果在 java OOM 之前未发生 GC 周期,那么这是一个 JVM 错误。
完全压缩:
确保 JVM 执行了适当的压缩工作,并且内存并未成碎片(否则会阻止分配大对象并触发 java OOM 错误)。
Java 对象要求内存是连续的。如果可用空闲内存是一些碎片,那么 JVM 将无法分配大对象,因为它可能无法放入任何可用
空闲内存块中。在这种情况下,JVM 将执行一次完全压缩,以便形成更多连续的空闲内存来容纳大对象。
压缩工作包括在 java 堆内存中将对象从一个位置移动到另一个位置,以及更新对这些对象的引用以指向新位置。除非确有
必要,否则 JVM 不会压缩所有对象。这是为了减少 GC 周期的暂停时间。
我们可以通过分析 verbose gc 消息来检查 java OOM 是否由碎片引起。如果您看到类似如下的输出(在此无论是否有可用
的空闲 java 堆都会抛出 OOM),那么这就是由碎片引起的。
[memory ] 8.162: GC 73043K->72989K (131072K) in 12.938 ms
[memory ] 8.172: GC 72989K->72905K (131072K) in 12.000 ms
[memory ] 8.182: GC 72905K->72580K (131072K) in 13.509 ms
java.lang.OutOfMemoryError
在上述情况中您可以看到,所指定的最大堆内存是 128MB,并且当实际内存使用量仅为 72580K 时,JVM 抛出 OOM。堆使用量
仅为 55%。因此在这种情况下,碎片影响是:即使还有 45% 的空闲堆,内存也会抛出 OOM。这是一个 JVM 错误或缺陷。您应
当与 JVM 供应商联系。
如果 JVM 一切都正常(上一步中提到的所有操作),那么此 java OOM 可能是应用程序的问题。应用程序可能在不断泄漏一些
java 内存,而这可能导致出现上述问题。或者,应用程序使用更多的活动对象,因此它需要更多 java 堆内存。在应用程序中
可以检查以下方面: 应用程序中的缓存功能 - 如果应用程序在内存中缓存 java 对象,则应确保此缓存并没有不断增大。对
缓存中的对象数应有一个限值。我们可以尝试减少此限值,来观察其是否降低 java 堆使用量。 Java 软引用也可用于数据缓存,
当 JVM 用完 java 堆时,可以保证删除软可及对象。
长期活动对象 - 如果应用程序中有长期活动对象,则可以尝试尽可能减少这些对象的存在期。例如,调整 HTTP 会话超时值将
有助于更快地回收空闲会话对象。
内存泄漏 - 内存泄漏的一个例子是在应用服务器中使用数据库连接池。当使用连接池时,必须在 finally 块中显式关闭 JDBC
语句和结果集对象。这是因为,当从池中调用连接对象上的 close() 时,只是简单地把连接返回池中以供重用,并没有实际关闭
连接和关联的语句/结果集对象。
增加 java 堆 - 如果可能的话,我们也可尝试增加 java 堆,以观察是否能解决问题。
如果上述建议都不适用于该应用程序,那么,我们需要使用一个基于 JVMPI(JVM 事件探查器接口)的事件探查器(如 Jprobe 或
OptimizeIt)来找出哪些对象正在占用 java 堆。事件探查器还提供 java 代码中正在创建这些对象的位置的详细信息。本文档并
不介绍每个事件探查器的详细信息。可以参考事件探查器文档来了解如何用事件探查器设置和启动应用程序。一般而言,基于 JVMPI
的事件探查器需要较高的系统开销,并会大大降低应用程序的性能。因此,在生产环境中使用这些事件探查器并不可取。
http://www.borland.com/optimizeit
http://www.quest.com/jprobe
对于本地 OOM 问题:
收集下列信息: .verbosegc 输出,通过它可监视 java 堆使用量。这样将有助于了解此应用程序的 java 内存要求。
应当注意,指定的最大堆内存量(在 java 命令行中使用 Xmx 标志)与应用程序的实际 java 堆使用量无关,其在 JVM 启动时被保留,
并且此保留内存不能用于其它任何用途。
在使用 Jrockit 时,使用 -verbose 来代替 -verbosegc,因为这可以提供 codegen 信息以及 GC 信息。
定期记录进程虚拟内存大小,从启动应用程序时起直到 JVM 用完本地内存。这样将有助于了解此进程是否确实达到该操作系统的大小限
值。在 Windows 环境下,使用下列步骤来监视虚拟进程大小:
在“开始” -> “运行”对话框中,输入“perfmon”并单击“确定”。
在弹出的“性能”窗口中,单击“+”按钮(图表上部)。
在显示的对话框中选择下列选项:
性能对象:进程(不是缺省的处理器)
从列表中选择计数器:虚拟字节数
从列表中选择实例:选择 JVM (java) 实例
单击“添加”,然后单击“关闭”
在 Unix 或 Linux 环境下,对于一个给定 PID,可以使用以下命令来查找虚拟内存大小 - ps -p <PID> -o vsz。
在 Linux 环境下,单个 JVM 实例内的每个 java 线程都显示为一个独立的进程。如果我们获得根 java 进程的 PID,那么这就足够了。
可以使用 ps 命令的 .forest 选项来找到根 java 进程。例如,ps lU <user> --forest 将提供一个由指定用户启动的所有进程的 ASCII
树图。您可以从该树图中找到根 java。
计算机中的内存可用性
如果计算机没有足够的 RAM 和交换空间,则操作系统将不能为此进程提供更多内存,这样也会导致内存不足。请确保 RAM 与磁盘中的交换
空间之和足以满足该计算机中正在运行的所有进程的需要。
调整 java 堆
如果 java 堆使用量完全在最大堆范围内,则减小 java 最大堆将为 JVM 提供更多的本地内存。这不是一个解决办法,而是一个可尝试的变
通方法。由于操作系统限制进程大小,我们需要在 java 堆和本地堆之间寻求一个平衡。
JVM 的本地内存使用量
在加载了所有类并调用了方法(代码生成结束)后,JVM 的本地内存用量预计将会几乎达到稳定。对于大多数应用程序而言,这通常发生在
最初几小时内。此后,JVM 可能会因加载运行时类型、生成优化代码等处理而仅使用少量本地内存。
为了缩小问题的范围,可尝试禁用运行时优化,并检查这是否会产生任何效果。
在使用 Jrockit 时,可使用 -Xnoopt 标志来禁用运行时优化。
在使用 SUN hotspot JVM 时,-Xint 标志将强迫 JVM 在解释模式中运行(不生成代码)。
如果在整个运行过程中,本地内存使用量继续不断增加,那么这可能是本地代码中的内存泄漏。
第三方本地模块或应用程序中的 JNI 代码
检查您是否在使用类似数据库驱动程序的任何第三方本地模块。这些本地模块也可以分配本地内存,泄漏可能从这些模块中发生。为了缩
小问题的范围,应尝试在没有这些第三方模块的情况下重现问题。例如,可以使用纯 java 驱动程序来代替本地数据库驱动程序。
检查应用程序是否使用一些 JNI 代码。这也可能造成本地内存泄漏,如果可能的话,您可以尝试在没有 JNI 代码的情况下运行应用程序。
如果在执行上述步骤后还不能找到本地内存泄漏的根源,那么您需要与 JVM 供应商合作来获得一个特殊的编译版本,它可以跟踪本地内存
分配调用,并可提供有关泄漏的更多信息。
Jrockit 特定特性
Jrockit 81SP1 和更高版本支持 JRA 记录(Java 运行时间分析器)。这对于收集 JVM 运行时的信息很有用,将提供应用程序的有关信息,
例如,正在运行的 GC 数、软/弱/虚引用的数目、热方法,等等。如果 JVM 出现性能问题或挂起问题,那么用几分钟进行记录和分析数据
就会很有用。有关详细信息,可以在 Jrockit 文档中找到。
http://e-docs.bea.com/wljrockit/docs142/userguide/jra.html
如果您已经理解这个模式,但仍需要其它帮助,您可以:
在 http://support.bea.com 上查询 AskBEA(例如使用“out of memory”),以查找其它已发布的解决方案。
在 http://support.bea.com 上,向 BEA 的某个新闻组中提出更详细具体的问题。
如果这还不能解决您的问题,并且您拥有有效的技术支持合同,您可以通过登录以下网站来打开支持案例:http://support.bea.com。
posted @
2005-12-21 21:48 java小记 阅读(408) |
评论 (0) |
编辑 收藏