Do not violate JVMS regarding initialization of final fields (#434)

Without this change instrumented classes can't pass checks
and cause IllegalAccessError starting from OpenJDK 9 EA b127
(see https://bugs.openjdk.java.net/browse/JDK-8157181).
diff --git a/org.jacoco.core.test/src-java8/org/jacoco/core/test/validation/BadCycleInterfaceTest.java b/org.jacoco.core.test/src-java8/org/jacoco/core/test/validation/BadCycleInterfaceTest.java
new file mode 100644
index 0000000..1b76d98
--- /dev/null
+++ b/org.jacoco.core.test/src-java8/org/jacoco/core/test/validation/BadCycleInterfaceTest.java
@@ -0,0 +1,43 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2016 Mountainminds GmbH & Co. KG and Contributors
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Evgeny Mandrikov - initial API and implementation
+ *
+ *******************************************************************************/
+package org.jacoco.core.test.validation;
+
+import org.jacoco.core.analysis.ICounter;
+import org.jacoco.core.test.validation.targets.BadCycleInterface;
+import org.junit.Test;
+
+/**
+ * Test of "bad cycles" with interfaces.
+ */
+public class BadCycleInterfaceTest extends BadCycleTestBase {
+
+	public BadCycleInterfaceTest() throws Exception {
+		super("src-java8", BadCycleInterface.class);
+	}
+
+	@Test
+	public void test() throws Exception {
+		loader.loadClass(BadCycleInterface.Child.class.getName())
+				.getMethod("childStaticMethod").invoke(null);
+
+		analyze(BadCycleInterface.Child.class);
+
+		if (System.getProperty("java.version").startsWith("9-ea")) {
+			// JDK-9042842
+			assertLine("2", ICounter.NOT_COVERED);
+		} else {
+			assertLine("2", ICounter.FULLY_COVERED);
+		}
+		assertLine("4", ICounter.FULLY_COVERED);
+	}
+
+}
diff --git a/org.jacoco.core.test/src-java8/org/jacoco/core/test/validation/InterfaceDefaultMethodsTest.java b/org.jacoco.core.test/src-java8/org/jacoco/core/test/validation/InterfaceDefaultMethodsTest.java
index 405c4cf..66c3843 100644
--- a/org.jacoco.core.test/src-java8/org/jacoco/core/test/validation/InterfaceDefaultMethodsTest.java
+++ b/org.jacoco.core.test/src-java8/org/jacoco/core/test/validation/InterfaceDefaultMethodsTest.java
@@ -16,7 +16,7 @@
 import org.junit.Test;
 
 /**
- * Tests of static initializer in interfaces.
+ * Tests of static initializer and default methods in interfaces.
  */
 public class InterfaceDefaultMethodsTest extends ValidationTestBase {
 
diff --git a/org.jacoco.core.test/src-java8/org/jacoco/core/test/validation/InterfaceOnlyDefaultMethodsTest.java b/org.jacoco.core.test/src-java8/org/jacoco/core/test/validation/InterfaceOnlyDefaultMethodsTest.java
new file mode 100644
index 0000000..24553d7
--- /dev/null
+++ b/org.jacoco.core.test/src-java8/org/jacoco/core/test/validation/InterfaceOnlyDefaultMethodsTest.java
@@ -0,0 +1,38 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2016 Mountainminds GmbH & Co. KG and Contributors
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Evgeny Mandrikov - initial API and implementation
+ *
+ *******************************************************************************/
+package org.jacoco.core.test.validation;
+
+import org.jacoco.core.analysis.ICounter;
+import org.jacoco.core.test.validation.targets.InterfaceOnlyDefaultMethodsTarget;
+import org.junit.Test;
+
+/**
+ * Tests of default methods in interfaces.
+ */
+public class InterfaceOnlyDefaultMethodsTest extends ValidationTestBase {
+
+	public InterfaceOnlyDefaultMethodsTest() {
+		super("src-java8", InterfaceOnlyDefaultMethodsTarget.class);
+	}
+
+	@Override
+	protected void run(final Class<?> targetClass) throws Exception {
+		loader.add(InterfaceOnlyDefaultMethodsTarget.Impl.class).newInstance();
+	}
+
+	@Test
+	public void testCoverageResult() {
+		assertLine("m1", ICounter.FULLY_COVERED);
+		assertLine("m2", ICounter.NOT_COVERED);
+	}
+
+}
diff --git a/org.jacoco.core.test/src-java8/org/jacoco/core/test/validation/targets/BadCycleInterface.java b/org.jacoco.core.test/src-java8/org/jacoco/core/test/validation/targets/BadCycleInterface.java
new file mode 100644
index 0000000..c2b1519
--- /dev/null
+++ b/org.jacoco.core.test/src-java8/org/jacoco/core/test/validation/targets/BadCycleInterface.java
@@ -0,0 +1,48 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2016 Mountainminds GmbH & Co. KG and Contributors
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Evgeny Mandrikov - initial API and implementation
+ *
+ *******************************************************************************/
+package org.jacoco.core.test.validation.targets;
+
+public class BadCycleInterface {
+
+	public interface Base {
+		static final Object BASE_CONST = new Child() {
+			{
+				Stubs.nop("base clinit"); // $line-1$
+			}
+		}.childDefaultMethod();
+
+		default void baseDefaultMethod() {
+		}
+	}
+
+	public interface Child extends Base {
+		static final Object CHILD_CONST = new Object() {
+			{
+				Stubs.nop("child clinit"); // $line-3$
+			}
+		};
+
+		default Object childDefaultMethod() {
+			Stubs.nop("child default method"); // $line-2$
+			return null;
+		}
+
+		static void childStaticMethod() {
+			Stubs.nop("child static method"); // $line-4$
+		}
+	}
+
+	public static void main(String[] args) {
+		Child.childStaticMethod();
+	}
+
+}
diff --git a/org.jacoco.core.test/src-java8/org/jacoco/core/test/validation/targets/InterfaceDefaultMethodsTarget.java b/org.jacoco.core.test/src-java8/org/jacoco/core/test/validation/targets/InterfaceDefaultMethodsTarget.java
index 65028b9..4bd91b3 100644
--- a/org.jacoco.core.test/src-java8/org/jacoco/core/test/validation/targets/InterfaceDefaultMethodsTarget.java
+++ b/org.jacoco.core.test/src-java8/org/jacoco/core/test/validation/targets/InterfaceDefaultMethodsTarget.java
@@ -14,7 +14,7 @@
 import static org.jacoco.core.test.validation.targets.Stubs.i1;
 
 /**
- * This test target is an interface with a class initializer.
+ * This test target is an interface with a class initializer and default methods.
  */
 public interface InterfaceDefaultMethodsTarget {
 
diff --git a/org.jacoco.core.test/src-java8/org/jacoco/core/test/validation/targets/InterfaceOnlyDefaultMethodsTarget.java b/org.jacoco.core.test/src-java8/org/jacoco/core/test/validation/targets/InterfaceOnlyDefaultMethodsTarget.java
new file mode 100644
index 0000000..a27409e
--- /dev/null
+++ b/org.jacoco.core.test/src-java8/org/jacoco/core/test/validation/targets/InterfaceOnlyDefaultMethodsTarget.java
@@ -0,0 +1,36 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2016 Mountainminds GmbH & Co. KG and Contributors
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Evgeny Mandrikov - initial API and implementation
+ *
+ *******************************************************************************/
+package org.jacoco.core.test.validation.targets;
+
+/**
+ * This test target is an interface with only default methods.
+ */
+public interface InterfaceOnlyDefaultMethodsTarget {
+
+	// no <clinit>, only default methods:
+
+	default void m1() {
+		return; // $line-m1$
+	}
+
+	default void m2() {
+		return; // $line-m2$
+	}
+
+	public class Impl implements InterfaceOnlyDefaultMethodsTarget {
+
+		public Impl() {
+			m1();
+		}
+	}
+
+}
diff --git a/org.jacoco.core.test/src/org/jacoco/core/internal/instr/ClassInstrumenterTest.java b/org.jacoco.core.test/src/org/jacoco/core/internal/instr/ClassInstrumenterTest.java
index 428ba65..5d41771 100644
--- a/org.jacoco.core.test/src/org/jacoco/core/internal/instr/ClassInstrumenterTest.java
+++ b/org.jacoco.core.test/src/org/jacoco/core/internal/instr/ClassInstrumenterTest.java
@@ -62,7 +62,7 @@
 
 	// === IProbeArrayStrategy ===
 
-	public int storeInstance(MethodVisitor mv, int variable) {
+	public int storeInstance(MethodVisitor mv, boolean clinit, int variable) {
 		return 0;
 	}
 
diff --git a/org.jacoco.core.test/src/org/jacoco/core/internal/instr/ProbeArrayStrategyFactoryTest.java b/org.jacoco.core.test/src/org/jacoco/core/internal/instr/ProbeArrayStrategyFactoryTest.java
index c8da6d6..3ecbd3a 100644
--- a/org.jacoco.core.test/src/org/jacoco/core/internal/instr/ProbeArrayStrategyFactoryTest.java
+++ b/org.jacoco.core.test/src/org/jacoco/core/internal/instr/ProbeArrayStrategyFactoryTest.java
@@ -14,7 +14,10 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.util.ArrayList;
+import java.util.List;
 
 import org.jacoco.core.runtime.IExecutionDataAccessorGenerator;
 import org.jacoco.core.runtime.OfflineInstrumentationAccessGenerator;
@@ -98,9 +101,8 @@
 		assertDataField(InstrSupport.DATAFIELD_ACC);
 		assertInitMethod(true);
 
-		final ClassVisitorMock cv = new ClassVisitorMock();
-		strategy.storeInstance(cv.visitMethod(0, null, null, null, null), 0);
-		assertFalse(cv.interfaceMethod);
+		strategy.storeInstance(cv.visitMethod(0, null, null, null, null), false,
+				0);
 	}
 
 	@Test
@@ -119,21 +121,21 @@
 
 	@Test(expected = UnsupportedOperationException.class)
 	public void testEmptyInterface7StoreInstance() {
-		IProbeArrayStrategy strategy = test(Opcodes.V1_7,
-				Opcodes.ACC_INTERFACE, false, false);
-		strategy.storeInstance(null, 0);
+		IProbeArrayStrategy strategy = test(Opcodes.V1_7, Opcodes.ACC_INTERFACE,
+				false, false);
+		strategy.storeInstance(null, false, 0);
 	}
 
 	@Test
 	public void testInterface8() {
+		cv.isInterface = true;
 		final IProbeArrayStrategy strategy = test(Opcodes.V1_8,
 				Opcodes.ACC_INTERFACE, false, true);
 		assertDataField(InstrSupport.DATAFIELD_INTF_ACC);
-		assertInitMethod(true);
+		assertInitAndClinitMethods();
 
-		final ClassVisitorMock cv = new ClassVisitorMock();
-		strategy.storeInstance(cv.visitMethod(0, null, null, null, null), 0);
-		assertTrue(cv.interfaceMethod);
+		strategy.storeInstance(cv.visitMethod(0, null, null, null, null), false,
+				0);
 	}
 
 	@Test
@@ -143,6 +145,13 @@
 		assertNoInitMethod();
 	}
 
+	@Test(expected = UnsupportedOperationException.class)
+	public void testEmptyInterface8StoreInstance() {
+		final IProbeArrayStrategy strategy = test(Opcodes.V1_8,
+				Opcodes.ACC_INTERFACE, false, false);
+		strategy.storeInstance(null, false, 0);
+	}
+
 	@Test
 	public void testClinitInterface8() {
 		test(Opcodes.V1_8, Opcodes.ACC_INTERFACE, true, false);
@@ -150,6 +159,18 @@
 		assertNoInitMethod();
 	}
 
+	@Test
+	public void testClinitAndMethodsInterface8() {
+		cv.isInterface = true;
+		final IProbeArrayStrategy strategy = test(Opcodes.V1_8,
+				Opcodes.ACC_INTERFACE, true, true);
+		assertDataField(InstrSupport.DATAFIELD_INTF_ACC);
+		assertInitAndClinitMethods();
+
+		strategy.storeInstance(cv.visitMethod(0, "<clinit>", null, null, null),
+				true, 0);
+	}
+
 	private IProbeArrayStrategy test(int version, int access, boolean clinit,
 			boolean method) {
 		ClassWriter writer = new ClassWriter(0);
@@ -179,16 +200,40 @@
 		return strategy;
 	}
 
+	private static class AddedMethod {
+		private final int access;
+		private final String name;
+		private final String desc;
+		private boolean frames;
+
+		AddedMethod(int access, String name, String desc) {
+			this.access = access;
+			this.name = name;
+			this.desc = desc;
+		}
+
+		void assertInitMethod(boolean frames) {
+			assertEquals(InstrSupport.INITMETHOD_NAME, name);
+			assertEquals(InstrSupport.INITMETHOD_DESC, desc);
+			assertEquals(InstrSupport.INITMETHOD_ACC, access);
+			assertEquals(Boolean.valueOf(frames), Boolean.valueOf(frames));
+		}
+
+		void assertClinit() {
+			assertEquals(InstrSupport.CLINIT_NAME, name);
+			assertEquals(InstrSupport.CLINIT_DESC, desc);
+			assertEquals(InstrSupport.CLINIT_ACC, access);
+			assertEquals(Boolean.valueOf(false), Boolean.valueOf(frames));
+		}
+	}
+
 	private static class ClassVisitorMock extends ClassVisitor {
 
+		private boolean isInterface;
+
 		private int fieldAccess;
 		private String fieldName;
-
-		private int methodAccess;
-		private String methodName;
-
-		private boolean frames;
-		private boolean interfaceMethod;
+		private final List<AddedMethod> methods = new ArrayList<AddedMethod>();
 
 		ClassVisitorMock() {
 			super(Opcodes.ASM5);
@@ -206,20 +251,51 @@
 		@Override
 		public MethodVisitor visitMethod(int access, String name, String desc,
 				String signature, String[] exceptions) {
-			assertNull(methodName);
-			methodAccess = access;
-			methodName = name;
+			final AddedMethod m = new AddedMethod(access, name, desc);
+			methods.add(m);
 			return new MethodVisitor(Opcodes.ASM5) {
 				@Override
 				public void visitFrame(int type, int nLocal, Object[] local,
 						int nStack, Object[] stack) {
-					frames = true;
+					m.frames = true;
+				}
+
+				@Override
+				public void visitFieldInsn(int opcode, String owner,
+						String name, String desc) {
+					assertEquals(InstrSupport.DATAFIELD_NAME, name);
+					assertEquals(InstrSupport.DATAFIELD_DESC, desc);
+
+					if (opcode == Opcodes.GETSTATIC) {
+						assertEquals(InstrSupport.INITMETHOD_NAME,
+								methods.get(methods.size() - 1).name);
+					} else if (opcode == Opcodes.PUTSTATIC) {
+						if (isInterface) {
+							assertEquals(InstrSupport.CLINIT_NAME,
+									methods.get(methods.size() - 1).name);
+						} else {
+							assertEquals(InstrSupport.INITMETHOD_NAME,
+									methods.get(methods.size() - 1).name);
+						}
+					} else {
+						fail();
+					}
 				}
 
 				@Override
 				public void visitMethodInsn(int opcode, String owner,
 						String name, String desc, boolean itf) {
-					interfaceMethod = itf;
+					if ("getProbes".equals(name)) {
+						// method's owner is not interface:
+						assertFalse(itf);
+						return;
+					}
+					assertEquals(itf, isInterface);
+
+					assertEquals(Opcodes.INVOKESTATIC, opcode);
+					assertEquals("Foo", owner);
+					assertEquals(InstrSupport.INITMETHOD_NAME, name);
+					assertEquals(InstrSupport.INITMETHOD_DESC, desc);
 				}
 			};
 		}
@@ -235,13 +311,18 @@
 	}
 
 	void assertInitMethod(boolean frames) {
-		assertEquals(InstrSupport.INITMETHOD_NAME, cv.methodName);
-		assertEquals(InstrSupport.INITMETHOD_ACC, cv.methodAccess);
-		assertEquals(Boolean.valueOf(frames), Boolean.valueOf(cv.frames));
+		assertEquals(cv.methods.size(), 1);
+		cv.methods.get(0).assertInitMethod(frames);
+	}
+
+	void assertInitAndClinitMethods() {
+		assertEquals(2, cv.methods.size());
+		cv.methods.get(0).assertInitMethod(true);
+		cv.methods.get(1).assertClinit();
 	}
 
 	void assertNoInitMethod() {
-		assertNull(cv.methodName);
+		assertEquals(0, cv.methods.size());
 	}
 
 }
diff --git a/org.jacoco.core.test/src/org/jacoco/core/internal/instr/ProbeInserterTest.java b/org.jacoco.core.test/src/org/jacoco/core/internal/instr/ProbeInserterTest.java
index 6f2d7ec..6a2f9c6 100644
--- a/org.jacoco.core.test/src/org/jacoco/core/internal/instr/ProbeInserterTest.java
+++ b/org.jacoco.core.test/src/org/jacoco/core/internal/instr/ProbeInserterTest.java
@@ -39,9 +39,8 @@
 		expected = new MethodRecorder();
 		expectedVisitor = expected.getVisitor();
 		arrayStrategy = new IProbeArrayStrategy() {
-
-			public int storeInstance(MethodVisitor mv, int variable) {
-				mv.visitLdcInsn("init");
+			public int storeInstance(MethodVisitor mv, boolean clinit, int variable) {
+				mv.visitLdcInsn(clinit ? "clinit" : "init");
 				return 5;
 			}
 
@@ -57,7 +56,7 @@
 
 	@Test
 	public void testVariableStatic() {
-		ProbeInserter pi = new ProbeInserter(Opcodes.ACC_STATIC, "()V",
+		ProbeInserter pi = new ProbeInserter(Opcodes.ACC_STATIC, "m", "()V",
 				actualVisitor, arrayStrategy);
 		pi.insertProbe(0);
 
@@ -69,7 +68,7 @@
 
 	@Test
 	public void testVariableNonStatic() {
-		ProbeInserter pi = new ProbeInserter(0, "()V", actualVisitor,
+		ProbeInserter pi = new ProbeInserter(0, "m", "()V", actualVisitor,
 				arrayStrategy);
 		pi.insertProbe(0);
 
@@ -81,7 +80,7 @@
 
 	@Test
 	public void testVariableNonStatic_IZObject() {
-		ProbeInserter pi = new ProbeInserter(0, "(IZLjava/lang/Object;)V",
+		ProbeInserter pi = new ProbeInserter(0, "m", "(IZLjava/lang/Object;)V",
 				actualVisitor, arrayStrategy);
 		pi.insertProbe(0);
 
@@ -93,7 +92,7 @@
 
 	@Test
 	public void testVariableNonStatic_JD() {
-		ProbeInserter pi = new ProbeInserter(0, "(JD)V", actualVisitor,
+		ProbeInserter pi = new ProbeInserter(0, "m", "(JD)V", actualVisitor,
 				arrayStrategy);
 		pi.insertProbe(0);
 
@@ -105,7 +104,7 @@
 
 	@Test
 	public void testVisitCode() {
-		ProbeInserter pi = new ProbeInserter(0, "()V", actualVisitor,
+		ProbeInserter pi = new ProbeInserter(0, "m", "()V", actualVisitor,
 				arrayStrategy);
 		pi.visitCode();
 
@@ -113,8 +112,17 @@
 	}
 
 	@Test
+	public void testVisitClinit() {
+		ProbeInserter pi = new ProbeInserter(0, "<clinit>", "()V",
+				actualVisitor, arrayStrategy);
+		pi.visitCode();
+
+		expectedVisitor.visitLdcInsn("clinit");
+	}
+
+	@Test
 	public void testVisitVarIns() {
-		ProbeInserter pi = new ProbeInserter(0, "(II)V", actualVisitor,
+		ProbeInserter pi = new ProbeInserter(0, "m", "(II)V", actualVisitor,
 				arrayStrategy);
 
 		pi.visitVarInsn(Opcodes.ALOAD, 0);
@@ -135,7 +143,7 @@
 
 	@Test
 	public void testVisitIincInsn() {
-		ProbeInserter pi = new ProbeInserter(0, "(II)V", actualVisitor,
+		ProbeInserter pi = new ProbeInserter(0, "m", "(II)V", actualVisitor,
 				arrayStrategy);
 		pi.visitIincInsn(0, 100);
 		pi.visitIincInsn(1, 101);
@@ -155,7 +163,7 @@
 
 	@Test
 	public void testVisitLocalVariable() {
-		ProbeInserter pi = new ProbeInserter(0, "(II)V", actualVisitor,
+		ProbeInserter pi = new ProbeInserter(0, "m", "(II)V", actualVisitor,
 				arrayStrategy);
 
 		pi.visitLocalVariable(null, null, null, null, null, 0);
@@ -176,7 +184,7 @@
 
 	@Test
 	public void testVisitMaxs1() {
-		ProbeInserter pi = new ProbeInserter(0, "(II)V", actualVisitor,
+		ProbeInserter pi = new ProbeInserter(0, "m", "(II)V", actualVisitor,
 				arrayStrategy);
 		pi.visitCode();
 		pi.visitMaxs(0, 8);
@@ -187,7 +195,7 @@
 
 	@Test
 	public void testVisitMaxs2() {
-		ProbeInserter pi = new ProbeInserter(0, "(II)V", actualVisitor,
+		ProbeInserter pi = new ProbeInserter(0, "m", "(II)V", actualVisitor,
 				arrayStrategy);
 		pi.visitCode();
 		pi.visitMaxs(10, 8);
@@ -198,7 +206,7 @@
 
 	@Test
 	public void testVisitFrame() {
-		ProbeInserter pi = new ProbeInserter(0, "(J)V", actualVisitor,
+		ProbeInserter pi = new ProbeInserter(0, "m", "(J)V", actualVisitor,
 				arrayStrategy);
 
 		pi.visitFrame(Opcodes.F_NEW, 3, new Object[] { "Foo", Opcodes.LONG,
@@ -210,7 +218,7 @@
 
 	@Test
 	public void testVisitFrameNoLocals() {
-		ProbeInserter pi = new ProbeInserter(Opcodes.ACC_STATIC, "()V",
+		ProbeInserter pi = new ProbeInserter(Opcodes.ACC_STATIC, "m", "()V",
 				actualVisitor, arrayStrategy);
 
 		pi.visitFrame(Opcodes.F_NEW, 0, new Object[] {}, 0, new Object[0]);
@@ -221,7 +229,7 @@
 
 	@Test
 	public void testVisitFrameProbeAt0() {
-		ProbeInserter pi = new ProbeInserter(Opcodes.ACC_STATIC, "()V",
+		ProbeInserter pi = new ProbeInserter(Opcodes.ACC_STATIC, "m", "()V",
 				actualVisitor, arrayStrategy);
 
 		pi.visitFrame(Opcodes.F_NEW, 2, new Object[] { Opcodes.DOUBLE, "Foo" },
@@ -233,7 +241,7 @@
 
 	@Test
 	public void testFillOneWord() {
-		ProbeInserter pi = new ProbeInserter(Opcodes.ACC_STATIC, "(I)V",
+		ProbeInserter pi = new ProbeInserter(Opcodes.ACC_STATIC, "m", "(I)V",
 				actualVisitor, arrayStrategy);
 
 		pi.visitFrame(Opcodes.F_NEW, 0, new Object[] {}, 0, new Object[] {});
@@ -245,7 +253,7 @@
 
 	@Test
 	public void testFillTwoWord() {
-		ProbeInserter pi = new ProbeInserter(Opcodes.ACC_STATIC, "(J)V",
+		ProbeInserter pi = new ProbeInserter(Opcodes.ACC_STATIC, "m", "(J)V",
 				actualVisitor, arrayStrategy);
 
 		pi.visitFrame(Opcodes.F_NEW, 0, new Object[] {}, 0, new Object[] {});
@@ -257,7 +265,7 @@
 
 	@Test
 	public void testFillPartly() {
-		ProbeInserter pi = new ProbeInserter(Opcodes.ACC_STATIC, "(DIJ)V",
+		ProbeInserter pi = new ProbeInserter(Opcodes.ACC_STATIC, "m", "(DIJ)V",
 				actualVisitor, arrayStrategy);
 
 		pi.visitFrame(Opcodes.F_NEW, 1, new Object[] { Opcodes.DOUBLE }, 0,
@@ -271,7 +279,7 @@
 
 	@Test(expected = IllegalArgumentException.class)
 	public void testVisitFrame_invalidType() {
-		ProbeInserter pi = new ProbeInserter(0, "()V", actualVisitor,
+		ProbeInserter pi = new ProbeInserter(0, "m", "()V", actualVisitor,
 				arrayStrategy);
 		pi.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
 	}
diff --git a/org.jacoco.core.test/src/org/jacoco/core/test/InstrumentingLoader.java b/org.jacoco.core.test/src/org/jacoco/core/test/InstrumentingLoader.java
new file mode 100644
index 0000000..99bbaed
--- /dev/null
+++ b/org.jacoco.core.test/src/org/jacoco/core/test/InstrumentingLoader.java
@@ -0,0 +1,75 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2016 Mountainminds GmbH & Co. KG and Contributors
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Evgeny Mandrikov - initial API and implementation
+ *
+ *******************************************************************************/
+package org.jacoco.core.test;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.jacoco.core.data.ExecutionDataStore;
+import org.jacoco.core.data.SessionInfoStore;
+import org.jacoco.core.instr.Instrumenter;
+import org.jacoco.core.runtime.IRuntime;
+import org.jacoco.core.runtime.RuntimeData;
+import org.jacoco.core.runtime.SystemPropertiesRuntime;
+
+public final class InstrumentingLoader extends ClassLoader {
+
+	private final RuntimeData data;
+	private final IRuntime runtime;
+
+	public InstrumentingLoader() throws Exception {
+		data = new RuntimeData();
+		runtime = new SystemPropertiesRuntime();
+		runtime.startup(data);
+	}
+
+	@Override
+	protected synchronized Class<?> loadClass(String name, boolean resolve)
+			throws ClassNotFoundException {
+		if (name.startsWith("org.jacoco.core.test.validation.targets.")) {
+			final byte[] bytes;
+			try {
+				bytes = getClassBytes(name);
+			} catch (IOException e) {
+				throw new ClassNotFoundException("Unable to load", e);
+			}
+			final byte[] instrumented;
+			try {
+				instrumented = new Instrumenter(runtime).instrument(bytes,
+						name);
+			} catch (IOException e) {
+				throw new ClassNotFoundException("Unable to instrument", e);
+			}
+			final Class<?> c = defineClass(name, instrumented, 0,
+					instrumented.length);
+			if (resolve) {
+				resolveClass(c);
+			}
+			return c;
+		}
+		return super.loadClass(name, resolve);
+	}
+
+	public byte[] getClassBytes(String name) throws IOException {
+		final String resource = "/" + name.replace('.', '/') + ".class";
+		final InputStream in = getClass().getResourceAsStream(resource);
+		return TargetLoader.getClassDataAsBytes(in);
+	}
+
+	public ExecutionDataStore collect() {
+		final ExecutionDataStore store = new ExecutionDataStore();
+		data.collect(store, new SessionInfoStore(), false);
+		runtime.shutdown();
+		return store;
+	}
+
+}
diff --git a/org.jacoco.core.test/src/org/jacoco/core/test/TargetLoader.java b/org.jacoco.core.test/src/org/jacoco/core/test/TargetLoader.java
index 00bab63..de54015 100644
--- a/org.jacoco.core.test/src/org/jacoco/core/test/TargetLoader.java
+++ b/org.jacoco.core.test/src/org/jacoco/core/test/TargetLoader.java
@@ -58,7 +58,11 @@
 	}
 
 	public static byte[] getClassDataAsBytes(Class<?> clazz) throws IOException {
-		InputStream in = getClassData(clazz);
+		return getClassDataAsBytes(getClassData(clazz));
+	}
+
+	public static byte[] getClassDataAsBytes(InputStream in) throws
+			IOException {
 		ByteArrayOutputStream out = new ByteArrayOutputStream();
 		byte[] buffer = new byte[0x100];
 		int len;
diff --git a/org.jacoco.core.test/src/org/jacoco/core/test/validation/BadCycleClassTest.java b/org.jacoco.core.test/src/org/jacoco/core/test/validation/BadCycleClassTest.java
new file mode 100644
index 0000000..47820e6
--- /dev/null
+++ b/org.jacoco.core.test/src/org/jacoco/core/test/validation/BadCycleClassTest.java
@@ -0,0 +1,37 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2016 Mountainminds GmbH & Co. KG and Contributors
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Evgeny Mandrikov - initial API and implementation
+ *
+ *******************************************************************************/
+package org.jacoco.core.test.validation;
+
+import org.jacoco.core.analysis.ICounter;
+import org.jacoco.core.test.validation.targets.BadCycleClass;
+import org.junit.Test;
+
+/**
+ * Test of "bad cycles" with classes.
+ */
+public class BadCycleClassTest extends BadCycleTestBase {
+
+	public BadCycleClassTest() throws Exception {
+		super(BadCycleClass.class);
+	}
+
+	@Test
+	public void test() throws Exception {
+		loader.loadClass(BadCycleClass.Child.class.getName()).newInstance();
+
+		analyze(BadCycleClass.Child.class);
+		assertLine("1", ICounter.FULLY_COVERED);
+		assertLine("2", ICounter.FULLY_COVERED);
+		assertLine("3", ICounter.FULLY_COVERED);
+	}
+
+}
diff --git a/org.jacoco.core.test/src/org/jacoco/core/test/validation/BadCycleTestBase.java b/org.jacoco.core.test/src/org/jacoco/core/test/validation/BadCycleTestBase.java
new file mode 100644
index 0000000..dd7b499
--- /dev/null
+++ b/org.jacoco.core.test/src/org/jacoco/core/test/validation/BadCycleTestBase.java
@@ -0,0 +1,66 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2016 Mountainminds GmbH & Co. KG and Contributors
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Evgeny Mandrikov - initial API and implementation
+ *
+ *******************************************************************************/
+package org.jacoco.core.test.validation;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.IOException;
+import java.util.Collection;
+
+import org.jacoco.core.analysis.Analyzer;
+import org.jacoco.core.analysis.CoverageBuilder;
+import org.jacoco.core.analysis.IClassCoverage;
+import org.jacoco.core.analysis.ISourceFileCoverage;
+import org.jacoco.core.data.ExecutionDataStore;
+import org.jacoco.core.test.InstrumentingLoader;
+
+class BadCycleTestBase extends ValidationTestBase {
+
+	protected final InstrumentingLoader loader = new InstrumentingLoader();
+
+	BadCycleTestBase(final Class<?> target) throws Exception {
+		super(target);
+	}
+
+	BadCycleTestBase(final String srcFolder, final Class<?> target)
+			throws Exception {
+		super(srcFolder, target);
+	}
+
+	@Override
+	public final void setup() throws Exception {
+		// nop
+	}
+
+	@Override
+	protected final void run(Class<?> targetClass) throws Exception {
+		// nop
+	}
+
+	final void analyze(Class<?> cls) throws IOException {
+		final byte[] bytes = loader.getClassBytes(cls.getName());
+		final ExecutionDataStore store = loader.collect();
+
+		final CoverageBuilder builder = new CoverageBuilder();
+		final Analyzer analyzer = new Analyzer(store, builder);
+		analyzer.analyzeClass(bytes, "TestTarget");
+		final Collection<IClassCoverage> classes = builder.getClasses();
+		assertEquals(1, classes.size(), 0.0);
+		classCoverage = classes.iterator().next();
+		final Collection<ISourceFileCoverage> files = builder.getSourceFiles();
+		assertEquals(1, files.size(), 0.0);
+		sourceCoverage = files.iterator().next();
+
+		source = Source.getSourceFor(srcFolder, target);
+	}
+
+}
diff --git a/org.jacoco.core.test/src/org/jacoco/core/test/validation/targets/BadCycleClass.java b/org.jacoco.core.test/src/org/jacoco/core/test/validation/targets/BadCycleClass.java
new file mode 100644
index 0000000..44e40cd
--- /dev/null
+++ b/org.jacoco.core.test/src/org/jacoco/core/test/validation/targets/BadCycleClass.java
@@ -0,0 +1,44 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2016 Mountainminds GmbH & Co. KG and Contributors
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Evgeny Mandrikov - initial API and implementation
+ *
+ *******************************************************************************/
+package org.jacoco.core.test.validation.targets;
+
+public class BadCycleClass {
+
+	public static class Base {
+		static final Child b = new Child();
+
+		static {
+			b.someMethod();
+		}
+	}
+
+	public static class Child extends Base {
+
+		static {
+			Stubs.nop("child clinit"); // $line-3$
+		}
+
+		public Child() {
+			Stubs.nop("child init"); // $line-1$
+		}
+
+		void someMethod() {
+			Stubs.nop("child someMethod"); // $line-2$
+		}
+
+	}
+
+	public static void main(String[] args) {
+		new Child();
+	}
+
+}
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/instr/FieldProbeArrayStrategy.java b/org.jacoco.core/src/org/jacoco/core/internal/instr/ClassFieldProbeArrayStrategy.java
similarity index 84%
rename from org.jacoco.core/src/org/jacoco/core/internal/instr/FieldProbeArrayStrategy.java
rename to org.jacoco.core/src/org/jacoco/core/internal/instr/ClassFieldProbeArrayStrategy.java
index 26902c2..3b7bf91 100644
--- a/org.jacoco.core/src/org/jacoco/core/internal/instr/FieldProbeArrayStrategy.java
+++ b/org.jacoco.core/src/org/jacoco/core/internal/instr/ClassFieldProbeArrayStrategy.java
@@ -18,11 +18,11 @@
 import org.objectweb.asm.Opcodes;
 
 /**
- * The strategy for regular classes and Java 8 interfaces which adds a static
- * field to hold the probe array and a static initialization method requesting
- * the probe array from the runtime.
+ * The strategy for regular classes adds a static field to hold the probe array
+ * and a static initialization method requesting the probe array from the
+ * runtime.
  */
-class FieldProbeArrayStrategy implements IProbeArrayStrategy {
+class ClassFieldProbeArrayStrategy implements IProbeArrayStrategy {
 
 	/**
 	 * Frame stack with a single boolean array.
@@ -37,26 +37,22 @@
 	private final String className;
 	private final long classId;
 	private final boolean withFrames;
-	private final boolean isInterface;
-	private final int fieldAccess;
 	private final IExecutionDataAccessorGenerator accessorGenerator;
 
-	FieldProbeArrayStrategy(final String className, final long classId,
-			final boolean withFrames, final boolean isInterface,
-			final int fieldAccess,
+	ClassFieldProbeArrayStrategy(final String className, final long classId,
+			final boolean withFrames,
 			final IExecutionDataAccessorGenerator accessorGenerator) {
 		this.className = className;
 		this.classId = classId;
 		this.withFrames = withFrames;
-		this.isInterface = isInterface;
-		this.fieldAccess = fieldAccess;
 		this.accessorGenerator = accessorGenerator;
 	}
 
-	public int storeInstance(final MethodVisitor mv, final int variable) {
+	public int storeInstance(final MethodVisitor mv, final boolean clinit,
+			final int variable) {
 		mv.visitMethodInsn(Opcodes.INVOKESTATIC, className,
 				InstrSupport.INITMETHOD_NAME, InstrSupport.INITMETHOD_DESC,
-				isInterface);
+				false);
 		mv.visitVarInsn(Opcodes.ASTORE, variable);
 		return 1;
 	}
@@ -67,7 +63,7 @@
 	}
 
 	private void createDataField(final ClassVisitor cv) {
-		cv.visitField(fieldAccess, InstrSupport.DATAFIELD_NAME,
+		cv.visitField(InstrSupport.DATAFIELD_ACC, InstrSupport.DATAFIELD_NAME,
 				InstrSupport.DATAFIELD_DESC, null, null);
 	}
 
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/instr/ClassInstrumenter.java b/org.jacoco.core/src/org/jacoco/core/internal/instr/ClassInstrumenter.java
index 6cea48c..a70c673 100644
--- a/org.jacoco.core/src/org/jacoco/core/internal/instr/ClassInstrumenter.java
+++ b/org.jacoco.core/src/org/jacoco/core/internal/instr/ClassInstrumenter.java
@@ -70,7 +70,7 @@
 		}
 		final MethodVisitor frameEliminator = new DuplicateFrameEliminator(mv);
 		final ProbeInserter probeVariableInserter = new ProbeInserter(access,
-				desc, frameEliminator, probeArrayStrategy);
+				name, desc, frameEliminator, probeArrayStrategy);
 		return new MethodInstrumenter(probeVariableInserter,
 				probeVariableInserter);
 	}
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/instr/IProbeArrayStrategy.java b/org.jacoco.core/src/org/jacoco/core/internal/instr/IProbeArrayStrategy.java
index 8973785..87a415a 100644
--- a/org.jacoco.core/src/org/jacoco/core/internal/instr/IProbeArrayStrategy.java
+++ b/org.jacoco.core/src/org/jacoco/core/internal/instr/IProbeArrayStrategy.java
@@ -26,11 +26,13 @@
 	 * 
 	 * @param mv
 	 *            visitor to create code
+	 * @param clinit
+	 *            true in case of {@code <clinit>} method
 	 * @param variable
 	 *            variable index to store probe array to
 	 * @return maximum stack size required by the generated code
 	 */
-	int storeInstance(MethodVisitor mv, int variable);
+	int storeInstance(MethodVisitor mv, boolean clinit, int variable);
 
 	/**
 	 * Adds additional class members required by this strategy. This method is
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/instr/InstrSupport.java b/org.jacoco.core/src/org/jacoco/core/internal/instr/InstrSupport.java
index ab054df..5d34a66 100644
--- a/org.jacoco.core/src/org/jacoco/core/internal/instr/InstrSupport.java
+++ b/org.jacoco.core/src/org/jacoco/core/internal/instr/InstrSupport.java
@@ -34,14 +34,37 @@
 	/**
 	 * Access modifiers of the field that stores coverage information of a
 	 * class.
+	 *
+	 * According to Java Virtual Machine Specification <a href=
+	 * "https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-6.html#jvms-6.5.putstatic">
+	 * §6.5.putstatic</a> this field must not be final:
+	 *
+	 * <blockquote>
+	 * <p>
+	 * if the field is final, it must be declared in the current class, and the
+	 * instruction must occur in the {@code <clinit>} method of the current
+	 * class.
+	 * </p>
+	 * </blockquote>
 	 */
 	public static final int DATAFIELD_ACC = Opcodes.ACC_SYNTHETIC
-			| Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC | Opcodes.ACC_TRANSIENT
-			| Opcodes.ACC_FINAL;
+			| Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC | Opcodes.ACC_TRANSIENT;
 
 	/**
 	 * Access modifiers of the field that stores coverage information of a Java
 	 * 8 interface.
+	 *
+	 * According to Java Virtual Machine Specification <a href=
+	 * "https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.5-200-A.3">
+	 * §4.5</a>:
+	 *
+	 * <blockquote>
+	 * <p>
+	 * Fields of interfaces must have their ACC_PUBLIC, ACC_STATIC, and
+	 * ACC_FINAL flags set; they may have their ACC_SYNTHETIC flag set and must
+	 * not have any of the other flags.
+	 * </p>
+	 * </blockquote>
 	 */
 	public static final int DATAFIELD_INTF_ACC = Opcodes.ACC_SYNTHETIC
 			| Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC | Opcodes.ACC_FINAL;
@@ -71,6 +94,67 @@
 			| Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC;
 
 	/**
+	 * Name of the interface initialization method.
+	 *
+	 * According to Java Virtual Machine Specification <a href=
+	 * "https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html#jvms-2.9-200">
+	 * §2.9</a>:
+	 *
+	 * <blockquote>
+	 * <p>
+	 * A class or interface has at most one class or interface initialization
+	 * method and is initialized by invoking that method. The initialization
+	 * method of a class or interface has the special name {@code <clinit>},
+	 * takes no arguments, and is void.
+	 * </p>
+	 * <p>
+	 * Other methods named {@code <clinit>} in a class file are of no
+	 * consequence. They are not class or interface initialization methods. They
+	 * cannot be invoked by any Java Virtual Machine instruction and are never
+	 * invoked by the Java Virtual Machine itself.
+	 * </p>
+	 * <p>
+	 * In a class file whose version number is 51.0 or above, the method must
+	 * additionally have its ACC_STATIC flag set in order to be the class or
+	 * interface initialization method.
+	 * </p>
+	 * <p>
+	 * This requirement was introduced in Java SE 7. In a class file whose
+	 * version number is 50.0 or below, a method named {@code <clinit>} that is
+	 * void and takes no arguments is considered the class or interface
+	 * initialization method regardless of the setting of its ACC_STATIC flag.
+	 * </p>
+	 * </blockquote>
+	 *
+	 * And <a href=
+	 * "https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.6-200-A.6">
+	 * §4.6</a>:
+	 *
+	 * <blockquote>
+	 * <p>
+	 * Class and interface initialization methods are called implicitly by the
+	 * Java Virtual Machine. The value of their access_flags item is ignored
+	 * except for the setting of the ACC_STRICT flag.
+	 * </p>
+	 * </blockquote>
+	 */
+	static final String CLINIT_NAME = "<clinit>";
+
+	/**
+	 * Descriptor of the interface initialization method.
+	 *
+	 * @see #CLINIT_NAME
+	 */
+	static final String CLINIT_DESC = "()V";
+
+	/**
+	 * Access flags of the interface initialization method generated by JaCoCo.
+	 *
+	 * @see #CLINIT_NAME
+	 */
+	static final int CLINIT_ACC = Opcodes.ACC_SYNTHETIC | Opcodes.ACC_STATIC;
+
+	/**
 	 * Ensures that the given member does not correspond to a internal member
 	 * created by the instrumentation process. This would mean that the class is
 	 * already instrumented.
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/instr/InterfaceFieldProbeArrayStrategy.java b/org.jacoco.core/src/org/jacoco/core/internal/instr/InterfaceFieldProbeArrayStrategy.java
new file mode 100644
index 0000000..00228e9
--- /dev/null
+++ b/org.jacoco.core/src/org/jacoco/core/internal/instr/InterfaceFieldProbeArrayStrategy.java
@@ -0,0 +1,154 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2016 Mountainminds GmbH & Co. KG and Contributors
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Evgeny Mandrikov - initial API and implementation
+ *
+ *******************************************************************************/
+package org.jacoco.core.internal.instr;
+
+import org.jacoco.core.runtime.IExecutionDataAccessorGenerator;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+/**
+ * This strategy for Java 8 interfaces adds a static method requesting the probe
+ * array from the runtime, a static field to hold the probe array and adds code
+ * for its initialization into interface initialization method.
+ */
+class InterfaceFieldProbeArrayStrategy implements IProbeArrayStrategy {
+
+	/**
+	 * Frame stack with a single boolean array.
+	 */
+	private static final Object[] FRAME_STACK_ARRZ = new Object[] { InstrSupport.DATAFIELD_DESC };
+
+	/**
+	 * Empty frame locals.
+	 */
+	private static final Object[] FRAME_LOCALS_EMPTY = new Object[0];
+
+	private final String className;
+	private final long classId;
+	private final int probeCount;
+	private final IExecutionDataAccessorGenerator accessorGenerator;
+
+	private boolean seenClinit = false;
+
+	InterfaceFieldProbeArrayStrategy(final String className, final long classId,
+			final int probeCount,
+			final IExecutionDataAccessorGenerator accessorGenerator) {
+		this.className = className;
+		this.classId = classId;
+		this.probeCount = probeCount;
+		this.accessorGenerator = accessorGenerator;
+	}
+
+	public int storeInstance(final MethodVisitor mv, final boolean clinit,
+			final int variable) {
+		if (clinit) {
+			final int maxStack = accessorGenerator.generateDataAccessor(classId,
+					className, probeCount, mv);
+
+			// Stack[0]: [Z
+
+			mv.visitInsn(Opcodes.DUP);
+
+			// Stack[1]: [Z
+			// Stack[0]: [Z
+
+			mv.visitFieldInsn(Opcodes.PUTSTATIC, className,
+					InstrSupport.DATAFIELD_NAME, InstrSupport.DATAFIELD_DESC);
+
+			// Stack[0]: [Z
+
+			mv.visitVarInsn(Opcodes.ASTORE, variable);
+
+			seenClinit = true;
+			return Math.max(maxStack, 2);
+		} else {
+			mv.visitMethodInsn(Opcodes.INVOKESTATIC, className,
+					InstrSupport.INITMETHOD_NAME, InstrSupport.INITMETHOD_DESC,
+					true);
+			mv.visitVarInsn(Opcodes.ASTORE, variable);
+			return 1;
+		}
+	}
+
+	public void addMembers(final ClassVisitor cv, final int probeCount) {
+		createDataField(cv);
+		createInitMethod(cv, probeCount);
+		if (!seenClinit) {
+			createClinitMethod(cv, probeCount);
+		}
+	}
+
+	private void createDataField(final ClassVisitor cv) {
+		cv.visitField(InstrSupport.DATAFIELD_INTF_ACC,
+				InstrSupport.DATAFIELD_NAME, InstrSupport.DATAFIELD_DESC, null,
+				null);
+	}
+
+	private void createInitMethod(final ClassVisitor cv, final int probeCount) {
+		final MethodVisitor mv = cv.visitMethod(InstrSupport.INITMETHOD_ACC,
+				InstrSupport.INITMETHOD_NAME, InstrSupport.INITMETHOD_DESC,
+				null, null);
+		mv.visitCode();
+
+		// Load the value of the static data field:
+		mv.visitFieldInsn(Opcodes.GETSTATIC, className,
+				InstrSupport.DATAFIELD_NAME, InstrSupport.DATAFIELD_DESC);
+		mv.visitInsn(Opcodes.DUP);
+
+		// Stack[1]: [Z
+		// Stack[0]: [Z
+
+		// Skip initialization when we already have a data array:
+		final Label alreadyInitialized = new Label();
+		mv.visitJumpInsn(Opcodes.IFNONNULL, alreadyInitialized);
+
+		// Stack[0]: [Z
+
+		mv.visitInsn(Opcodes.POP);
+		final int size = accessorGenerator.generateDataAccessor(classId,
+				className, probeCount, mv);
+
+		// Stack[0]: [Z
+
+		// Return the class' probe array:
+		mv.visitFrame(Opcodes.F_NEW, 0, FRAME_LOCALS_EMPTY, 1,
+				FRAME_STACK_ARRZ);
+		mv.visitLabel(alreadyInitialized);
+		mv.visitInsn(Opcodes.ARETURN);
+
+		mv.visitMaxs(Math.max(size, 2), 0); // Maximum local stack size is 2
+		mv.visitEnd();
+	}
+
+	private void createClinitMethod(final ClassVisitor cv,
+			final int probeCount) {
+		final MethodVisitor mv = cv.visitMethod(InstrSupport.CLINIT_ACC,
+				InstrSupport.CLINIT_NAME, InstrSupport.CLINIT_DESC, null, null);
+		mv.visitCode();
+
+		final int maxStack = accessorGenerator.generateDataAccessor(classId,
+				className, probeCount, mv);
+
+		// Stack[0]: [Z
+
+		mv.visitFieldInsn(Opcodes.PUTSTATIC, className,
+				InstrSupport.DATAFIELD_NAME, InstrSupport.DATAFIELD_DESC);
+
+		mv.visitInsn(Opcodes.RETURN);
+
+		mv.visitMaxs(maxStack, 0);
+		mv.visitEnd();
+	}
+
+}
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/instr/LocalProbeArrayStrategy.java b/org.jacoco.core/src/org/jacoco/core/internal/instr/LocalProbeArrayStrategy.java
index f13dddf..ff0b884 100644
--- a/org.jacoco.core/src/org/jacoco/core/internal/instr/LocalProbeArrayStrategy.java
+++ b/org.jacoco.core/src/org/jacoco/core/internal/instr/LocalProbeArrayStrategy.java
@@ -38,7 +38,8 @@
 		this.accessorGenerator = accessorGenerator;
 	}
 
-	public int storeInstance(final MethodVisitor mv, final int variable) {
+	public int storeInstance(final MethodVisitor mv, final boolean clinit,
+			final int variable) {
 		final int maxStack = accessorGenerator.generateDataAccessor(classId,
 				className, probeCount, mv);
 		mv.visitVarInsn(Opcodes.ASTORE, variable);
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/instr/NoneProbeArrayStrategy.java b/org.jacoco.core/src/org/jacoco/core/internal/instr/NoneProbeArrayStrategy.java
index 6b29518..0c4d572 100644
--- a/org.jacoco.core/src/org/jacoco/core/internal/instr/NoneProbeArrayStrategy.java
+++ b/org.jacoco.core/src/org/jacoco/core/internal/instr/NoneProbeArrayStrategy.java
@@ -20,7 +20,8 @@
  */
 class NoneProbeArrayStrategy implements IProbeArrayStrategy {
 
-	public int storeInstance(final MethodVisitor mv, final int variable) {
+	public int storeInstance(final MethodVisitor mv, final boolean clinit,
+			final int variable) {
 		throw new UnsupportedOperationException();
 	}
 
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/instr/ProbeArrayStrategyFactory.java b/org.jacoco.core/src/org/jacoco/core/internal/instr/ProbeArrayStrategyFactory.java
index 8ce2f23..bd185fe 100644
--- a/org.jacoco.core/src/org/jacoco/core/internal/instr/ProbeArrayStrategyFactory.java
+++ b/org.jacoco.core/src/org/jacoco/core/internal/instr/ProbeArrayStrategyFactory.java
@@ -28,7 +28,8 @@
 
 	/**
 	 * Creates a suitable strategy instance for the class described by the given
-	 * reader.
+	 * reader. Created instance must be used only to process a class or
+	 * interface for which it has been created and must be used only once.
 	 * 
 	 * @param reader
 	 *            reader to get information about the class
@@ -50,16 +51,15 @@
 				return new NoneProbeArrayStrategy();
 			}
 			if (version >= Opcodes.V1_8 && counter.hasMethods()) {
-				return new FieldProbeArrayStrategy(className, classId,
-						withFrames, true, InstrSupport.DATAFIELD_INTF_ACC,
-						accessorGenerator);
+				return new InterfaceFieldProbeArrayStrategy(className, classId,
+						counter.getCount(), accessorGenerator);
 			} else {
 				return new LocalProbeArrayStrategy(className, classId,
 						counter.getCount(), accessorGenerator);
 			}
 		} else {
-			return new FieldProbeArrayStrategy(className, classId, withFrames,
-					false, InstrSupport.DATAFIELD_ACC, accessorGenerator);
+			return new ClassFieldProbeArrayStrategy(className, classId,
+					withFrames, accessorGenerator);
 		}
 	}
 
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/instr/ProbeCounter.java b/org.jacoco.core/src/org/jacoco/core/internal/instr/ProbeCounter.java
index 0028fcd..d45a8f9 100644
--- a/org.jacoco.core/src/org/jacoco/core/internal/instr/ProbeCounter.java
+++ b/org.jacoco.core/src/org/jacoco/core/internal/instr/ProbeCounter.java
@@ -30,7 +30,7 @@
 	@Override
 	public MethodProbesVisitor visitMethod(final int access, final String name,
 			final String desc, final String signature, final String[] exceptions) {
-		if (!"<clinit>".equals(name)) {
+		if (!InstrSupport.CLINIT_NAME.equals(name)) {
 			methods = true;
 		}
 		return null;
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/instr/ProbeInserter.java b/org.jacoco.core/src/org/jacoco/core/internal/instr/ProbeInserter.java
index 6cf8460..5e0084b 100644
--- a/org.jacoco.core/src/org/jacoco/core/internal/instr/ProbeInserter.java
+++ b/org.jacoco.core/src/org/jacoco/core/internal/instr/ProbeInserter.java
@@ -27,6 +27,12 @@
 
 	private final IProbeArrayStrategy arrayStrategy;
 
+	/**
+	 * <code>true</code> if method is a class or interface initialization
+	 * method.
+	 */
+	private final boolean clinit;
+
 	/** Position of the inserted variable. */
 	private final int variable;
 
@@ -37,7 +43,9 @@
 	 * Creates a new {@link ProbeInserter}.
 	 * 
 	 * @param access
-	 *            access flags of the adapted method.
+	 *            access flags of the adapted method
+	 * @param name
+	 *            the method's name
 	 * @param desc
 	 *            the method's descriptor
 	 * @param mv
@@ -46,9 +54,10 @@
 	 *            callback to create the code that retrieves the reference to
 	 *            the probe array
 	 */
-	ProbeInserter(final int access, final String desc, final MethodVisitor mv,
+	ProbeInserter(final int access, final String name, final String desc, final MethodVisitor mv,
 			final IProbeArrayStrategy arrayStrategy) {
 		super(JaCoCo.ASM_API_VERSION, mv);
+		this.clinit = InstrSupport.CLINIT_NAME.equals(name);
 		this.arrayStrategy = arrayStrategy;
 		int pos = (Opcodes.ACC_STATIC & access) == 0 ? 1 : 0;
 		for (final Type t : Type.getArgumentTypes(desc)) {
@@ -82,7 +91,7 @@
 
 	@Override
 	public void visitCode() {
-		accessorStackSize = arrayStrategy.storeInstance(mv, variable);
+		accessorStackSize = arrayStrategy.storeInstance(mv, clinit, variable);
 		mv.visitCode();
 	}
 
diff --git a/org.jacoco.doc/docroot/doc/changes.html b/org.jacoco.doc/docroot/doc/changes.html
index b2e9d4b..e62b4a8 100644
--- a/org.jacoco.doc/docroot/doc/changes.html
+++ b/org.jacoco.doc/docroot/doc/changes.html
@@ -29,6 +29,10 @@
 
 <h3>Fixed Bugs</h3>
 <ul>
+  <li>Fix instrumentation to not violate Java Virtual Machine Specification regarding
+      initialization of final fields, otherwise <code>IllegalAccessError</code>
+      will be thrown starting from OpenJDK 9 EA b127
+      (GitHub <a href="https://github.com/jacoco/jacoco/issues/434">#434</a>).</li>
   <li>Fix instrumentation of interfaces with default methods to not create incorrect
       constant pool entries, which lead to <code>IncompatibleClassChangeError</code>
       starting from OpenJDK 9 EA b122
diff --git a/org.jacoco.tests/pom.xml b/org.jacoco.tests/pom.xml
index f91f275..742e90d 100644
--- a/org.jacoco.tests/pom.xml
+++ b/org.jacoco.tests/pom.xml
@@ -50,7 +50,7 @@
         <artifactId>jacoco-maven-plugin</artifactId>
         <version>${project.version}</version>
         <configuration>
-          <exclClassLoaders>sun.reflect.DelegatingClassLoader:org.jacoco.core.test.TargetLoader</exclClassLoaders>
+          <exclClassLoaders>sun.reflect.DelegatingClassLoader:org.jacoco.core.test.TargetLoader:org.jacoco.core.test.InstrumentingLoader</exclClassLoaders>
           <sessionId>${project.artifactId}</sessionId>
           <includes>
             <include>${jacoco.includes}</include>