项目中某类static初始化线程时遇内存不足异常及后续的NoClassDefFoundError异常记录
最近,我发现该项目的情况相对有趣,因此我将在此处记录下来。

一类在静态初始化代码块中启动线程,以监视类的操作并打印日志。代码如下。
public class Test {
static {
new Thread(new Runnable(){}).start();
}
...// other code
}
在项目操作期间,有一个需要此类的地方。加载类时,机器在此时间点无法使用足够的内存,从而导致线程启动故障并引发异常:java.lang.outofmemoryerror:无法创建新的本机线程。此例外很容易理解,但后来发现同样的例外正在打印:
java.lang.noclassdeffounderror:无法初始化类xx.xx.test(上面定义的测试类)
根据上下文,可以猜测,在静态块中创建线程时会抛出的OutofMemoryError导致测试类不再加载,从而导致随后的一系列NoclassDeffoundErrors。为了验证这个猜想,我编写了一个代码来对其进行测试
import java.util.Date;
import org.apache.commons.lang3.time.DateUtils;
/**
* @author klein.lei
* @version $Id: TestStaticError.java, v 0.1 2018年02月27日 下午8:28 klein.lei Exp $
*/
public class TestStaticError {
static {
if (new Date().after(DateUtils.addDays(new Date(), -1))) { // 这里是为了保证一定抛出该异常,如果你在static块中直接抛异常,编译无法通过
throw new RuntimeException();
}
}
public void test1() {
System.out.println("hello world");
}
}
主要方法
import java.util.Scanner;
/**
* @author klein.lei
* @version $Id: Test.java, v 0.1 2018年02月27日 下午8:28 klein.lei Exp $
*/
public class Test {
public static void main(String[] args) throws InterruptedException {
new Thread(new Runnable() {
@Override
public void run() {
TestStaticError testStaticError = new TestStaticError();
testStaticError.test1();
}
}).start();
Thread.sleep(200L);
for (int i = 0; i < 10; i ++) {
new Thread(new Runnable() {
@Override
public void run() {
TestStaticError testStaticError = new TestStaticError();
testStaticError.test1();
}
}).start();
}
new Scanner(System.in).next();
}
}
跑步可以得到以下结果
Exception in thread "Thread-0" java.lang.ExceptionInInitializerError
at learning.web.Test$1.run(Test.java:19)
at java.lang.Thread.run(Thread.java:745)
Caused by: java.lang.RuntimeException
at learning.web.TestStaticError.(TestStaticError.java:19)
... 2 more
Exception in thread "Thread-1" Exception in thread "Thread-2" java.lang.NoClassDefFoundError: Could not initialize class learning.web.TestStaticError
at learning.web.Test$2.run(Test.java:28)
at java.lang.Thread.run(Thread.java:745)
Exception in thread "Thread-4" Exception in thread "Thread-3" java.lang.NoClassDefFoundError: Could not initialize class learning.web.TestStaticError
at learning.web.Test$2.run(Test.java:28)
at java.lang.Thread.run(Thread.java:745)
Exception in thread "Thread-6" java.lang.NoClassDefFoundError: Could not initialize class learning.web.TestStaticError
at learning.web.Test$2.run(Test.java:28)
at java.lang.Thread.run(Thread.java:745)
java.lang.NoClassDefFoundError: Could not initialize class learning.web.TestStaticError
at learning.web.Test$2.run(Test.java:28)
at java.lang.Thread.run(Thread.java:745)
Exception in thread "Thread-10" Exception in thread "Thread-5" java.lang.NoClassDefFoundError: Could not initialize class learning.web.TestStaticError
at learning.web.Test$2.run(Test.java:28)
at java.lang.Thread.run(Thread.java:745)
Exception in thread "Thread-9" Exception in thread "Thread-8" java.lang.NoClassDefFoundError: Could not initialize class learning.web.TestStaticError
at learning.web.Test$2.run(Test.java:28)
at java.lang.Thread.run(Thread.java:745)
Exception in thread "Thread-7" java.lang.NoClassDefFoundError: Could not initialize class learning.web.TestStaticError
at learning.web.Test$2.run(Test.java:28)
at java.lang.Thread.run(Thread.java:745)
java.lang.NoClassDefFoundError: Could not initialize class learning.web.TestStaticError
at learning.web.Test$2.run(Test.java:28)
at java.lang.Thread.run(Thread.java:745)
java.lang.NoClassDefFoundError: Could not initialize class learning.web.TestStaticError
at learning.web.Test$2.run(Test.java:28)
at java.lang.Thread.run(Thread.java:745)
java.lang.NoClassDefFoundError: Could not initialize class learning.web.TestStaticError
at learning.web.Test$2.run(Test.java:28)
at java.lang.Thread.run(Thread.java:745)
可以发现,第一次是RuntimeException,其次是Java.lang.noclassdeffounderror,表明上述猜测是正确的。至于为什么发生这种情况,它可能需要将Classloader的源代码加倍。
在Google之后,我找到了答案。有人在stackoverflow上问了这个问题(单击以打开链接)(当然,程序员需要掌握面向stackoverflow的编程)
让我们简要总结:
我们知道Java中的每个C类都有一个相应的类对象(Java.lang.class)。此类对象用于描述有关C类的一些信息,包括C类C的状态C类描述C类。
1。C类已通过验证和准备阶段,但尚未初始化。
(注意:类加载过程)
2。C类是由其他线程初始化的。
3。C类已由其他线程初始化。
4。C类处于错误状态,这可能是由于尝试初始化时的失败。
根据Oracle提供的文档(),JVM在初始化类时必须严格遵守以下规范:
1。对于每个C类,都有一个唯一的初始化锁定LC。初始化类时,当前线程必须首先获取锁。
2。根据C类的类对象的状态指示,如果C类由其他线程初始化,则释放当前锁定LC,并且当前线程被阻止,直到班级初始化完成为止。
3。根据C类的类对象的状态指示,如果C类由当前线程初始化,则意味着它是一个递归调用,需要发布锁定LC,并且请求正常结束。
4。根据C类的类对象的状态指示,如果初始化了C类,则需要发布锁定LC,并且请求正常终止。
5。根据C类的类对象的状态指示,如果C类处于错误状态,则意味着类的初始化无法完成。目前,您需要释放锁并扔一个NoclassDeffoundError。
6。如果在2、3、4和5中不是这种情况,则意味着当前线程可以正常初始化类。目前,有必要在C的类对象中记录当前线程的初始化C类,然后释放锁定LC,然后将其初始化。在编译过程中可以确定的C和其他常数字段中的最终场。
7。如果C类不是接口,并且尚未初始化C的父级SC,请重复对SC初始化类的所有步骤。如有必要,请先验证并准备SC。如果在SC的初始化过程中引发了异常,则初始化过程将出乎意料地中止,然后需要获取锁。 C类在C类的类对象中标记为错误状态。标记完成后,其他等待线程释放锁定LC,最后在SC初始化过程中引发了该过程的同一例外,从而流产了类的初始化过程C
8。接下来,确定C类的断言()是否生效。
9。接下来,遵循代码文本定义的顺序(注意:
public class {
int a = getA();
int b = getB();
}
这里的文本顺序是指第一个初始化,然后是b),初始化类的字段,静态块代码和接口上定义的字段。
10。如果此执行过程正常结束,则获取了锁定液压,则标记为C类标记的类对象已完全初始化,所有其他线程均已唤醒,锁定锁定,并且该过程通常结束。
11。如果在此执行过程中发生例外,则抛出异常E,导致该过程不小心流产。如果此异常不是错误或错误的子类,请创建一个异常的依属二氨基烯词,并将E用作异常的构造函数参数。在下一步中,用ExcepeninInitializerError替换E。如果由于OUTOFMEMORYERROR错误而无法创建异常的initializerError对象,请在下一步中将E替换为OutofMemoryError。
12.尝试获取锁定LC,将C类的类对象标记为错误的状态,唤起所有等待线程,释放锁定LC,并以Exception e(或ExceptionininitialInitializerError或OutofmemoryError)出现在步骤中11。
根据上述步骤,我们可以发现静态块的代码初始化是在步骤9中初始化的。如果此时抛出了异常,则抛出了一个异常的,它符合我们的测试代码,因为它符合我们的测试代码,因为类的初始化过程全部锁定,当第一个线程加载类时,类状态被标记为错误状态。当其他线程再次加载类时,它们会直接输入步骤5中描述的过程并将其投掷。 NoclassDeffoundError也匹配我们的测试代码的性能。
在这一点上,整个例外的原因也很清楚。在代码编写过程中,我们应该尽量避免在静态块代码或类成员变量中抛出异常,因为一旦在初始化过程中,不能重试的类初始化过程。如果发生例外,则将在此后正常加载类。