我的Java应用程序出了问题。有时我的应用程序因未知原因崩溃。
我已经复制了问题,并且被起诉的方法似乎是
g.drawString(s, CELL_WIDTH + c * CELL_WIDTH, y)
但是,非常奇怪的是,没有生成任何JVM崩溃文件(hs_pid.log ...),虽然我使用以下命令行选项,但控制台窗口中都没有报告错误
-verbose:gc -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/root/TEST -XX:ErrorFile=/root/TEST/vmlogs%p.log -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/root/TEST -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:/root/TEST/gc.log
下面你可以找到一个通过强调来重现问题的小测试用例
Thread.sleep (1)
启动程序,在5-6小时后(更多),问题再现。
我使用Java 1.7.0_80(Java 7系列的最新版本)在Linux PC上运行应用程序,并且我使用最新的Java 6也重现了同样的问题。
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JLabel;
public class TestDraw {
private static final int WINDOW_WIDTH = 800;
private static final int WINDOW_HEIGHT = 480;
private static final String AB = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-:?$";
private static Random rnd = new Random();
private static final Font m_font = new Font("monospaced", Font.PLAIN, 22);
private static final int NUM_ROWS = 17;
private static final int CELL_WIDTH = 12;
private static final int ROW_HEIGTH = 25;
private static final int ROW_WIDTH = WINDOW_WIDTH;
private static String randomString(int len) {
StringBuilder sb = new StringBuilder(len);
for (int i = 0; i < len; i++) {
sb.append(AB.charAt(rnd.nextInt(AB.length())));
}
return sb.toString();
}
public static void main(String[] args) {
JLabel textLabel = new JLabel() {
@Override
protected void paintComponent(Graphics g) {
for (int i = 0; i <= NUM_ROWS; i++) {
int y_text = (i + 1) * ROW_HEIGTH - 7;
String text = randomString(64);
drawString(text, y_text, g, Color.black);
}
}
private void drawString(String text, int y, Graphics g, Color color) {
for (int c = 0; c < text.length(); c++) {
String s = text.substring(c, c + 1);
g.drawString(s, CELL_WIDTH + c * CELL_WIDTH, y);
}
}
};
JFrame myFrame = new JFrame("TEST COMPONENT");
myFrame.setLayout(new BorderLayout());
myFrame.setResizable(false);
myFrame.add(textLabel, BorderLayout.CENTER);
myFrame.setSize(WINDOW_WIDTH, WINDOW_HEIGHT);
myFrame.setVisible(true);
myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
textLabel.setFont(m_font);
while (true) {
textLabel.repaint();
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
我用@Hovercraft Full Of Eels建议更改了测试用例。
这是修改:
textLabel.setFont(m_font);
Thread t = new Thread(new Runnable() {
@Override
public void run() {
while (true) {
textLabel.repaint();
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
t.start();
}
我更新了我的测试用例,添加了@apangin的建议,我在main中调用了这个方法。
private static void attachShutDownHook() {
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
System.out.println("Inside Shutdown Hook");
}
});
System.out.println("Shut Down Hook Attached.");
}
结果是应用程序始终崩溃,但
System.out.println("Inside Shutdown Hook");
未被调用。
相反,如果我手动杀死Java进程,则会调用先前的打印。
我根据@Hovercraft Full Of Eels的建议修改了我的测试用例。现在我没有使用true和thread.sleep但是使用swing定时器来排队事件线程上的Swing代码。测试用例总是崩溃。
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.Timer;
public class TestDraw {
private static final int WINDOW_WIDTH = 800;
private static final int WINDOW_HEIGHT = 480;
private static final String AB = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-:?$";
private static Random rnd = new Random();
private static final Font m_font = new Font("monospaced", Font.PLAIN, 22);
private static final int NUM_ROWS = 17;
private static final int CELL_WIDTH = 12;
private static final int ROW_HEIGTH = 25;
private static final int ROW_WIDTH = WINDOW_WIDTH;
private static Timer updateTimer = null;
private static JLabel textLabel = null;
private static String randomString(int len) {
StringBuilder sb = new StringBuilder(len);
for (int i = 0; i < len; i++) {
sb.append(AB.charAt(rnd.nextInt(AB.length())));
}
return sb.toString();
}
public static void main(String[] args) {
textLabel = new JLabel() {
@Override
protected void paintComponent(Graphics g) {
for (int i = 0; i <= NUM_ROWS; i++) {
int y_text = (i + 1) * ROW_HEIGTH - 7;
String text = randomString(64);
drawString(text, y_text, g, Color.black);
}
}
private void drawString(String text, int y, Graphics g, Color color) {
for (int c = 0; c < text.length(); c++) {
String s = text.substring(c, c + 1);
g.drawString(s, CELL_WIDTH + c * CELL_WIDTH, y);
}
}
};
updateTimer = new Timer(1, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
textLabel.repaint();
}
});
JFrame myFrame = new JFrame("TEST COMPONENT");
myFrame.setLayout(new BorderLayout());
myFrame.setResizable(false);
myFrame.add(textLabel, BorderLayout.CENTER);
myFrame.setSize(WINDOW_WIDTH, WINDOW_HEIGHT);
myFrame.setVisible(true);
myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
textLabel.setFont(m_font);
updateTimer.start();
}
}