2009-12-09 43 views
0

这是我对S.O.的第一个问题。
我有一个很奇怪的问题。
下面是我的问题...java进程被冻结(?)在linux上

我写了一个非常简单的方法,写一些文本到一个文件。
当然它运作良好,我的机器(XP,4CPU,jdk1.5.0_17 [SUN])
somtimes冻结运行服务器
(Linux的Accounting240 2.4.20-8smp上,4CPU, jdk1.5.0_22 [SUN])。

kill -3不起作用。
ctrl + \不起作用。

所以,我不能告诉你线程转储。

冻结以及.. 当我就在这个方法写一些了Thread.sleep(XX),问题就顺利(?)...
睡眠(XX)破......它再次发生今天与Thread.sleep(XX)...

你知道这个问题吗? 你有一些解决方案吗? 谢谢。 :-)

P.S.
linux发行版:Red Hat Linux 3.2.2-5
命令:java -cp。牛逼

import java.io.BufferedWriter; 
import java.io.File; 
import java.io.FileOutputStream; 
import java.io.OutputStreamWriter; 
import java.text.SimpleDateFormat; 
import java.util.Date; 

public class T { 
private BufferedWriter writer = null; 

private void log(String log) { 
    try { 
     if (writer == null) { 
      File logFile = new File("test.log"); 
      writer = new BufferedWriter(new OutputStreamWriter(
        new FileOutputStream(logFile, true))); 
     } 
     writer.write(new SimpleDateFormat("[yyyy-MM-dd HH:mm:ss] ") 
       .format(new Date())); 
     writer.write("[" + log + "]" + "\n"); 
     writer.flush(); 

     /* 
         * this is ad hoc solution ??? 
         */ 
     //Thread.sleep(10); 
    } catch (Exception e) { 
     e.printStackTrace(); 
    } finally {   
    } 

} 

public void test() { 
    long startTime = System.currentTimeMillis(); 

    while (true) { 
     log(String.valueOf(System.currentTimeMillis())); 
     System.out.println(System.currentTimeMillis()); 
     try { 
      //Thread.sleep((int) (Math.random() * 100)); 
     } catch (Exception e) { 
      break; 
     } 

     if (System.currentTimeMillis() - startTime > 1000 * 5) { 
      break; 
     } 
    } 

    if (writer != null) { 
     try { 
      writer.close(); 
     } catch (Exception e) { 
     } 
    } 
    System.out.println("OK"); 
} 

public static void main(String[] args) { 
    new T().test(); 
} 
} 
+2

如果你想做日志记录,我强烈建议你使用现有的日志记录框架,如果这是一个对你开放的选项。记住log4j或slf4j。 – Buhb 2009-12-09 08:14:29

+0

或java.util.logging。附带您的JDK。 – Thilo 2009-12-09 08:26:01

+0

在我的双核Ubuntu AMD64计算机(Sun的“服务器”虚拟机)上运行5次后无法重现。 Java版本 “1.6.0_0” OpenJDK的运行时环境(IcedTea6 1.6.1)(6b16-1.6.1-3ubuntu1) OpenJDK的64位服务器VM(建14.0-B16,混合模式) – 2009-12-09 08:29:15

回答

2

如果JVM不响应杀-3那么它是不是你的程序但失败这是坏的JVM和将需要Sun的一个bug报告

我注意到你正在运行一个2.4.20-8smp内核。这不是目前开源Linux发行版的典型内核,所以我建议你看看http://java.sun.com/j2se/1.5.0/system-configurations.html,看看你是否部署到支持的配置。如果没有,你应该让负责任的人知道这一点!

0

第一步是在程序“冻结”时获取线程转储。如果这是在Java 6上,那么默认情况下可以将JVisualVM或JConsole连接到它,并从那里获取所有线程的堆栈跟踪。因为它是Java 5,所以你应该能够使用jstack命令来获得线程转储(或者你可以通过命令行选项来启用JMX来附加上述工具,但我认为在这种情况下它不值得) 。在所有情况下,从启动应用程序的控制台按Ctrl-Break也可能会产生线程转储,具体取决于环境。

这样做几秒钟,然后比较线程转储。如果它们总是相同的,那么它看起来像是你的应用程序被锁定了;并且转储的最上面一行将显示线程正在阻塞的位置(当您查看该代码行时,它将提供非常好的线索,它们被阻止的资源)。

在另一方面,如果线程转储不时改变,程序不严格的僵持状态,但看起来像它的无限循环中运行 - 也许是你的循环条件之一是不正确声明所以线程永远不会退出或这种东西。再次看一下线程转储的集合,看看每个线程循环的代码区域,这会让您了解从未评估退出条件的循环条件。

如果这个问题在这个分析中不是很明显,那么回发垃圾箱,因为它可以帮助人们调试上面的代码。

+0

是啊~~这次我遇到了这个问题,我有一个寻找和挖掘解决问题的方法。线程转储,JConsole就像你说的。 这些是我尝试的方法。首先,线程转储的情况下,Ctrl + \\(Linux)不起作用。并杀死-3 PID不起作用。但我可以通过这些方式看到其他java进程的线程转储。这有点奇怪。 其次,JConsole成功连接到冻结的java进程,但监视器数据未显示在Jconsole UI中。但是我可以看到另一个java进程的彩色图形监视器数据。啊~~~~现在,我不想试图解决这个问题。 – uuidcode 2009-12-09 09:38:07

+0

非常感谢Andrzej Doyle .. – uuidcode 2009-12-09 10:01:45

0

我认为这是一种竞争条件。 while(true)将强制Linux上的虚拟机连续写入和刷新,并且Linux内核虚拟机将尝试拦截这些调用并缓冲写入。这将使进程在等待系统调用完成时进行spinloop;同时,它将被调度程序拾取并分配给另一个CPU(我可能在这里错了,tho)。新的CPU将尝试获取资源的锁定,并且所有操作都将导致死锁。

这可能是未来其他问题的标志。我建议:

  • 首先,为了清晰起见:移动log()方法之外的文件创建。这就是构造函数的用途。

  • 其次,你为什么要写这样的文件?你确定你的程序逻辑首先是有意义的吗?您是否宁愿将日志消息写入容器(例如ArrayList),并且每隔XX秒将其转储到单独的线程中的磁盘?现在,您正在限制您的磁盘速度的日志记录能力:您可能想要避免的事情。