美文网首页
关于java环境下process阻塞的问题

关于java环境下process阻塞的问题

作者: 假鞋子 | 来源:发表于2022-12-15 20:38 被阅读0次

场景:
线上环境一些配置突然被历史数据覆盖,通过分析发现【创建时间和变更时间相差好几天】,同时分析日志发现某个线程耗时好几天。从而导致旧数据覆盖了最新的数据,通过临时方案解决后,开始分析该问题。

分析:
执行非常耗时,大概率是阻塞问题。可以通过一下几种方式分析。
1:通过jstack pid查看线程栈,重点关注阻塞线程所在线程池的线程,发现存在好几个runable,且执行到同个地方,按cpu运算速度来看这是不合理的。

2:分析发现执行线程栈,找到对应的业务代码


线程栈.png

发现是FileInputStream造成的阻塞,但是底层理论上不会有太大问题,因此往上找到业务代码。发现是一个叫excuteBuild的代码,开始分析代码

3:分析如下业务代码,发现是读取流的时候被阻塞


业务代码

4:理论上正常读取流不会被阻塞,那问题大概来源于上面生成这个流对象的process对象的方法,查询Processs阻塞的资料发现如下资料


Process
process的坑

因此,大概率是标准输出流和标准错误流之间的阻塞问题导致的java阻塞。修改读取部分代码为Future<StringBuilder>使用多线程读取流,即解决了当前问题。

至此,问题解决。但是尚未弄明白原理。继续搜索相关资料

5:根据资料所说,标准错误流是无缓冲流,那为什么会阻塞呢?
需要注意的是,标准错误不缓存并不意味着操作系统或者设备驱动不缓存。这里所说的标准错误流无缓冲指的是内核调用标准IO库的时候,没有缓冲会直接输出。至于后续有没有缓冲,是依赖于操作系统或者用户态的。
而process进程间通信用的是pipe,标准错误流无缓冲直接输出到pipe管道中(理解为缓冲区,但不是不标准IO库的缓冲区),当pipe管道被占满时(java未读错误流),子进程阻塞。这时由于阻塞,也不再输出标准输出流,但java对标准输出流还没读取完,因此java线程仍处与等待中。如下图,pipe不属于标准IO的缓存


管道通信

这也验证了process的读取阻塞中真正起关键作用的缓冲区是getErrorStream()对应的那个缓冲区没有被清空,意思就是说其实只要及时读取标准错误流缓冲区的数据程序就不会被block。

6:测试
1:通过kill -9杀掉阻塞进程后,java阻塞的线程能继续执行。
因为子进程退出,
2:通过cat /proc/pid/fd/2 >>/1.txt将pipe管道内的标准错误流输出到1.txt临时文件中。java阻塞现场继续执行。
因为标准错误流被读取完,管道内有多余空间,子进程不再阻塞继续执行,且能执行完。一旦子进程执行完,java读取的标准输出流就不再阻塞,java进程就能继续往下执

参考资料:
管道通信
如果管道已满,进程将写入管道块吗?
一些排查路径
linux系统编程之管道
docker场景下相同问题排查

关键词:
进程通信阻塞

相关文章

网友评论

      本文标题:关于java环境下process阻塞的问题

      本文链接:https://www.haomeiwen.com/subject/dgwrqdtx.html