栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 面试经验 > 面试问答

由于多次搜索而导致OutOfMemoryError

面试问答 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

由于多次搜索而导致OutOfMemoryError

实施的解决方案最终看起来像这样:

一般的想法是维护当前正在运行的查询的所有Hibernate会话到发起它们的用户的HttpSession的映射,这样,当用户关闭浏览器时,我们将能够杀死正在运行的查询。

这里要克服两个主要挑战。一种是将HTTP会话ID从Web层传播到EJB层,而不会干扰沿途的所有方法调用-
即不篡改系统中的现有代码。第二个挑战是弄清楚一旦数据库已经开始返回结果并且Hibernate用结果填充对象,如何取消查询。

根据我们的认识,第一个问题得以克服,因为我们意识到沿着堆栈调用的所有方法都由同一线程处理。这很有意义,因为我们的应用程序全部存在于一个容器中,并且没有任何远程调用。在这种情况下,我们创建了一个Servlet过滤器来拦截对应用程序的每次调用,并添加

ThreadLocal
带有当前HTTP会话ID
的变量。这样,HTTP会话ID将可沿行的每个方法调用使用。

第二个挑战是更具粘性。我们发现,负责运行查询并随后填充POJO的Hibernate方法已被调用

doQuery
并位于
org.hibernate.loader.Loader.java
该类中。(我们碰巧使用的是Hibernate
3.5.3,但对于较新版本的Hibernate也是如此。):

private List doQuery(        final SessionImplementor session,        final QueryParameters queryParameters,        final boolean returnProxies) throws SQLException, HibernateException {    final RowSelection selection = queryParameters.getRowSelection();    final int maxRows = hasMaxRows( selection ) ? selection.getMaxRows().intValue() : Integer.MAX_VALUE;    final int entitySpan = getEntityPersisters().length;    final ArrayList hydratedObjects = entitySpan == 0 ? null : new ArrayList( entitySpan * 10 );    final PreparedStatement st = prepareQueryStatement( queryParameters, false, session );    final ResultSet rs = getResultSet( st, queryParameters.hasAutoDiscoverScalarTypes(), queryParameters.isCallable(), selection, session );    final EntityKey optionalObjectKey = getOptionalObjectKey( queryParameters, session );    final LockMode[] lockModesArray = getLockModes( queryParameters.getLockOptions() );    final boolean createSubselects = isSubselectLoadingEnabled();    final List subselectResultKeys = createSubselects ? new ArrayList() : null;    final List results = new ArrayList();    try {        handleEmptyCollections( queryParameters.getCollectionKeys(), rs, session );        EntityKey[] keys = new EntityKey[entitySpan]; //we can reuse it for each row        if ( log.isTraceEnabled() ) log.trace( "processing result set" );        int count;        for ( count = 0; count < maxRows && rs.next(); count++ ) { if ( log.isTraceEnabled() ) log.debug("result set row: " + count); Object result = getRowFromResultSet(          rs,         session,         queryParameters,         lockModesArray,         optionalObjectKey,         hydratedObjects,         keys,         returnProxies  ); results.add( result ); if ( createSubselects ) {     subselectResultKeys.add(keys);     keys = new EntityKey[entitySpan]; //can't reuse in this case }        }        if ( log.isTraceEnabled() ) { log.trace( "done processing result set (" + count + " rows)" );        }    }    finally {        session.getBatcher().closeQueryStatement( st, rs );    }    initializeEntitiesAndCollections( hydratedObjects, rs, session, queryParameters.isReadonly( session ) );    if ( createSubselects ) createSubselects( subselectResultKeys, queryParameters, session );    return results; //getResultList(results);}

在此方法中,您可以看到,首先以老式的形式从数据库中获取结果

java.sql.ResultSet
,然后在每个集合上循环运行并从中创建一个对象。
initializeEntitiesAndCollections()
在循环之后调用的方法中执行一些其他初始化。经过一点调试之后,我们发现大部分性能开销都在方法的这些部分中,而不是在
java.sql.ResultSet
从数据库中获取的部分中,但是该
cancelQuery
方法仅在第一部分有效。因此,解决方案是在for循环中添加一个附加条件,以检查线程是否被中断,如下所示:

for ( count = 0; count < maxRows && rs.next() && !currentThread.isInterrupted(); count++ ) {// ...}

以及在调用

initializeEntitiesAndCollections()
方法之前执行相同的检查:

if (!Thread.interrupted()) {    initializeEntitiesAndCollections(hydratedObjects, rs, session,     queryParameters.isReadonly(session));    if (createSubselects) {        createSubselects(subselectResultKeys, queryParameters, session);    }}

另外,通过

Thread.interrupted()
在第二个检查中调用,该标志被清除,并且不影响程序的进一步功能。现在,当要取消查询时,取消方法将使用HTTP
session-id作为键访问Hibernate会话和存储在映射中的线程
cancelQuery
,在会话上调用该
interrupt
方法,然后调用该线程的方法。



转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/412939.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号