重型批处理任务引发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
根源与因果关系分析
咱们从时间线和异常栈细节来拆解:
第一个异常(13:55:52):Java堆内存溢出
异常发生在T4CBlobAccessor.checkAndAllocateLobPrefetchMemory方法,说明JDBC驱动在尝试预取Blob数据时,JVM无法分配足够的堆内存。这是初始触发点——你的批处理任务在查询包含Blob字段的数据时,一次性加载了过多的Blob内容到内存,直接导致堆内存耗尽。第二个异常(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




