Java 8 的 stream 常规操作导致线程卡死

2022-08-01 17:06:01 +08:00
 coderstory

java 8 的 stream 操作导致 线程卡死

先贴一段堆栈打印 0x46 这个线程一直无法完成任务


root@data-f7b697db9-hq9lf:/app# jstack 8 | grep -A20 0x46
"http-nio-8200-exec-1" #60 daemon prio=5 os_prio=0 cpu=573228.20ms elapsed=783.76s tid=0x00007f8751e8d800 nid=0x46 runnable  [0x00007f871eaf1000]
   java.lang.Thread.State: RUNNABLE
        at java.util.stream.ReferencePipeline$2$1.accept(java.base@11.0.12/ReferencePipeline.java:176)
        at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(java.base@11.0.12/ArrayList.java:1655)
        at java.util.stream.AbstractPipeline.copyInto(java.base@11.0.12/AbstractPipeline.java:484)
        at java.util.stream.AbstractPipeline.wrapAndCopyInto(java.base@11.0.12/AbstractPipeline.java:474)
        at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(java.base@11.0.12/ReduceOps.java:913)
        at java.util.stream.AbstractPipeline.evaluate(java.base@11.0.12/AbstractPipeline.java:234)
        at java.util.stream.ReferencePipeline.collect(java.base@11.0.12/ReferencePipeline.java:578)
        at cn.bobmao.pro.data.repository.externalDataSourceHelper.DataBaseRepositoryImplCommon.getForeignKeyTable(DataBaseRepositoryImplCommon.java:32)
        at cn.bobmao.pro.data.repository.externalDataSourceHelper.DataBaseRepositoryImplCommon.getForeignKeyTable(DataBaseRepositoryImplCommon.java:40)
        at cn.bobmao.pro.data.repository.externalDataSourceHelper.DataBaseRepositoryImplCommon.getForeignKeyTable(DataBaseRepositoryImplCommon.java:40)
        at cn.bobmao.pro.data.repository.externalDataSourceHelper.DataBaseRepositoryImpl.getTableNames(DataBaseRepositoryImpl.java:66)
        at cn.bobmao.pro.data.repository.externalDataSourceHelper.ExternalDataSourceExecutor.getTableNames(ExternalDataSourceExecutor.java:57)
        at cn.bobmao.pro.data.service.ExternalDataSourceService.updateTable(ExternalDataSourceService.java:215)
        at cn.bobmao.pro.data.controller.ExternalDataSourceController.getTableInfo(ExternalDataSourceController.java:50)
        at cn.bobmao.pro.data.controller.ExternalDataSourceController$$FastClassBySpringCGLIB$$f577fbc0.invoke(<generated>)
        at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
        at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:779)
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
        at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:750)

pid 为 70 的线程( 16 进制就是 0x46 )为异常线程

    PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND                                                                                                                                                              
     12 root      20   0 7793308   1.9g  24564 S  61.3   6.0   2:03.58 G1 Conc#0                                                                                                                                                            
     70 root      20   0 7793308   1.9g  24564 S  16.3   6.0  10:17.51 http-nio-8200-e                                                                                                                                                      
     29 root      20   0 7793308   1.9g  24564 S   5.0   6.0   0:06.73 GC Thread#1                                                                                                                                                          
     10 root      20   0 7793308   1.9g  24564 S   4.7   6.0   0:06.69 GC Thread#0                                                                                                                                                          
     15 root      20   0 7793308   1.9g  24564 S   0.7   6.0   0:01.93 VM Thread  

对应 java 代码

    @Override
   public List<String> getForeignKeyTable(List<String> tableNames, DataSourceEntity info, List<ForeignKeyInfo> foreignKeyInfos) {
       List<ColumnInfo> result = new ArrayList<>();
       List<String> allTableNames = new ArrayList<>(tableNames);
       for (String tableName : tableNames) {
           result.addAll(findAllTable(info.getDataBaseName(), tableName));
       }
       result = result.stream().filter(column -> Arrays.stream(DB_KEYWORD).noneMatch(it -> column.getColumnName().equalsIgnoreCase(it))).collect(Collectors.toList());
       Map<String, List<ColumnInfo>> tables = result.stream().collect(Collectors.groupingBy(ColumnInfo::getTableName));
       List<String> childTables = new ArrayList<>();
       for (String name : tables.keySet()) {
           List<ColumnInfo> columnInfos = tables.get(name);
           for (ColumnInfo columnInfo : columnInfos) {
               List<ForeignKeyInfo> collect = foreignKeyInfos.stream().filter(it -> it.getSourceTableName().equals(name) && it.getSourceColumnName().equals(columnInfo.getColumnName())).collect(Collectors.toList()); // 32 行
               if (!CollectionUtils.isEmpty(collect)) {
                   //获取子表的表名
                   childTables.addAll(collect.stream().map(ForeignKeyInfo::getTargetTableName).collect(Collectors.toList()));
               }
           }
       }
       if (!CollectionUtils.isEmpty(childTables)) {
           allTableNames.addAll(getForeignKeyTable(childTables, info, foreignKeyInfos)); // 40 行
       }
       return allTableNames;
   }

一脸疑问,不清楚怎么排查。。。等一会儿容器就被 k8s 杀死重启了。

5539 次点击
所在节点    Java
41 条回复
TWorldIsNButThis
2022-08-02 02:32:43 +08:00
@Vegetable 而且这里的所谓 stream 全 tm 是单步操作然后就 collect ,看得出来这人根本就不怎么会,完全是把 stream 当成 collectionutil.filter 在用
Aloento
2022-08-02 02:33:59 +08:00
好恐怖呀哈哈哈
chengchen
2022-08-02 03:38:51 +08:00
这不就是二叉树层序遍历的变形题吗,leetcode 的 easy 难度
MoYi123
2022-08-02 10:13:56 +08:00
看起来像是数据里有环.
Belmode
2022-08-02 10:54:52 +08:00
数据库里存在表外键循环依赖了,导致内存居高不下,一直 GC
dorr
2022-08-02 11:01:35 +08:00
@chengchen 这个是图的遍历吧,一个表有多个字段外键指向另一个表,这个路径可以看做同一条
zmal
2022-08-02 11:21:33 +08:00
线程卡死本身和 stream 没啥关系。
但这个代码写的实在是太辣了。stream 不是让这么用的。
lmshl
2022-08-02 11:32:05 +08:00
先帮你等价替换一版,Stream API 其实写起来很漂亮的,只要改换一下思路就好了。
lmshl
2022-08-02 11:36:18 +08:00
pocketz
2022-08-02 14:41:36 +08:00
@lmshl 虽然但是,即使不看代码,这个配色也挺好看,能分享一下吗
lmshl
2022-08-02 17:53:10 +08:00
@pocketz Idea New UI ,配色是 New Dark ,字体是 Fire Code
bigfei
2022-08-02 18:15:08 +08:00
MYSQL 有元数据表的呀。。直接用 CTP 查询元数据表就可以了
bigfei
2022-08-02 18:17:08 +08:00
bigfei
2022-08-02 18:21:27 +08:00
lmshl
2022-08-02 18:49:20 +08:00
梳理了一遍依赖以后发现中间没必要 groupingBy ,代码可以再缩减到这程度。如果想再精简的话就得结合业务功能分析了,我估计结合业务还能砍掉 3-5 行,如果换成 Scala 大概 5-10 行就写完了。
lmshl
2022-08-02 18:58:22 +08:00
还能接着缩,逻辑依然等价
nbndco
2022-08-02 19:00:08 +08:00
每当这个时候我就特别能理解为什么说千万不要用新特性,没事不要修改不要更新不要升级了。这水平要是写 for 可能这代码还跑的快一点,至少不用 collect 这么多次。可读性本来就没有,所以也无所谓了。
chengchen
2022-08-02 19:54:58 +08:00
@dorr 层序遍历不就用到了图的广度优先搜索么
chrisia
2022-08-03 12:34:31 +08:00
@lmshl 优雅 😀
golangLover
2022-08-13 01:02:48 +08:00
@lmshl new dark? 找不到啊

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/870012

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX