March 03, 2017
This is a how-to guide for crashing the java virtual machine. It gives you an introduction to race conditions and shows you what errors can happen if your code contains such bugs.
public class DataRaceTest implements Runnable { private Type[] instance; @Override public void run() { if (instance == null) { Type[] ts = new Type[1]; ts[0] = Object.class; instance = ts; } instance[0].getTypeName(); } }
If this method is executed by multiple threads it leads to a race condition. More specifically it leads to a data race. Data races are defined in the java memory model as an access to a shared field without correct synchronization. According to the java memory model data races lead to platform dependent undefined behavior:
Without correct synchronization, very strange, confusing and counterintuitive behaviors are possible.If the code is executed in the given order everything is o.k. But if some component reorders the statements, the array might not be completely initialized. One such component is the cache system of the CPU. Intel Processors, for example, have a cache system with a strong memory guarantees. The cache system makes sure that the values written by the cores are almost always seen in the same order as they have been written by the other cores. But ARM compatible processors like in smartphones or the Raspberry Pi do not give this guarantee.
java -cp stress-test-0.0.1-SNAPSHOT-jar-with-dependencies.jar com.vmlens.StressTest -i 5 -w 16 com.vmlens.stressTest.examples.dataRace.DataRaceTestSetup
using the following test setup class:
public class DataRaceTestSetup implements TestSetup { @Override public Runnable createTest() { return new DataRaceTest(); } }
This runs the method run of DataRaceTest for 5 iterations. Each iteration consists of 16.000 tests run by 16 threads.
If I run this on my intel i5 workstation, I could not create an exception. If I run this on my Raspberry Pi, I see the following Nullpointer Exception for every 2000 tests:
java.lang.NullPointerException at com.vmlens.stressTest.examples.dataRace.DataRaceTest.run(DataRaceTest.java:78) at com.vmlens.stressTest.internal.TestCall.call(TestCall.java:36) at com.vmlens.stressTest.internal.TestCall.call(TestCall.java:7) at com.vmlens.stressTest.internal.WorkerThread.run(WorkerThread.java:22)
@Override public void run() { if (instance == null) { Type[] ts = new Type[1]; ts[0] = Object.class; instance = ts; } Type[] clonedInstance = instance.clone(); clonedInstance[0].getTypeName(); }
This time I run the test till I see at least one error. This is done by using the -e option:
java -cp stress-test-0.0.1-SNAPSHOT-jar-with-dependencies.jar com.vmlens.StressTest -e 1 -w 16 com.vmlens.stressTest.examples.dataRace.DataRaceTestSetup
If I run this code on a Raspberry Pi, I see a java virtual machine crash after some time. It takes between half an hour and a day:
# # A fatal error has been detected by the Java Runtime Environment: # # SIGSEGV (0xb) at pc=0x741ab340, pid=26364, tid=1682633824 # # JRE version: Java(TM) SE Runtime Environment (8.0_65-b17) (build 1.8.0_65-b17) # Java VM: Java HotSpot(TM) Client VM (25.65-b01 mixed mode linux-arm ) # Problematic frame: # J 38 C1 com.vmlens.stressTest.internal.TestSetupCall.call()Lcom/vmlens/stressTest/internal/Result; (45 bytes) @ 0x741ab340 [0x741ab250+0xf0]
That is the reason I developed vmlens, a tool to detect data races in the execution trace of an application.
© 2020 vmlens Legal Notice Privacy Policy