You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

重型批处理任务引发Oracle Protocol Violation与Java堆内存溢出异常的因果关系排查问询

排查Java堆内存溢出与Oracle Protocol Violation的因果关系

问题背景

你负责的批处理任务在执行过程中先后抛出了Java堆内存溢出和Oracle协议违反的异常,现在需要明确两者的因果关系和问题根源。

异常日志详情

2021-05-08 13:55:52,190 ERROR TransactionAspectSupport - 应用异常被回滚异常覆盖
java.lang.OutOfMemoryError: Java heap space
	at oracle.jdbc.driver.T4CBlobAccessor.checkAndAllocateLobPrefetchMemory(T4CBlobAccessor.java:319)
	at oracle.jdbc.driver.T4CBlobAccessor.handlePrefetch(T4CBlobAccessor.java:504)
	at oracle.jdbc.driver.T4CBlobAccessor.unmarshalOneRow(T4CBlobAccessor.java:191)
	at oracle.jdbc.driver.T4CTTIrxd.unmarshal(T4CTTIrxd.java:934)
	at oracle.jdbc.driver.T4CTTIrxd.unmarshal(T4CTTIrxd.java:853)
	at oracle.jdbc.driver.T4C8Oall.readRXD(T4C8Oall.java:699)
	at oracle.jdbc.driver.T4CTTIfun.receive(T4CTTIfun.java:337)
	at oracle.jdbc.driver.T4CTTIfun.doRPC(T4CTTIfun.java:191)
	at oracle.jdbc.driver.T4C8Oall.doOALL(T4C8Oall.java:523)
	at oracle.jdbc.driver.T4CPreparedStatement.doOall8(T4CPreparedStatement.java:207)
	at oracle.jdbc.driver.T4CPreparedStatement.executeForRows(T4CPreparedStatement.java:1010)
	at oracle.jdbc.driver.OracleStatement.executeMaybeDescribe(OracleStatement.java:1185)
	at oracle.jdbc.driver.OracleStatement.doExecuteWithTimeout(OracleStatement.java:1275)
	at oracle.jdbc.driver.OraclePreparedStatement.executeInternal(OraclePreparedStatement.java:3576)
	at oracle.jdbc.driver.OraclePreparedStatement.executeQuery(OraclePreparedStatement.java:3620)
	at oracle.jdbc.driver.OraclePreparedStatementWrapper.executeQuery(OraclePreparedStatementWrapper.java:1491)
	at com.mchange.v2.c3p0.impl.NewProxyPreparedStatement.executeQuery(NewProxyPreparedStatement.java:76)
	at org.hibernate.jdbc.AbstractBatcher.getResultSet(AbstractBatcher.java:208)
	at org.hibernate.loader.Loader.getResultSet(Loader.java:1953)
	at org.hibernate.loader.Loader.doQuery(Loader.java:802)
	at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:274)
	at org.hibernate.loader.Loader.doList(Loader.java:2542)
	at org.hibernate.loader.Loader.listIgnoreQueryCache(Loader.java:2276)
	at org.hibernate.loader.Loader.list(Loader.java:2271)
	at org.hibernate.loader.criteria.CriteriaLoader.list(CriteriaLoader.java:119)
	at org.hibernate.impl.SessionImpl.list(SessionImpl.java:1716)
	at org.hibernate.impl.CriteriaImpl.list(CriteriaImpl.java:347)
	at com.***.property.dao.hibernate.CompanySystemPropertyHibernateDao.retrieveCompanySystemProperty(CompanySystemPropertyHibernateDao.java:107)
	at sun.reflect.GeneratedMethodAccessor574.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:606)
	at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:317)

2021-05-08 14:05:46,590 ERROR TaskUtils$LoggingErrorHandler - 定时任务中发生意外错误。
org.springframework.transaction.TransactionSystemException: 无法回滚Hibernate事务;嵌套异常为org.hibernate.TransactionException: JDBC回滚失败
	at org.springframework.orm.hibernate3.HibernateTransactionManager.doRollback(HibernateTransactionManager.java:680)
	at org.springframework.transaction.support.AbstractPlatformTransactionManager.processRollback(AbstractPlatformTransactionManager.java:846)
	at org.springframework.transaction.support.AbstractPlatformTransactionManager.rollback(AbstractPlatformTransactionManager.java:823)
	at org.springframework.transaction.interceptor.TransactionAspectSupport.completeTransactionAfterThrowing(TransactionAspectSupport.java:493)
	at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:264)
	at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:94)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
	at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:204)
	at com.sun.proxy.$Proxy68.refresh(Unknown Source)
	at sun.reflect.GeneratedMethodAccessor571.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:606)
	at org.springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:64)
	at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:53)
	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471)
	at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:304)
	at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$301(ScheduledThreadPoolExecutor.java:178)
	at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
	at java.lang.Thread.run(Thread.java:745)
Caused by: org.hibernate.TransactionException: JDBC回滚失败
	at org.hibernate.transaction.JDBCTransaction.rollback(JDBCTransaction.java:200)
	at org.springframework.orm.hibernate3.HibernateTransactionManager.doRollback(HibernateTransactionManager.java:677)
	... 20 more
Caused by: java.sql.SQLException: Protocol violation
	at oracle.jdbc.driver.T4CTTIfun.receive(T4CTTIfun.java:459)
	at oracle.jdbc.driver.T4CTTIfun.doRPC(T4CTTIfun.java:191)
	at oracle.jdbc.driver.T4C7Ocommoncall.doOROLLBACK(T4C7Ocommoncall.java:68)
	at oracle.jdbc.driver.T4CConnection.doRollback(T4CConnection.java:649)
	at oracle.jdbc.driver.PhysicalConnection.rollback(PhysicalConnection.java:3893)
	at com.mchange.v2.c3p0.impl.NewProxyConnection.rollback(NewProxyConnection.java:855)
	at org.hibernate.transaction.JDBCTransaction.rollbackAndResetAutoCommit(JDBCTransaction.java:213)
	at org.hibernate.transaction.JDBCTransaction.rollback(JDBCTransaction.java:192)
	... 21 more

根源与因果关系分析

咱们从时间线和异常栈细节来拆解:

  1. 第一个异常(13:55:52):Java堆内存溢出
    异常发生在T4CBlobAccessor.checkAndAllocateLobPrefetchMemory方法,说明JDBC驱动在尝试预取Blob数据时,JVM无法分配足够的堆内存。这是初始触发点——你的批处理任务在查询包含Blob字段的数据时,一次性加载了过多的Blob内容到内存,直接导致堆内存耗尽。

  2. 第二个异常(14:05:46):Oracle Protocol Violation
    这个异常是OOM引发的连锁反应。当JVM发生内存溢出时,不仅当前的查询操作失败,还会破坏JDBC连接的内部状态(比如网络缓冲区、会话变量)。后续Spring框架尝试回滚事务时,异常的连接与Oracle服务器通信出现协议不匹配,最终抛出"Protocol violation"错误。

简单总结:Java堆内存溢出是因,Oracle协议违反是果。OOM导致JDBC连接失效,进而引发回滚阶段的通信错误。

解决方案建议

针对这个问题,给你几个具体的排查和修复方向:

  • 调整Blob预取配置:Oracle JDBC驱动默认会预取Lob数据,你可以检查oracle.jdbc.defaultLobPrefetchSize参数,把它设置为较小值(比如1024),或者直接关闭预取(设置为-1),避免一次性加载大Blob到内存。
  • 优化JVM堆内存:临时可以增大JVM堆内存参数(比如-Xmx2g,根据实际场景调整),但这只是治标,核心还是要优化数据加载逻辑。
  • 修改查询逻辑:不要一次性查询大量包含Blob的记录,采用分页查询或流式处理;如果业务不需要Blob内容,就不要在查询中包含该字段。
  • 排查内存泄漏:使用JVM工具(如jmap、jstack、VisualVM)分析内存快照,确认是否有Blob对象或其他大对象未被及时回收,导致内存占用过高。
  • 优化批处理逻辑:确保批处理过程中,处理完的对象能被GC及时回收,比如清空不再使用的集合、及时关闭Hibernate Session等。

内容的提问来源于stack exchange,提问作者Olgun Kaya

火山引擎 最新活动