Closed cbqqkcel closed 9 months ago
查询数据库时报错了
这个是你自己打的 jar 包?
是的
是基于什么时候的代码?你没有改动过吧?
我看这个异常,是不是通过嵌入式的方式使用数据库的?堆栈没有看到调度服务线程的信息。
没有改过代码,是嵌入式的。
java.lang.IllegalStateException: Position 0 [5.2.0/6]
at org.lealone.common.util.DataUtils.newIllegalStateException(DataUtils.java:616) ~[lealone-common-5.2.0.jar!/:na]
at org.lealone.storage.aose.btree.BTreeStorage.readPage(BTreeStorage.java:165) ~[lealone-aose-5.2.0.jar!/:na]
at org.lealone.storage.aose.btree.page.PageReference.readPage(PageReference.java:199) ~[lealone-aose-5.2.0.jar!/:na]
at org.lealone.storage.aose.btree.page.PageReference.getOrReadPage(PageReference.java:175) ~[lealone-aose-5.2.0.jar!/:na]
at org.lealone.storage.aose.btree.page.NodePage.getChildPage(NodePage.java:51) ~[lealone-aose-5.2.0.jar!/:na]
at org.lealone.storage.aose.btree.BTreeCursor.hasNext(BTreeCursor.java:78) ~[lealone-aose-5.2.0.jar!/:na]
at org.lealone.storage.aose.btree.BTreeCursor.next(BTreeCursor.java:56) ~[lealone-aose-5.2.0.jar!/:na]
at org.lealone.transaction.aote.AOTransactionMap$2.next(AOTransactionMap.java:290) ~[lealone-aote-5.2.0.jar!/:na]
at org.lealone.db.index.standard.StandardPrimaryIndex$StandardPrimaryIndexCursor.next(StandardPrimaryIndex.java:496) ~[lealone-db-5.2.0.jar!/:na]
at org.lealone.sql.optimizer.IndexCursor.next(IndexCursor.java:263) ~[lealone-sql-5.2.0.jar!/:na]
at org.lealone.sql.optimizer.TableFilter.next(TableFilter.java:357) ~[lealone-sql-5.2.0.jar!/:na]
at org.lealone.sql.optimizer.TableIterator.next(TableIterator.java:46) ~[lealone-sql-5.2.0.jar!/:na]
at org.lealone.sql.query.QOperator.next(QOperator.java:76) ~[lealone-sql-5.2.0.jar!/:na]
at org.lealone.sql.query.QFlat.run(QFlat.java:19) ~[lealone-sql-5.2.0.jar!/:na]
at org.lealone.sql.query.YieldableSelect.executeInternal(YieldableSelect.java:95) ~[lealone-sql-5.2.0.jar!/:na]
at org.lealone.sql.executor.YieldableBase.run(YieldableBase.java:115) ~[lealone-sql-5.2.0.jar!/:na]
at org.lealone.sql.StatementBase.syncExecute(StatementBase.java:507) ~[lealone-sql-5.2.0.jar!/:na]
at org.lealone.sql.StatementBase.executeQuery(StatementBase.java:527) ~[lealone-sql-5.2.0.jar!/:na]
at org.lealone.client.jdbc.JdbcStatement.executeQuerySQLCommand(JdbcStatement.java:376) ~[lealone-client-5.2.0.jar!/:na]
at org.lealone.client.jdbc.JdbcPreparedStatement.execute(JdbcPreparedStatement.java:308) ~[lealone-client-5.2.0.jar!/:na]
at org.apache.ibatis.executor.statement.PreparedStatementHandler.query(PreparedStatementHandler.java:65) ~[mybatis-3.5.13.jar!/:3.5.13]
at cn.com.idmy.orm.core.mybatis.FlexStatementHandler.lambda$query$2(FlexStatementHandler.java:100) ~[core-1.3.5.jar!/:1.3.5]
at cn.com.idmy.orm.core.audit.AuditManager.startAudit(AuditManager.java:104) ~[core-1.3.5.jar!/:1.3.5]
at cn.com.idmy.orm.core.mybatis.FlexStatementHandler.query(FlexStatementHandler.java:100) ~[core-1.3.5.jar!/:1.3.5]
at org.apache.ibatis.executor.SimpleExecutor.doQuery(SimpleExecutor.java:65) ~[mybatis-3.5.13.jar!/:3.5.13]
at org.apache.ibatis.executor.BaseExecutor.queryFromDatabase(BaseExecutor.java:333) ~[mybatis-3.5.13.jar!/:3.5.13]
at org.apache.ibatis.executor.BaseExecutor.query(BaseExecutor.java:158) ~[mybatis-3.5.13.jar!/:3.5.13]
at org.apache.ibatis.executor.CachingExecutor.query(CachingExecutor.java:110) ~[mybatis-3.5.13.jar!/:3.5.13]
at org.apache.ibatis.executor.CachingExecutor.query(CachingExecutor.java:90) ~[mybatis-3.5.13.jar!/:3.5.13]
at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:154) ~[mybatis-3.5.13.jar!/:3.5.13]
at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:147) ~[mybatis-3.5.13.jar!/:3.5.13]
at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:142) ~[mybatis-3.5.13.jar!/:3.5.13]
at jdk.internal.reflect.GeneratedMethodAccessor61.invoke(Unknown Source) ~[na:na]
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:425) ~[mybatis-spring-3.0.1.jar!/:3.0.1]
at jdk.proxy2/jdk.proxy2.$Proxy77.selectList(Unknown Source) ~[na:na]
at org.mybatis.spring.SqlSessionTemplate.selectList(SqlSessionTemplate.java:224) ~[mybatis-spring-3.0.1.jar!/:3.0.1]
at org.apache.ibatis.binding.MapperMethod.executeForMany(MapperMethod.java:147) ~[mybatis-3.5.13.jar!/:3.5.13]
at org.apache.ibatis.binding.MapperMethod.execute(MapperMethod.java:80) ~[mybatis-3.5.13.jar!/:3.5.13]
at org.apache.ibatis.binding.MapperProxy$PlainMethodInvoker.invoke(MapperProxy.java:142) ~[mybatis-3.5.13.jar!/:3.5.13]
at org.apache.ibatis.binding.MapperProxy.invoke(MapperProxy.java:86) ~[mybatis-3.5.13.jar!/:3.5.13]
at jdk.proxy2/jdk.proxy2.$Proxy86.selectListByQuery(Unknown Source) ~[na:na]
at cn.com.idmy.orm.core.util.MapperUtil.doPaginate(MapperUtil.java:167) ~[core-1.3.5.jar!/:1.3.5]
at cn.com.idmy.orm.core.BaseDao.paginateAs(BaseDao.java:1015) ~[core-1.3.5.jar!/:1.3.5]
at java.base/java.lang.invoke.MethodHandle.invokeWithArguments(MethodHandle.java:732) ~[na:na]
at org.apache.ibatis.binding.MapperProxy$DefaultMethodInvoker.invoke(MapperProxy.java:155) ~[mybatis-3.5.13.jar!/:3.5.13]
at org.apache.ibatis.binding.MapperProxy.invoke(MapperProxy.java:86) ~[mybatis-3.5.13.jar!/:3.5.13]
at jdk.proxy2/jdk.proxy2.$Proxy86.paginateAs(Unknown Source) ~[na:na]
at cn.com.idmy.orm.core.BaseDao.paginate(BaseDao.java:939) ~[core-1.3.5.jar!/:1.3.5]
at java.base/java.lang.invoke.MethodHandle.invokeWithArguments(MethodHandle.java:732) ~[na:na]
at org.apache.ibatis.binding.MapperProxy$DefaultMethodInvoker.invoke(MapperProxy.java:155) ~[mybatis-3.5.13.jar!/:3.5.13]
at org.apache.ibatis.binding.MapperProxy.invoke(MapperProxy.java:86) ~[mybatis-3.5.13.jar!/:3.5.13]
at jdk.proxy2/jdk.proxy2.$Proxy86.paginate(Unknown Source) ~[na:na]
at jdk.internal.reflect.GeneratedMethodAccessor172.invoke(Unknown Source) ~[na:na]
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
at cn.com.idmy.orm.core.mybatis.MapperInvocationHandler.invoke(MapperInvocationHandler.java:82) ~[core-1.3.5.jar!/:1.3.5]
at jdk.proxy2/jdk.proxy2.$Proxy86.paginate(Unknown Source) ~[na:na]
at cn.com.idmy.cloud.service.impl.DefaultServiceImpl.page(DefaultServiceImpl.java:224) ~[service-1.0.0.jar!/:1.0.0]
at cn.com.idmy.zhdc.pa.admin.service.impl.RoomServiceImpl.page(RoomServiceImpl.java:37) ~[classes!/:1.0.0]
at cn.com.idmy.cloud.service.ReadService.page(ReadService.java:124) ~[service-1.0.0.jar!/:1.0.0]
at jdk.internal.reflect.GeneratedMethodAccessor171.invoke(Unknown Source) ~[na:na]
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:343) ~[spring-aop-6.0.11.jar!/:6.0.11]
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:196) ~[spring-aop-6.0.11.jar!/:6.0.11]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-6.0.11.jar!/:6.0.11]
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:756) ~[spring-aop-6.0.11.jar!/:6.0.11]
at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:123) ~[spring-tx-6.0.11.jar!/:6.0.11]
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:391) ~[spring-tx-6.0.11.jar!/:6.0.11]
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119) ~[spring-tx-6.0.11.jar!/:6.0.11]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) ~[spring-aop-6.0.11.jar!/:6.0.11]
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:756) ~[spring-aop-6.0.11.jar!/:6.0.11]
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:97) ~[spring-aop-6.0.11.jar!/:6.0.11]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) ~[spring-aop-6.0.11.jar!/:6.0.11]
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:756) ~[spring-aop-6.0.11.jar!/:6.0.11]
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:708) ~[spring-aop-6.0.11.jar!/:6.0.11]
at cn.com.idmy.zhdc.pa.admin.service.impl.RoomServiceImpl$$SpringCGLIB$$0.page(
这个表新的数据有问题,不查询新的数据不会出错。
用6.0.0-SNAPSHOT读这个数据还是一样的。估计是数据坏了
这个表的主键特别长,还有中文
如果直接内嵌在 tomcat 的容器里跑,并发读写 lealone 用的是 tomcat 的线程,这时就绕过 lealone 内部的调度服务线程了。嵌入式环境使用 lealone 然后用容器的并发线程读写会有并发访问的问题,这个我之前没有考虑到,我以为嵌入式场景是单线程的。
lealone 整个并发控制系统是完全依赖 lealone 调度服务线程的,如果调度服务线程不参与并发读写了,是一定会发生并发访问问题的。
lealone-tomcat 那个插件也是内嵌在 tomcat 中跑的,但是我用 lealone 的调度服务线程完全替换掉 tomcat 的线程池了,所以并发读写 lealone 是安全的。
那怎么办,想着删除今天的数据。也不能删的
主表的少量 page 因为并发读写出问题有可能损坏了,你需要把数据库备份一下,然后执行 repair table 语句修复一下看看还有问题没有。
修复完之后还是建议先改成 client-server 的方式访问数据库,目前这种直接内嵌在 tomcat 中用它的线程并发读写 lealone 我已经发现是会有并发问题然后容易导致数据损坏的。
repair table 没用。 有办法把今天新增的数据全部删除吗
直接去对应的表目录下,找到尾部数字最大的 chunk 文件,删掉它之后最新的数据就从上一个 chunk 文件开始。
表的 id 可以通过 information schema 的 db_objects 表查询
information schema 的 tables 表也可以查表 id
select id,database_name from information_schema.databases; select id,table_name from information_schema.tables; 使用上面两条 sql 找到数据库和表的 id,然后到 lealone 的 data 目录下找 db_xx/t_xx_yy,就是表的数据目录,如果是 i 开头的就是索引的数据。
如果 redo_log 目录中还有数据,删除表的最新 chunk 之后是可以在重新启动时自动恢复的。
data\redo_log\archives 目录中的 redoLog_xxx 文件还保留了几天?虽然已经归档了,依然可以从这些归档的 redoLog 文件恢复到今天的数据的。不过我还没有开发工具从 archives 目录中的 redoLog_xxx 文件恢复,重启数据库时只是从未归档的 redoLog 文件恢复。
data\redo_log\archives 目录中的 redoLog_xxx 文件还保留了几天?虽然已经归档了,依然可以从这些归档的 redoLog 文件恢复到今天的数据的。不过我还没有开发工具从 archives 目录中的 redoLog_xxx 文件恢复,重启数据库时只是从未归档的 redoLog 文件恢复。
只有10-25和26日的
那个 lealone-5.2.0.jar 里面是不是没有 snakeyaml?没有的话就不读 lealone.yaml
那个 lealone-5.2.0.jar 里面是不是没有 snakeyaml?没有的话就不读 lealone.yaml
没有
你下个 zip 或 gz 的包,然后去 bin 目录下运行,这个才是完整的分发包。
data\redo_log 目录及其子目录下的所有文件都很重要,如果数据损坏后又不允许丢失一条数据,就可以通过 redo_log 目录中的文件恢复。从 redo_log 归档文件恢复数据的工具我还没做,如果你的数据不能丢失,我可以花点时间做个工具,你可以等一段时间,等我做完了我就把工具给你恢复数据,如果数据丢点没关系,那我就不着急做这个工具了,会把这个功能规划到 lealone 6.0 中。
data\redo_log 目录及其子目录下的所有文件都很重要,如果数据损坏后又不允许丢失一条数据,就可以通过 redo_log 目录中的文件恢复。从 redo_log 归档文件恢复数据的工具我还没做,如果你的数据不能丢失,我可以花点时间做个工具,你可以等一段时间,等我做完了我就把工具给你恢复数据,如果数据丢点没关系,那我就不着急做这个工具了,会把这个功能规划到 lealone 6.0 中。
可以丢失只要能回到今天之前。不过今天才发现之前的数据也有问题。
我这个项目是每月使用一次
之前的数据有几张表报 [2023-10-26 21:58:35] [HY000][50000] General error: "java.lang.StackOverflowError" [50000-0]
我一直以为你是在 lealone 的微服务框架里跑的,lealone-tomcat 插件我就是专门为了解决 tomcat 的线程池访问 lealone 产生的并发问题。直到看到堆栈错误里没有调度服务线程的信息,我才猜到调度服务线程根本就没有起作用,那肯定从第一天运行开始就可能导致 tomcat 的多个线程并发读写 lealone 从而会导致数据损坏。
在 lealone 的测试代码中,如果不启动 tcp server,调度服务线程也不会启动,但凡是嵌入式测试场景我都是单线程跑的,因为多线程确实有问题。
简单总结就是: 如果在 leaone 中调度服务线程没有起作用,其他线程只能单线程访问 lealone 才是安全的,否则一定是会有各种奇奇怪怪的并发问题的。调度服务线程就是 lealone 的根基。
lealone 的微服务 不太习惯,习惯了Spring boot 那岂不是说spring boot 不能使用嵌入式
在 lealone 6 中再想想怎么解决嵌入式环境外部多线程并发读写 lealone 的问题了,这不是个小问题。
Exception in thread "ScheduleService-2" java.lang.NullPointerException: Cannot invoke "org.lealone.storage.StorageMap.isClosed()" because "map" is null at org.lealone.transaction.aote.AOTransactionEngine.fullGc(AOTransactionEngine.java:270) at org.lealone.server.Scheduler.gc(Scheduler.java:219) at org.lealone.server.Scheduler.executeNextStatement(Scheduler.java:228) at org.lealone.server.Scheduler.run(Scheduler.java:110)
导数据一段时间就报这个错了。不知道是代码问题还是数据问题。
Exception in thread "ScheduleService-7" java.lang.OutOfMemoryError: Capacity: 6144 at org.lealone.db.DataBuffer.grow(DataBuffer.java:553) at org.lealone.db.DataBuffer.ensureCapacity(DataBuffer.java:532) at org.lealone.db.DataBuffer.put(DataBuffer.java:362) at org.lealone.net.nio.NioBuffer.appendBytes(NioBuffer.java:68) at org.lealone.net.nio.NioBuffer.appendBytes(NioBuffer.java:13) at org.lealone.net.NetBufferOutputStream.write(NetBufferOutputStream.java:35) at java.base/java.io.DataOutputStream.writeChar(DataOutputStream.java:190) at org.lealone.net.TransferOutputStream.writeString(TransferOutputStream.java:213) at org.lealone.net.TransferConnection.sendError(TransferConnection.java:105) at org.lealone.server.SessionInfo.sendError(SessionInfo.java:130) at org.lealone.server.Scheduler.executeNextStatement(Scheduler.java:262) at org.lealone.server.Scheduler.run(Scheduler.java:110)
你开了多大的内存?JVM OOM了。
set JAVA_OPTS=-ea -Xms1G -Xmx16G -XX:MaxDirectMemorySize=1G
我本机是64G内存。
16G 按理说足够了,我用256M内存就能持续导入 tpcc 的测试数据了,导入上百G的硬盘数据都没问题。你把你的表中所有的大对象类型直接改成 varchar 再导入试试,如果不出错那说明是大对象字段的问题,如果还出错,我需要表结构和导入数据的代码亲自试一下才知道问题到底出在哪。
早上好。我把9月份的数据放到生产环境,数据库版本号不记得了。全部数据是可以查询出来的。(嵌入式的) 搞到本地5.2 client-server 来就不行了。本地机器还比服务器的强几倍。
嵌入式场景我可以100%确定是存在并发更新导致数据损坏的问题的,现在查询正确不代表以后就不出问题了。
还是先搞清楚 client server 下数据为什么导入失败的问题。至少在 client server 下并发读写我已经压测了很久的 tpcc,并没有发现问题。
如果可以,你把表结构和导入数据的代码发一份到我的邮箱:codefollower AT gmail dot com
我试一下,不用发到 github,发 github 不安全
Exception in thread "ScheduleService-2" java.lang.NullPointerException: Cannot invoke "org.lealone.storage.StorageMap.isClosed()" because "map" is null at org.lealone.transaction.aote.AOTransactionEngine.fullGc(AOTransactionEngine.java:270) at org.lealone.server.Scheduler.gc(Scheduler.java:219) at org.lealone.server.Scheduler.executeNextStatement(Scheduler.java:228) at org.lealone.server.Scheduler.run(Scheduler.java:110)
导数据一段时间就报这个错了。不知道是代码问题还是数据问题。
这个我试了一下是个代码的 bug,一个线程准备做 GC 时,刚拿到当前使用的表对应的 StorageMap 的名字,另外一个线程就把表删除了,这时再用 StorageMap 的名字 get 出 StorageMap 对象就为 null 了,然后没有判断是否为 null 就去拿它调用 isClosed() 方法,最后就抛出 NullPointerException。
这个 bug 容易修复,加上 if(map != null) 即可。
Exception in thread "ScheduleService-7" java.lang.OutOfMemoryError: Capacity: 6144 at org.lealone.db.DataBuffer.grow(DataBuffer.java:553) at org.lealone.db.DataBuffer.ensureCapacity(DataBuffer.java:532) at org.lealone.db.DataBuffer.put(DataBuffer.java:362) at org.lealone.net.nio.NioBuffer.appendBytes(NioBuffer.java:68) at org.lealone.net.nio.NioBuffer.appendBytes(NioBuffer.java:13) at org.lealone.net.NetBufferOutputStream.write(NetBufferOutputStream.java:35) at java.base/java.io.DataOutputStream.writeChar(DataOutputStream.java:190) at org.lealone.net.TransferOutputStream.writeString(TransferOutputStream.java:213) at org.lealone.net.TransferConnection.sendError(TransferConnection.java:105) at org.lealone.server.SessionInfo.sendError(SessionInfo.java:130) at org.lealone.server.Scheduler.executeNextStatement(Scheduler.java:262) at org.lealone.server.Scheduler.run(Scheduler.java:110)
这个问题我也重现出来了,原因是看到你配的参数是 set JAVA_OPTS=-ea -Xms1G -Xmx16G -XX:MaxDirectMemorySize=1G
我才突然明白问题是出在 -XX:MaxDirectMemorySize=1G 这个参数,把它去掉就好了,运行 java 开发的数据库不需要它。
以下是原因:
使用 java 开发的数据库,为了避免经常被 JVM GC 暂停影响会选择使用 java.nio.DirectByteBuffer 也就是 DirectMemory 来管理内存中的数据,lealone 也不例外,也大量使用了 DirectByteBuffer,并且 lealone 只在乎 -Xmx 这个参数,直接忽视 -XX:MaxDirectMemorySize,也就是说如果 -Xmx 设置为 16G,lealone 就会尽可能的把数据装载到 DirectByteBuffer 中,并不会考虑 -XX:MaxDirectMemorySize 这个参数,哪怕你设置了 1G,lealone 在装了 1G 的 DirectByteBuffer 后,看到最大 -Xmx 是 16G,还小得很呢,就继续加载数据到 DirectByteBuffer,但是 JVM 本身是要按 -XX:MaxDirectMemorySize 这个参数办事的,当 lealone 加载的 DirectByteBuffer 超过 1G了,JVM 就抛出 OutOfMemoryError 了。
我也是第一次碰到这个问题,-XX:MaxDirectMemorySize 这个参数在我的印象里我从来没用过,限制它的大小带来的好处我也不知道有哪些,缺点对于数据库来说就很致命了。
-XX:MaxDirectMemorySize的作用:
大的DirectByteBuffer在堆内占用很少的内存空间,但是在堆外占用大量的内存,如果不加以限制,超过物理内存的大小就麻烦了。特别是,一些使用时间比较长的DirectByteBuffer对象,晋升到老年代,然后又变成垃圾了,如果老年代gc迟迟不触发,这部分堆外内存就不会释放。加了-XX:MaxDirectMemorySize以后,分配堆外内存如果超限制了,会触发一次堆内的gc,释放掉占坑不拉屎的DirectByteBuffer对象。
当然更好的办法是手工释放DirectByteBuffer,在java里面没有直接的办法,需要通过非标准api。
-XX:MaxDirectMemorySize的作用:
大的DirectByteBuffer在堆内占用很少的内存空间,但是在堆外占用大量的内存,如果不加以限制,超过物理内存的大小就麻烦了。特别是,一些使用时间比较长的DirectByteBuffer对象,晋升到老年代,然后又变成垃圾了,如果老年代gc迟迟不触发,这部分堆外内存就不会释放。加了-XX:MaxDirectMemorySize以后,分配堆外内存如果超限制了,会触发一次堆内的gc,释放掉占坑不拉屎的DirectByteBuffer对象。
当然更好的办法是手工释放DirectByteBuffer,在java里面没有直接的办法,需要通过非标准api。
lealone 内部自己实现了一套 GC 算法,分配的 DirectByteBuffer 的大小是受 -Xmx 限制的,所以不会超过物理内存的大小,同时 GC 算法也会动态把 DirectByteBuffer 对象置 null,这时 JVM 的 GC 线程可以及时回收。 所以设置 -XX:MaxDirectMemorySize 参数对 lealone 是没有任何意义的,反而误让 JVM 干坏事了。
就这个情况-Xmx16G -XX:MaxDirectMemorySize=1G这样设置是不合理的(其实在很多数情况下这个设置也不太合理,Xmx比MaxDirectMemorySize大太多)。
有的时候,光把DirectByteBuffer 对象置 null是不够的,现在java主流gc算法都是分代的比如G1和CMS,如果一个DirectByteBuffer能够在多个minor gc后保持存活,就晋升到老年代去了,有的程序老年代gc要很久才触发一次(比如大部分对象都是短生命周期的,都被minor gc干掉了,老年代就迟迟不会满),这样在老年代gc发生前,这个设置为null的DirectByteBuffer 都不会回收。
不分代的gc算法比如zgc下会如何我不太清楚,但现在使用最广泛的gc还是分代的。
只设置-Xmx是最好的,因为我看到报了一个错,网上说是MaxDirectMemorySize太小了。所以就调大了 JVM 参数不懂,就随便调大了
只设置-Xmx是最好的,因为我看到报了一个错,网上说是MaxDirectMemorySize太小了。所以就调大了
没事的,这里只是讨论技术问题,如果下次遇到运行 lealone 出现任何问题,直接发来 github 就可以了,不用先去网上查的。
2023-10-26T18:34:10.351+08:00 ERROR 16245 --- [nio-5220-exec-7] c.c.i.c.advice.GlobalResponseHandler : Throwable
java.lang.IllegalStateException: Position 0 [5.2.0/6] at org.lealone.common.util.DataUtils.newIllegalStateException(DataUtils.java:616) ~[lealone-common-5.2.0.jar!/:na] at org.lealone.storage.aose.btree.BTreeStorage.readPage(BTreeStorage.java:165) ~[lealone-aose-5.2.0.jar!/:na] at org.lealone.storage.aose.btree.page.PageReference.readPage(PageReference.java:199) ~[lealone-aose-5.2.0.jar!/:na] at org.lealone.storage.aose.btree.page.PageReference.getOrReadPage(PageReference.java:175) ~[lealone-aose-5.2.0.jar!/:na] at org.lealone.storage.aose.btree.page.NodePage.getChildPage(NodePage.java:51) ~[lealone-aose-5.2.0.jar!/:na] at org.lealone.storage.aose.btree.page.Page.gotoLeafPage(Page.java:267) ~[lealone-aose-5.2.0.jar!/:na] at org.lealone.storage.aose.btree.BTreeMap.getObjects(BTreeMap.java:178) ~[lealone-aose-5.2.0.jar!/:na] at org.lealone.transaction.aote.AOTransactionMap.getObjects(AOTransactionMap.java:563) ~[lealone-aote-5.2.0.jar!/:na] at org.lealone.db.index.standard.StandardPrimaryIndex.getRow(StandardPrimaryIndex.java:325) ~[lealone-db-5.2.0.jar!/:na] at org.lealone.db.table.StandardTable.getRow(StandardTable.java:230) ~[lealone-db-5.2.0.jar!/:na] at org.lealone.db.index.standard.StandardSecondaryIndex$StandardSecondaryIndexCursor.get(StandardSecondaryIndex.java:305) ~[lealone-db-5.2.0.jar!/:na] at org.lealone.db.index.standard.StandardSecondaryIndex$StandardSecondaryIndexCursor.get(StandardSecondaryIndex.java:299) ~[lealone-db-5.2.0.jar!/:na]