diff --git a/lib/cglib-2.2.2.jar b/lib/cglib-2.2.2.jar new file mode 100644 index 0000000..564b9f8 Binary files /dev/null and b/lib/cglib-2.2.2.jar differ diff --git a/src/org/fenixsoft/jvm/chapter10/AutoBoxing.java b/src/org/fenixsoft/jvm/chapter10/AutoBoxing.java new file mode 100644 index 0000000..21968f2 --- /dev/null +++ b/src/org/fenixsoft/jvm/chapter10/AutoBoxing.java @@ -0,0 +1,22 @@ +package org.fenixsoft.jvm.chapter10; + +/** + * @author zzm + */ +public class AutoBoxing { + public static void main(String[] args) { + Integer a = 1; + Integer b = 2; + Integer c = 3; + Integer d = 3; + Integer e = 321; + Integer f = 321; + Long g = 3L; + System.out.println(c == d); + System.out.println(e == f); + System.out.println(c == (a + b)); + System.out.println(c.equals(a + b)); + System.out.println(g == (a + b)); + System.out.println(g.equals(a + b)); + } +} diff --git a/src/org/fenixsoft/jvm/chapter10/NameChecker.java b/src/org/fenixsoft/jvm/chapter10/NameChecker.java new file mode 100644 index 0000000..9b1cfdd --- /dev/null +++ b/src/org/fenixsoft/jvm/chapter10/NameChecker.java @@ -0,0 +1,180 @@ +package org.fenixsoft.jvm.chapter10; + +import javax.annotation.processing.Messager; +import javax.annotation.processing.ProcessingEnvironment; +import javax.lang.model.element.*; +import javax.lang.model.util.ElementScanner6; +import java.util.EnumSet; + +import static javax.lang.model.element.ElementKind.*; +import static javax.lang.model.element.ElementKind.INTERFACE; +import static javax.lang.model.element.Modifier.*; +import static javax.tools.Diagnostic.Kind.WARNING; + +/** + * 程序名称规范的编译器插件:
+ * 如果程序命名不合规范,将会输出一个编译器的WARNING信息 + */ +public class NameChecker { + private final Messager messager; + + NameCheckScanner nameCheckScanner = new NameCheckScanner(); + + NameChecker(ProcessingEnvironment processsingEnv) { + this.messager = processsingEnv.getMessager(); + } + + /** + * 对Java程序命名进行检查,根据《Java语言规范》第三版第6.8节的要求,Java程序命名应当符合下列格式: + * + * + */ + public void checkNames(Element element) { + nameCheckScanner.scan(element); + } + + /** + * 名称检查器实现类,继承了JDK 6中新提供的ElementScanner6
+ * 将会以Visitor模式访问抽象语法树中的元素 + */ + private class NameCheckScanner extends ElementScanner6 { + + /** + * 此方法用于检查Java类 + */ + @Override + public Void visitType(TypeElement e, Void p) { + scan(e.getTypeParameters(), p); + checkCamelCase(e, true); + super.visitType(e, p); + return null; + } + + /** + * 检查方法命名是否合法 + */ + @Override + public Void visitExecutable(ExecutableElement e, Void p) { + if (e.getKind() == METHOD) { + Name name = e.getSimpleName(); + if (name.contentEquals(e.getEnclosingElement().getSimpleName())) + messager.printMessage(WARNING, "一个普通方法 “" + name + "”不应当与类名重复,避免与构造函数产生混淆", e); + checkCamelCase(e, false); + } + super.visitExecutable(e, p); + return null; + } + + /** + * 检查变量命名是否合法 + */ + @Override + public Void visitVariable(VariableElement e, Void p) { + // 如果这个Variable是枚举或常量,则按大写命名检查,否则按照驼式命名法规则检查 + if (e.getKind() == ENUM_CONSTANT || e.getConstantValue() != null || heuristicallyConstant(e)) + checkAllCaps(e); + else + checkCamelCase(e, false); + return null; + } + + /** + * 判断一个变量是否是常量 + */ + private boolean heuristicallyConstant(VariableElement e) { + if (e.getEnclosingElement().getKind() == INTERFACE) + return true; + else if (e.getKind() == FIELD && e.getModifiers().containsAll(EnumSet.of(PUBLIC, STATIC, FINAL))) + return true; + else { + return false; + } + } + + /** + * 检查传入的Element是否符合驼式命名法,如果不符合,则输出警告信息 + */ + private void checkCamelCase(Element e, boolean initialCaps) { + String name = e.getSimpleName().toString(); + boolean previousUpper = false; + boolean conventional = true; + int firstCodePoint = name.codePointAt(0); + + if (Character.isUpperCase(firstCodePoint)) { + previousUpper = true; + if (!initialCaps) { + messager.printMessage(WARNING, "名称“" + name + "”应当以小写字母开头", e); + return; + } + } else if (Character.isLowerCase(firstCodePoint)) { + if (initialCaps) { + messager.printMessage(WARNING, "名称“" + name + "”应当以大写字母开头", e); + return; + } + } else + conventional = false; + + if (conventional) { + int cp = firstCodePoint; + for (int i = Character.charCount(cp); i < name.length(); i += Character.charCount(cp)) { + cp = name.codePointAt(i); + if (Character.isUpperCase(cp)) { + if (previousUpper) { + conventional = false; + break; + } + previousUpper = true; + } else + previousUpper = false; + } + } + + if (!conventional) + messager.printMessage(WARNING, "名称“" + name + "”应当符合驼式命名法(Camel Case Names)", e); + } + + /** + * 大写命名检查,要求第一个字母必须是大写的英文字母,其余部分可以是下划线或大写字母 + */ + private void checkAllCaps(Element e) { + String name = e.getSimpleName().toString(); + + boolean conventional = true; + int firstCodePoint = name.codePointAt(0); + + if (!Character.isUpperCase(firstCodePoint)) + conventional = false; + else { + boolean previousUnderscore = false; + int cp = firstCodePoint; + for (int i = Character.charCount(cp); i < name.length(); i += Character.charCount(cp)) { + cp = name.codePointAt(i); + if (cp == (int) '_') { + if (previousUnderscore) { + conventional = false; + break; + } + previousUnderscore = true; + } else { + previousUnderscore = false; + if (!Character.isUpperCase(cp) && !Character.isDigit(cp)) { + conventional = false; + break; + } + } + } + } + + if (!conventional) + messager.printMessage(WARNING, "常量“" + name + "”应当全部以大写字母或下划线命名,并且以字母开头", e); + } + } +} diff --git a/src/org/fenixsoft/jvm/chapter12/VolatileTest.java b/src/org/fenixsoft/jvm/chapter12/VolatileTest.java new file mode 100644 index 0000000..b216471 --- /dev/null +++ b/src/org/fenixsoft/jvm/chapter12/VolatileTest.java @@ -0,0 +1,38 @@ +package org.fenixsoft.jvm.chapter12; + +/** + * volatile变量自增运算测试 + * + * @author zzm + */ +public class VolatileTest { + + public static volatile int race = 0; + + public static void increase() { + race++; + } + + private static final int THREADS_COUNT = 20; + + public static void main(String[] args) { + Thread[] threads = new Thread[THREADS_COUNT]; + for (int i = 0; i < THREADS_COUNT; i++) { + threads[i] = new Thread(new Runnable() { + @Override + public void run() { + for (int i = 0; i < 10000; i++) { + increase(); + } + } + }); + threads[i].start(); + } + + // 等待所有累加线程都结束 + while (Thread.activeCount() > 1) + Thread.yield(); + + System.out.println(race); + } +} diff --git a/src/org/fenixsoft/jvm/chapter13/AtomicTest.java b/src/org/fenixsoft/jvm/chapter13/AtomicTest.java new file mode 100644 index 0000000..0b53f7a --- /dev/null +++ b/src/org/fenixsoft/jvm/chapter13/AtomicTest.java @@ -0,0 +1,40 @@ +package org.fenixsoft.jvm.chapter13; + +import java.util.concurrent.atomic.AtomicInteger; + +/** + * Atomic变量自增运算测试 + * + * @author zzm + */ +public class AtomicTest { + + public static AtomicInteger race = new AtomicInteger(0); + + public static void increase() { + race.incrementAndGet(); + } + + private static final int THREADS_COUNT = 20; + + public static void main(String[] args) throws Exception { + Thread[] threads = new Thread[THREADS_COUNT]; + for (int i = 0; i < THREADS_COUNT; i++) { + threads[i] = new Thread(new Runnable() { + @Override + public void run() { + for (int i = 0; i < 10000; i++) { + increase(); + } + } + }); + threads[i].start(); + } + + while (Thread.activeCount() > 1) + Thread.yield(); + + System.out.println(race); + } +} + diff --git a/src/org/fenixsoft/jvm/chapter13/VectorTestCase_1.java b/src/org/fenixsoft/jvm/chapter13/VectorTestCase_1.java new file mode 100644 index 0000000..37b92a1 --- /dev/null +++ b/src/org/fenixsoft/jvm/chapter13/VectorTestCase_1.java @@ -0,0 +1,44 @@ +package org.fenixsoft.jvm.chapter13; + +import java.util.Vector; + +/** + * @author zzm + */ +public class VectorTestCase_1 { + + private static Vector vector = new Vector(); + + public static void main(String[] args) { + while (true) { + for (int i = 0; i < 10; i++) { + vector.add(i); + } + + Thread removeThread = new Thread(new Runnable() { + @Override + public void run() { + for (int i = 0; i < vector.size(); i++) { + vector.remove(i); + } + } + }); + + Thread printThread = new Thread(new Runnable() { + @Override + public void run() { + for (int i = 0; i < vector.size(); i++) { + System.out.println((vector.get(i))); + } + } + }); + + removeThread.start(); + printThread.start(); + + //不要同时产生过多的线程,否则会导致操作系统假死 + while (Thread.activeCount() > 20) ; + } + } + +} diff --git a/src/org/fenixsoft/jvm/chapter13/VectorTestCase_2.java b/src/org/fenixsoft/jvm/chapter13/VectorTestCase_2.java new file mode 100644 index 0000000..cbbcc72 --- /dev/null +++ b/src/org/fenixsoft/jvm/chapter13/VectorTestCase_2.java @@ -0,0 +1,48 @@ +package org.fenixsoft.jvm.chapter13; + +import java.util.Vector; + +/** + * @author zzm + */ +public class VectorTestCase_2 { + + private static Vector vector = new Vector(); + + public static void main(String[] args) { + while (true) { + for (int i = 0; i < 10; i++) { + vector.add(i); + } + + Thread removeThread = new Thread(new Runnable() { + @Override + public void run() { + synchronized (vector) { + for (int i = 0; i < vector.size(); i++) { + vector.remove(i); + } + } + } + }); + + Thread printThread = new Thread(new Runnable() { + @Override + public void run() { + synchronized (vector) { + for (int i = 0; i < vector.size(); i++) { + System.out.println((vector.get(i))); + } + } + } + }); + + removeThread.start(); + printThread.start(); + + //不要同时产生过多的线程,否则会导致操作系统假死 + while (Thread.activeCount() > 20) ; + } + } + +} diff --git a/src/org/fenixsoft/jvm/chapter2/DirectMemoryOOM.java b/src/org/fenixsoft/jvm/chapter2/DirectMemoryOOM.java new file mode 100644 index 0000000..b6698d5 --- /dev/null +++ b/src/org/fenixsoft/jvm/chapter2/DirectMemoryOOM.java @@ -0,0 +1,24 @@ +package org.fenixsoft.jvm.chapter2; + +import sun.misc.Unsafe; + +import java.lang.reflect.Field; + +/** + * VM Args:-Xmx20M -XX:MaxDirectMemorySize=10M + * + * @author zzm + */ +public class DirectMemoryOOM { + + private static final int _1MB = 1024 * 1024; + + public static void main(String[] args) throws Exception { + Field unsafeField = Unsafe.class.getDeclaredFields()[0]; + unsafeField.setAccessible(true); + Unsafe unsafe = (Unsafe) unsafeField.get(null); + while (true) { + unsafe.allocateMemory(_1MB); + } + } +} diff --git a/src/org/fenixsoft/jvm/chapter2/HeapOOM.java b/src/org/fenixsoft/jvm/chapter2/HeapOOM.java new file mode 100644 index 0000000..0a814b7 --- /dev/null +++ b/src/org/fenixsoft/jvm/chapter2/HeapOOM.java @@ -0,0 +1,24 @@ +package org.fenixsoft.jvm.chapter2; + +import java.util.ArrayList; +import java.util.List; + +/** + * VM Args:-Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError + * + * @author zzm + */ +public class HeapOOM { + + static class OOMObject { + } + + public static void main(String[] args) { + List list = new ArrayList(); + + while (true) { + list.add(new OOMObject()); + } + } +} + diff --git a/src/org/fenixsoft/jvm/chapter2/JavaMethodAreaOOM.java b/src/org/fenixsoft/jvm/chapter2/JavaMethodAreaOOM.java new file mode 100644 index 0000000..2cbea4e --- /dev/null +++ b/src/org/fenixsoft/jvm/chapter2/JavaMethodAreaOOM.java @@ -0,0 +1,32 @@ +package org.fenixsoft.jvm.chapter2; + +import net.sf.cglib.proxy.Enhancer; +import net.sf.cglib.proxy.MethodInterceptor; +import net.sf.cglib.proxy.MethodProxy; + +import java.lang.reflect.Method; + +/** + * VM Args: -XX:PermSize=10M -XX:MaxPermSize=10M + * + * @author zzm + */ +public class JavaMethodAreaOOM { + + public static void main(String[] args) { + while (true) { + Enhancer enhancer = new Enhancer(); + enhancer.setSuperclass(OOMObject.class); + enhancer.setUseCache(false); + enhancer.setCallback(new MethodInterceptor() { + public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { + return proxy.invokeSuper(obj, args); + } + }); + enhancer.create(); + } + } + + static class OOMObject { + } +} diff --git a/src/org/fenixsoft/jvm/chapter2/JavaVMStackOOM.java b/src/org/fenixsoft/jvm/chapter2/JavaVMStackOOM.java new file mode 100644 index 0000000..9c680b6 --- /dev/null +++ b/src/org/fenixsoft/jvm/chapter2/JavaVMStackOOM.java @@ -0,0 +1,31 @@ +package org.fenixsoft.jvm.chapter2; + +/** + * VM Args:-Xss2M (这时候不妨设大些,请在32位系统下运行) + * + * @author zzm + */ +public class JavaVMStackOOM { + + private void dontStop() { + while (true) { + } + } + + public void stackLeakByThread() { + while (true) { + Thread thread = new Thread(new Runnable() { + @Override + public void run() { + dontStop(); + } + }); + thread.start(); + } + } + + public static void main(String[] args) throws Throwable { + JavaVMStackOOM oom = new JavaVMStackOOM(); + oom.stackLeakByThread(); + } +} diff --git a/src/org/fenixsoft/jvm/chapter2/JavaVMStackSOF_1.java b/src/org/fenixsoft/jvm/chapter2/JavaVMStackSOF_1.java new file mode 100644 index 0000000..b3843a1 --- /dev/null +++ b/src/org/fenixsoft/jvm/chapter2/JavaVMStackSOF_1.java @@ -0,0 +1,26 @@ +package org.fenixsoft.jvm.chapter2; + +/** + * VM Args:-Xss128k + * + * @author zzm + */ +public class JavaVMStackSOF_1 { + + private int stackLength = 1; + + public void stackLeak() { + stackLength++; + stackLeak(); + } + + public static void main(String[] args) { + JavaVMStackSOF_1 oom = new JavaVMStackSOF_1(); + try { + oom.stackLeak(); + } catch (Throwable e) { + System.out.println("stack length:" + oom.stackLength); + throw e; + } + } +} diff --git a/src/org/fenixsoft/jvm/chapter2/JavaVMStackSOF_2.java b/src/org/fenixsoft/jvm/chapter2/JavaVMStackSOF_2.java new file mode 100644 index 0000000..86d5710 --- /dev/null +++ b/src/org/fenixsoft/jvm/chapter2/JavaVMStackSOF_2.java @@ -0,0 +1,26 @@ +package org.fenixsoft.jvm.chapter2; + +/** + * VM Args:-Xss128k + * + * @author zzm + */ +public class JavaVMStackSOF_2 { + + private int stackLength = 1; + + public void stackLeak() { + stackLength++; + stackLeak(); + } + + public static void main(String[] args) { + JavaVMStackSOF_2 oom = new JavaVMStackSOF_2(); + try { + oom.stackLeak(); + } catch (Throwable e) { + System.out.println("stack length:" + oom.stackLength); + throw e; + } + } +} diff --git a/src/org/fenixsoft/jvm/chapter2/JavaVMStackSOF_3.java b/src/org/fenixsoft/jvm/chapter2/JavaVMStackSOF_3.java new file mode 100644 index 0000000..8e16652 --- /dev/null +++ b/src/org/fenixsoft/jvm/chapter2/JavaVMStackSOF_3.java @@ -0,0 +1,66 @@ +package org.fenixsoft.jvm.chapter2; + +/** + * VM: JDK 1.0.2, Sun Classic VM + * + * @author zzm + */ +public class JavaVMStackSOF_3 { + private static int stackLength = 0; + + public static void test() { + long unused1, unused2, unused3, unused4, unused5, + unused6, unused7, unused8, unused9, unused10, + unused11, unused12, unused13, unused14, unused15, + unused16, unused17, unused18, unused19, unused20, + unused21, unused22, unused23, unused24, unused25, + unused26, unused27, unused28, unused29, unused30, + unused31, unused32, unused33, unused34, unused35, + unused36, unused37, unused38, unused39, unused40, + unused41, unused42, unused43, unused44, unused45, + unused46, unused47, unused48, unused49, unused50, + unused51, unused52, unused53, unused54, unused55, + unused56, unused57, unused58, unused59, unused60, + unused61, unused62, unused63, unused64, unused65, + unused66, unused67, unused68, unused69, unused70, + unused71, unused72, unused73, unused74, unused75, + unused76, unused77, unused78, unused79, unused80, + unused81, unused82, unused83, unused84, unused85, + unused86, unused87, unused88, unused89, unused90, + unused91, unused92, unused93, unused94, unused95, + unused96, unused97, unused98, unused99, unused100; + + stackLength ++; + test(); + + unused1 = unused2 = unused3 = unused4 = unused5 = + unused6 = unused7 = unused8 = unused9 = unused10 = + unused11 = unused12 = unused13 = unused14 = unused15 = + unused16 = unused17 = unused18 = unused19 = unused20 = + unused21 = unused22 = unused23 = unused24 = unused25 = + unused26 = unused27 = unused28 = unused29 = unused30 = + unused31 = unused32 = unused33 = unused34 = unused35 = + unused36 = unused37 = unused38 = unused39 = unused40 = + unused41 = unused42 = unused43 = unused44 = unused45 = + unused46 = unused47 = unused48 = unused49 = unused50 = + unused51 = unused52 = unused53 = unused54 = unused55 = + unused56 = unused57 = unused58 = unused59 = unused60 = + unused61 = unused62 = unused63 = unused64 = unused65 = + unused66 = unused67 = unused68 = unused69 = unused70 = + unused71 = unused72 = unused73 = unused74 = unused75 = + unused76 = unused77 = unused78 = unused79 = unused80 = + unused81 = unused82 = unused83 = unused84 = unused85 = + unused86 = unused87 = unused88 = unused89 = unused90 = + unused91 = unused92 = unused93 = unused94 = unused95 = + unused96 = unused97 = unused98 = unused99 = unused100 = 0; + } + + public static void main(String[] args) { + try { + test(); + }catch (Error e){ + System.out.println("stack length:" + stackLength); + throw e; + } + } +} diff --git a/src/org/fenixsoft/jvm/chapter2/RuntimeConstantPoolOOM_1.java b/src/org/fenixsoft/jvm/chapter2/RuntimeConstantPoolOOM_1.java new file mode 100644 index 0000000..e41e14d --- /dev/null +++ b/src/org/fenixsoft/jvm/chapter2/RuntimeConstantPoolOOM_1.java @@ -0,0 +1,22 @@ +package org.fenixsoft.jvm.chapter2; + +import java.util.HashSet; +import java.util.Set; + +/** + * VM Args:-XX:PermSize=6M -XX:MaxPermSize=6M + * + * @author zzm + */ +public class RuntimeConstantPoolOOM_1 { + + public static void main(String[] args) { + // 使用Set保持着常量池引用,避免Full GC回收常量池行为 + Set set = new HashSet(); + // 在short范围内足以让6MB的PermSize产生OOM了 + short i = 0; + while (true) { + set.add(String.valueOf(i++).intern()); + } + } +} diff --git a/src/org/fenixsoft/jvm/chapter2/RuntimeConstantPoolOOM_2.java b/src/org/fenixsoft/jvm/chapter2/RuntimeConstantPoolOOM_2.java new file mode 100644 index 0000000..c96bb21 --- /dev/null +++ b/src/org/fenixsoft/jvm/chapter2/RuntimeConstantPoolOOM_2.java @@ -0,0 +1,12 @@ +package org.fenixsoft.jvm.chapter2; + +public class RuntimeConstantPoolOOM_2 { + + public static void main(String[] args) { + String str1 = new StringBuilder("计算机").append("软件").toString(); + System.out.println(str1.intern() == str1); + + String str2 = new StringBuilder("ja").append("va").toString(); + System.out.println(str2.intern() == str2); + } +} diff --git a/src/org/fenixsoft/jvm/chapter3/FinalizeEscapeGC.java b/src/org/fenixsoft/jvm/chapter3/FinalizeEscapeGC.java new file mode 100644 index 0000000..946bd9c --- /dev/null +++ b/src/org/fenixsoft/jvm/chapter3/FinalizeEscapeGC.java @@ -0,0 +1,50 @@ +package org.fenixsoft.jvm.chapter3; + +/** + * 此代码演示了两点: + * 1.对象可以在被GC时自我拯救。 + * 2.这种自救的机会只有一次,因为一个对象的finalize()方法最多只会被系统自动调用一次 + * + * @author zzm + */ +public class FinalizeEscapeGC { + + public static FinalizeEscapeGC SAVE_HOOK = null; + + public void isAlive() { + System.out.println("yes, i am still alive :)"); + } + + @Override + protected void finalize() throws Throwable { + super.finalize(); + System.out.println("finalize method executed!"); + FinalizeEscapeGC.SAVE_HOOK = this; + } + + public static void main(String[] args) throws Throwable { + SAVE_HOOK = new FinalizeEscapeGC(); + + //对象第一次成功拯救自己 + SAVE_HOOK = null; + System.gc(); + // 因为Finalizer方法优先级很低,暂停0.5秒,以等待它 + Thread.sleep(500); + if (SAVE_HOOK != null) { + SAVE_HOOK.isAlive(); + } else { + System.out.println("no, i am dead :("); + } + + // 下面这段代码与上面的完全相同,但是这次自救却失败了 + SAVE_HOOK = null; + System.gc(); + // 因为Finalizer方法优先级很低,暂停0.5秒,以等待它 + Thread.sleep(500); + if (SAVE_HOOK != null) { + SAVE_HOOK.isAlive(); + } else { + System.out.println("no, i am dead :("); + } + } +} diff --git a/src/org/fenixsoft/jvm/chapter3/ReferenceCountingGC.java b/src/org/fenixsoft/jvm/chapter3/ReferenceCountingGC.java new file mode 100644 index 0000000..173bb21 --- /dev/null +++ b/src/org/fenixsoft/jvm/chapter3/ReferenceCountingGC.java @@ -0,0 +1,31 @@ +package org.fenixsoft.jvm.chapter3; + +/** + * testGC()方法执行后,objA和objB会不会被GC呢? + * + * @author zzm + */ +public class ReferenceCountingGC { + + public Object instance = null; + + private static final int _1MB = 1024 * 1024; + + /** + * 这个成员属性的唯一意义就是占点内存,以便在能在GC日志中看清楚是否有回收过 + */ + private byte[] bigSize = new byte[2 * _1MB]; + + public static void testGC() { + ReferenceCountingGC objA = new ReferenceCountingGC(); + ReferenceCountingGC objB = new ReferenceCountingGC(); + objA.instance = objB; + objB.instance = objA; + + objA = null; + objB = null; + + // 假设在这行发生GC,objA和objB是否能被回收? + System.gc(); + } +} diff --git a/src/org/fenixsoft/jvm/chapter3/TestSerialGCAllocation.java b/src/org/fenixsoft/jvm/chapter3/TestSerialGCAllocation.java new file mode 100644 index 0000000..0ecb8af --- /dev/null +++ b/src/org/fenixsoft/jvm/chapter3/TestSerialGCAllocation.java @@ -0,0 +1,78 @@ +package org.fenixsoft.jvm.chapter3; + +/** + * VM参数:+XX:UseSerialGC + * @author zzm + */ +public class TestSerialGCAllocation { + + private static final int _1MB = 1024 * 1024; + + /** + * VM参数:-verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8 + */ + public static void testAllocation() { + byte[] allocation1, allocation2, allocation3, allocation4; + allocation1 = new byte[2 * _1MB]; + allocation2 = new byte[2 * _1MB]; + allocation3 = new byte[2 * _1MB]; + allocation4 = new byte[4 * _1MB]; // 出现一次Minor GC + } + + /** + * VM参数:-verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8 + * -XX:PretenureSizeThreshold=3145728 + */ + public static void testPretenureSizeThreshold() { + byte[] allocation; + allocation = new byte[4 * _1MB]; //直接分配在老年代中 + } + + /** + * VM参数:-verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=1 + * -XX:+PrintTenuringDistribution + */ + public static void testTenuringThreshold() { + byte[] allocation1, allocation2, allocation3; + allocation1 = new byte[_1MB / 4]; // 什么时候进入老年代决定于XX:MaxTenuringThreshold设置 + allocation2 = new byte[4 * _1MB]; + allocation3 = new byte[4 * _1MB]; + allocation3 = null; + allocation3 = new byte[4 * _1MB]; + } + + /** + * VM参数:-verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=15 + * -XX:+PrintTenuringDistribution + */ + public static void testTenuringThreshold2() { + byte[] allocation1, allocation2, allocation3, allocation4; + allocation1 = new byte[_1MB / 4]; // allocation1+allocation2大于survivo空间一半 + allocation2 = new byte[_1MB / 4]; + allocation3 = new byte[4 * _1MB]; + allocation4 = new byte[4 * _1MB]; + allocation4 = null; + allocation4 = new byte[4 * _1MB]; + } + + /** + * VM参数:-Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:-HandlePromotionFailure + */ + public static void testHandlePromotion() { + byte[] allocation1, allocation2, allocation3, allocation4, allocation5, allocation6, allocation7; + allocation1 = new byte[2 * _1MB]; + allocation2 = new byte[2 * _1MB]; + allocation3 = new byte[2 * _1MB]; + allocation1 = null; + allocation4 = new byte[2 * _1MB]; + allocation5 = new byte[2 * _1MB]; + allocation6 = new byte[2 * _1MB]; + allocation4 = null; + allocation5 = null; + allocation6 = null; + allocation7 = new byte[2 * _1MB]; + } + + + +} diff --git a/src/org/fenixsoft/jvm/chapter4/BTraceTest.java b/src/org/fenixsoft/jvm/chapter4/BTraceTest.java new file mode 100644 index 0000000..c187bb5 --- /dev/null +++ b/src/org/fenixsoft/jvm/chapter4/BTraceTest.java @@ -0,0 +1,48 @@ +package org.fenixsoft.jvm.chapter4; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; + +/** + * @author zzm + */ +public class BTraceTest { + + public int add(int a, int b) { + return a + b; + } + + public static void main(String[] args) throws IOException { + BTraceTest test = new BTraceTest(); + BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); + for (int i = 0; i < 10; i++) { + reader.readLine(); + int a = (int) Math.round(Math.random() * 1000); + int b = (int) Math.round(Math.random() * 1000); + System.out.println(test.add(a, b)); + } + } +} + +/* BTrace Script Template +import com.sun.btrace.annotations.*; + import static com.sun.btrace.BTraceUtils.*; + +@BTrace +public class TracingScript { + @OnMethod( + clazz="org.fenixsoft.monitoring.BTraceTest", + method="add", + location=@Location(Kind.RETURN) + ) + + public static void func(@Self org.fenixsoft.monitoring.BTraceTest instance,int a,int b,@Return int result) { + println("调用堆栈:"); + jstack(); + println(strcat("方法参数A:",str(a))); + println(strcat("方法参数B:",str(b))); + println(strcat("方法结果:",str(result))); + } +} +*/ \ No newline at end of file diff --git a/src/org/fenixsoft/jvm/chapter4/JConsoleTestCase.java b/src/org/fenixsoft/jvm/chapter4/JConsoleTestCase.java new file mode 100644 index 0000000..49d4c57 --- /dev/null +++ b/src/org/fenixsoft/jvm/chapter4/JConsoleTestCase.java @@ -0,0 +1,29 @@ +package org.fenixsoft.jvm.chapter4; + +import java.util.ArrayList; +import java.util.List; + +public class JConsoleTestCase { + + /** + * 内存占位符对象,一个OOMObject大约占64K + */ + static class OOMObject { + public byte[] placeholder = new byte[64 * 1024]; + } + + public static void fillHeap(int num) throws InterruptedException { + List list = new ArrayList(); + for (int i = 0; i < num; i++) { + // 稍作延时,令监视曲线的变化更加明显 + Thread.sleep(50); + list.add(new OOMObject()); + } + System.gc(); + } + + public static void main(String[] args) throws Exception { + fillHeap(1000); + } + +} diff --git a/src/org/fenixsoft/jvm/chapter4/JHSDBTestCase.java b/src/org/fenixsoft/jvm/chapter4/JHSDBTestCase.java new file mode 100644 index 0000000..c9ec249 --- /dev/null +++ b/src/org/fenixsoft/jvm/chapter4/JHSDBTestCase.java @@ -0,0 +1,24 @@ +package org.fenixsoft.jvm.chapter4; + +/** + * staticObj、instanceObj、localObj存放在哪里? + */ +public class JHSDBTestCase { + + static class Test { + static ObjectHolder staticObj = new ObjectHolder(); + ObjectHolder instanceObj = new ObjectHolder(); + + void foo() { + ObjectHolder localObj = new ObjectHolder(); + System.out.println("done"); // 这里设一个断点 + } + } + + private static class ObjectHolder {} + + public static void main(String[] args) { + Test test = new JHSDBTestCase.Test(); + test.foo(); + } +} diff --git a/src/org/fenixsoft/jvm/chapter4/ThreadDeadLockTestCase_1.java b/src/org/fenixsoft/jvm/chapter4/ThreadDeadLockTestCase_1.java new file mode 100644 index 0000000..5162b2c --- /dev/null +++ b/src/org/fenixsoft/jvm/chapter4/ThreadDeadLockTestCase_1.java @@ -0,0 +1,52 @@ +package org.fenixsoft.jvm.chapter4; + +import java.io.BufferedReader; +import java.io.InputStreamReader; + +/** + * @author zzm + */ +public class ThreadDeadLockTestCase_1 { + /** + * 线程死循环演示 + */ + public static void createBusyThread() { + Thread thread = new Thread(new Runnable() { + @Override + public void run() { + while (true) // 第41行 + ; + } + }, "testBusyThread"); + thread.start(); + } + + /** + * 线程锁等待演示 + */ + public static void createLockThread(final Object lock) { + Thread thread = new Thread(new Runnable() { + @Override + public void run() { + synchronized (lock) { + try { + lock.wait(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + }, "testLockThread"); + thread.start(); + } + + public static void main(String[] args) throws Exception { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + br.readLine(); + createBusyThread(); + br.readLine(); + Object obj = new Object(); + createLockThread(obj); + } + +} diff --git a/src/org/fenixsoft/jvm/chapter4/ThreadDeadLockTestCase_2.java b/src/org/fenixsoft/jvm/chapter4/ThreadDeadLockTestCase_2.java new file mode 100644 index 0000000..efd5a11 --- /dev/null +++ b/src/org/fenixsoft/jvm/chapter4/ThreadDeadLockTestCase_2.java @@ -0,0 +1,36 @@ +package org.fenixsoft.jvm.chapter4; + +/** + * @author zzm + */ +public class ThreadDeadLockTestCase_2 { + + /** + * 线程死锁等待演示 + */ + static class SynAddRunnalbe implements Runnable { + int a, b; + public SynAddRunnalbe(int a, int b) { + this.a = a; + this.b = b; + } + + @Override + public void run() { + synchronized (Integer.valueOf(a)) { + synchronized (Integer.valueOf(b)) { + System.out.println(a + b); + } + } + } + } + + public static void main(String[] args) { + for (int i = 0; i < 100; i++) { + new Thread(new SynAddRunnalbe(1, 2)).start(); + new Thread(new SynAddRunnalbe(2, 1)).start(); + } + } + + +} diff --git a/src/org/fenixsoft/jvm/chapter5/EclipseStartTime_1.0.0.201011281102.jar b/src/org/fenixsoft/jvm/chapter5/EclipseStartTime_1.0.0.201011281102.jar new file mode 100644 index 0000000..828021a Binary files /dev/null and b/src/org/fenixsoft/jvm/chapter5/EclipseStartTime_1.0.0.201011281102.jar differ diff --git a/src/org/fenixsoft/jvm/chapter7/ClassLoaderTest.java b/src/org/fenixsoft/jvm/chapter7/ClassLoaderTest.java new file mode 100644 index 0000000..bbf5fa1 --- /dev/null +++ b/src/org/fenixsoft/jvm/chapter7/ClassLoaderTest.java @@ -0,0 +1,42 @@ +package org.fenixsoft.jvm.chapter7; + +/** + * @author zzm + */ + +import java.io.IOException; +import java.io.InputStream; + +/** + * 类加载器与instanceof关键字演示 + * + * @author zzm + */ +public class ClassLoaderTest { + + public static void main(String[] args) throws Exception { + + ClassLoader myLoader = new ClassLoader() { + @Override + public Class loadClass(String name) throws ClassNotFoundException { + try { + String fileName = name.substring(name.lastIndexOf(".") + 1) + ".class"; + InputStream is = getClass().getResourceAsStream(fileName); + if (is == null) { + return super.loadClass(name); + } + byte[] b = new byte[is.available()]; + is.read(b); + return defineClass(name, b, 0, b.length); + } catch (IOException e) { + throw new ClassNotFoundException(name); + } + } + }; + + Object obj = myLoader.loadClass("org.fenixsoft.jvm.chapter7.ClassLoaderTest").newInstance(); + + System.out.println(obj.getClass()); + System.out.println(obj instanceof org.fenixsoft.jvm.chapter7.ClassLoaderTest); + } +} diff --git a/src/org/fenixsoft/jvm/chapter7/ConstClass.java b/src/org/fenixsoft/jvm/chapter7/ConstClass.java new file mode 100644 index 0000000..44ab6bb --- /dev/null +++ b/src/org/fenixsoft/jvm/chapter7/ConstClass.java @@ -0,0 +1,14 @@ +package org.fenixsoft.jvm.chapter7; + +/** + * 被动使用类字段演示三: + * 常量在编译阶段会存入调用类的常量池中,本质上没有直接引用到定义常量的类,因此不会触发定义常量的类的初始化。 + **/ +public class ConstClass { + + static { + System.out.println("ConstClass init!"); + } + + public static final String HELLOWORLD = "hello world"; +} diff --git a/src/org/fenixsoft/jvm/chapter7/DeadLoopTestCase.java b/src/org/fenixsoft/jvm/chapter7/DeadLoopTestCase.java new file mode 100644 index 0000000..07caa71 --- /dev/null +++ b/src/org/fenixsoft/jvm/chapter7/DeadLoopTestCase.java @@ -0,0 +1,34 @@ +package org.fenixsoft.jvm.chapter7; + +/** + * @author zzm + */ +public class DeadLoopTestCase { + + static class DeadLoopClass { + static { + // 如果不加上这个if语句,编译器将提示“Initializer does not complete normally”并拒绝编译 + if (true) { + System.out.println(Thread.currentThread() + "init DeadLoopClass"); + while (true) { + } + } + } + } + + public static void main(String[] args) { + Runnable script = new Runnable() { + public void run() { + System.out.println(Thread.currentThread() + "start"); + DeadLoopClass dlc = new DeadLoopClass(); + System.out.println(Thread.currentThread() + " run over"); + } + }; + + Thread thread1 = new Thread(script); + Thread thread2 = new Thread(script); + thread1.start(); + thread2.start(); + } + +} diff --git a/src/org/fenixsoft/jvm/chapter7/FieldResolution.java b/src/org/fenixsoft/jvm/chapter7/FieldResolution.java new file mode 100644 index 0000000..049b42b --- /dev/null +++ b/src/org/fenixsoft/jvm/chapter7/FieldResolution.java @@ -0,0 +1,31 @@ +package org.fenixsoft.jvm.chapter7; + +/** + * @author zzm + */ +public class FieldResolution { + + interface Interface0 { + int A = 0; + } + + interface Interface1 extends Interface0 { + int A = 1; + } + + interface Interface2 { + int A = 2; + } + + static class Parent implements Interface1 { + public static int A = 3; + } + + static class Sub extends Parent implements Interface2 { + public static int A = 4; + } + + public static void main(String[] args) { + System.out.println(Sub.A); + } +} diff --git a/src/org/fenixsoft/jvm/chapter7/NotInitialization_1.java b/src/org/fenixsoft/jvm/chapter7/NotInitialization_1.java new file mode 100644 index 0000000..17addca --- /dev/null +++ b/src/org/fenixsoft/jvm/chapter7/NotInitialization_1.java @@ -0,0 +1,12 @@ +package org.fenixsoft.jvm.chapter7; + +/** + * 非主动使用类字段演示 + **/ +public class NotInitialization_1 { + + public static void main(String[] args) { + System.out.println(SubClass.value); + } + +} diff --git a/src/org/fenixsoft/jvm/chapter7/NotInitialization_2.java b/src/org/fenixsoft/jvm/chapter7/NotInitialization_2.java new file mode 100644 index 0000000..89903af --- /dev/null +++ b/src/org/fenixsoft/jvm/chapter7/NotInitialization_2.java @@ -0,0 +1,13 @@ +package org.fenixsoft.jvm.chapter7; + +/** + * 被动使用类字段演示二: + * 通过数组定义来引用类,不会触发此类的初始化 + **/ +public class NotInitialization_2 { + + public static void main(String[] args) { + SuperClass[] sca = new SuperClass[10]; + } + +} diff --git a/src/org/fenixsoft/jvm/chapter7/NotInitialization_3.java b/src/org/fenixsoft/jvm/chapter7/NotInitialization_3.java new file mode 100644 index 0000000..885f301 --- /dev/null +++ b/src/org/fenixsoft/jvm/chapter7/NotInitialization_3.java @@ -0,0 +1,11 @@ +package org.fenixsoft.jvm.chapter7; + +/** + * 非主动使用类字段演示 + **/ +public class NotInitialization_3 { + + public static void main(String[] args) { + System.out.println(ConstClass.HELLOWORLD); + } +} diff --git a/src/org/fenixsoft/jvm/chapter7/SubClass.java b/src/org/fenixsoft/jvm/chapter7/SubClass.java new file mode 100644 index 0000000..ba82030 --- /dev/null +++ b/src/org/fenixsoft/jvm/chapter7/SubClass.java @@ -0,0 +1,11 @@ +package org.fenixsoft.jvm.chapter7; + +/** + * @author zzm + */ +public class SubClass extends SuperClass { + + static { + System.out.println("SubClass init!"); + } +} diff --git a/src/org/fenixsoft/jvm/chapter7/SuperClass.java b/src/org/fenixsoft/jvm/chapter7/SuperClass.java new file mode 100644 index 0000000..6184cc3 --- /dev/null +++ b/src/org/fenixsoft/jvm/chapter7/SuperClass.java @@ -0,0 +1,14 @@ +package org.fenixsoft.jvm.chapter7; + +/** + * 被动使用类字段演示一: + * 通过子类引用父类的静态字段,不会导致子类初始化 + **/ +public class SuperClass { + + static { + System.out.println("SuperClass init!"); + } + + public static int value = 123; +} \ No newline at end of file diff --git a/src/org/fenixsoft/jvm/chapter8/Dispatch.java b/src/org/fenixsoft/jvm/chapter8/Dispatch.java new file mode 100644 index 0000000..242fd7e --- /dev/null +++ b/src/org/fenixsoft/jvm/chapter8/Dispatch.java @@ -0,0 +1,39 @@ +package org.fenixsoft.jvm.chapter8; + +/** + * 单分派、多分派演示 + * @author zzm + */ +public class Dispatch { + + static class QQ {} + + static class _360 {} + + public static class Father { + public void hardChoice(QQ arg) { + System.out.println("father choose qq"); + } + + public void hardChoice(_360 arg) { + System.out.println("father choose 360"); + } + } + + public static class Son extends Father { + public void hardChoice(QQ arg) { + System.out.println("son choose qq"); + } + + public void hardChoice(_360 arg) { + System.out.println("son choose 360"); + } + } + + public static void main(String[] args) { + Father father = new Father(); + Father son = new Son(); + father.hardChoice(new _360()); + son.hardChoice(new QQ()); + } +} diff --git a/src/org/fenixsoft/jvm/chapter8/DynamicDispatch.java b/src/org/fenixsoft/jvm/chapter8/DynamicDispatch.java new file mode 100644 index 0000000..d1ee86b --- /dev/null +++ b/src/org/fenixsoft/jvm/chapter8/DynamicDispatch.java @@ -0,0 +1,35 @@ +package org.fenixsoft.jvm.chapter8; + +/** + * 方法动态分派演示 + * @author zzm + */ +public class DynamicDispatch { + + static abstract class Human { + protected abstract void sayHello(); + } + + static class Man extends Human { + @Override + protected void sayHello() { + System.out.println("man say hello"); + } + } + + static class Woman extends Human { + @Override + protected void sayHello() { + System.out.println("woman say hello"); + } + } + + public static void main(String[] args) { + Human man = new Man(); + Human woman = new Woman(); + man.sayHello(); + woman.sayHello(); + man = new Woman(); + man.sayHello(); + } +} diff --git a/src/org/fenixsoft/jvm/chapter8/FieldHasNoPolymorphic.java b/src/org/fenixsoft/jvm/chapter8/FieldHasNoPolymorphic.java new file mode 100644 index 0000000..b3509aa --- /dev/null +++ b/src/org/fenixsoft/jvm/chapter8/FieldHasNoPolymorphic.java @@ -0,0 +1,39 @@ +package org.fenixsoft.jvm.chapter8; + +/** + * 字段不参与多态 + * @author zzm + */ +public class FieldHasNoPolymorphic { + + static class Father { + public int money = 1; + + public Father() { + money = 2; + showMeTheMoney(); + } + + public void showMeTheMoney() { + System.out.println("I am Father, i have $" + money); + } + } + + static class Son extends Father { + public int money = 3; + + public Son() { + money = 4; + showMeTheMoney(); + } + + public void showMeTheMoney() { + System.out.println("I am Son, i have $" + money); + } + } + + public static void main(String[] args) { + Father gay = new Son(); + System.out.println("This gay has $" + gay.money); + } +} diff --git a/src/org/fenixsoft/jvm/chapter8/GrandFatherTestCase_1.java b/src/org/fenixsoft/jvm/chapter8/GrandFatherTestCase_1.java new file mode 100644 index 0000000..ee9673d --- /dev/null +++ b/src/org/fenixsoft/jvm/chapter8/GrandFatherTestCase_1.java @@ -0,0 +1,41 @@ +package org.fenixsoft.jvm.chapter8; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodType; + +import static java.lang.invoke.MethodHandles.lookup; + +/** + * @author zzm + */ +public class GrandFatherTestCase_1 { + + class GrandFather { + void thinking() { + System.out.println("i am grandfather"); + } + } + + class Father extends GrandFather { + void thinking() { + System.out.println("i am father"); + } + } + + class Son extends Father { + void thinking() { + try { + MethodType mt = MethodType.methodType(void.class); + MethodHandle mh = lookup().findSpecial(GrandFather.class, + "thinking", mt, getClass()); + mh.invoke(this); + } catch (Throwable e) { + } + } + } + + public static void main(String[] args) { + (new GrandFatherTestCase_1().new Son()).thinking(); + } + +} diff --git a/src/org/fenixsoft/jvm/chapter8/GrandFatherTestCase_2.java b/src/org/fenixsoft/jvm/chapter8/GrandFatherTestCase_2.java new file mode 100644 index 0000000..0178cf7 --- /dev/null +++ b/src/org/fenixsoft/jvm/chapter8/GrandFatherTestCase_2.java @@ -0,0 +1,43 @@ +package org.fenixsoft.jvm.chapter8; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.lang.reflect.Field; + +/** + * @author zzm + */ +public class GrandFatherTestCase_2 { + + class GrandFather { + void thinking() { + System.out.println("i am grandfather"); + } + } + + class Father extends GrandFatherTestCase_2.GrandFather { + void thinking() { + System.out.println("i am father"); + } + } + + class Son extends GrandFatherTestCase_2.Father { + void thinking() { + try { + MethodType mt = MethodType.methodType(void.class); + Field lookupImpl = MethodHandles.Lookup.class.getDeclaredField("IMPL_LOOKUP"); + lookupImpl.setAccessible(true); + MethodHandle mh = ((MethodHandles.Lookup) lookupImpl.get(null)).findSpecial(GrandFather.class,"thinking", mt, GrandFather.class); + mh.invoke(this); + } catch (Throwable e) { + } + } + + } + + public static void main(String[] args) { + (new GrandFatherTestCase_2().new Son()).thinking(); + } + +} diff --git a/src/org/fenixsoft/jvm/chapter8/InvokeDynamicTest.java b/src/org/fenixsoft/jvm/chapter8/InvokeDynamicTest.java new file mode 100644 index 0000000..c018633 --- /dev/null +++ b/src/org/fenixsoft/jvm/chapter8/InvokeDynamicTest.java @@ -0,0 +1,37 @@ +package org.fenixsoft.jvm.chapter8; + +import java.lang.invoke.*; + +import static java.lang.invoke.MethodHandles.lookup; + +public class InvokeDynamicTest { + + public static void main(String[] args) throws Throwable { + INDY_BootstrapMethod().invokeExact("icyfenix"); + } + + public static void testMethod(String s) { + System.out.println("hello String:" + s); + } + + public static CallSite BootstrapMethod(MethodHandles.Lookup lookup, String name, MethodType mt) throws Throwable { + return new ConstantCallSite(lookup.findStatic(InvokeDynamicTest.class, name, mt)); + } + + private static MethodType MT_BootstrapMethod() { + return MethodType + .fromMethodDescriptorString( + "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;", + null); + } + + private static MethodHandle MH_BootstrapMethod() throws Throwable { + return lookup().findStatic(InvokeDynamicTest.class, "BootstrapMethod", MT_BootstrapMethod()); + } + + private static MethodHandle INDY_BootstrapMethod() throws Throwable { + CallSite cs = (CallSite) MH_BootstrapMethod().invokeWithArguments(lookup(), "testMethod", + MethodType.fromMethodDescriptorString("(Ljava/lang/String;)V", null)); + return cs.dynamicInvoker(); + } +} diff --git a/src/org/fenixsoft/jvm/chapter8/MethodHandleTest.java b/src/org/fenixsoft/jvm/chapter8/MethodHandleTest.java new file mode 100644 index 0000000..2e5656f --- /dev/null +++ b/src/org/fenixsoft/jvm/chapter8/MethodHandleTest.java @@ -0,0 +1,33 @@ +package org.fenixsoft.jvm.chapter8; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodType; + +import static java.lang.invoke.MethodHandles.lookup; + +/** + * JSR 292 MethodHandle基础用法演示 + * @author zzm + */ +public class MethodHandleTest { + + static class ClassA { + public void println(String s) { + System.out.println(s); + } + } + + public static void main(String[] args) throws Throwable { + Object obj = System.currentTimeMillis() % 2 == 0 ? System.out : new ClassA(); + // 无论obj最终是哪个实现类,下面这句都能正确调用到println方法。 + getPrintlnMH(obj).invokeExact("icyfenix"); + } + + private static MethodHandle getPrintlnMH(Object reveiver) throws Throwable { + // MethodType:代表“方法类型”,包含了方法的返回值(methodType()的第一个参数)和具体参数(methodType()第二个及以后的参数)。 + MethodType mt = MethodType.methodType(void.class, String.class); + // lookup()方法来自于MethodHandles.lookup,这句的作用是在指定类中查找符合给定的方法名称、方法类型,并且符合调用权限的方法句柄。 + // 因为这里调用的是一个虚方法,按照Java语言的规则,方法第一个参数是隐式的,代表该方法的接收者,也即是this指向的对象,这个参数以前是放在参数列表中进行传递,现在提供了bindTo()方法来完成这件事情。 + return lookup().findVirtual(reveiver.getClass(), "println", mt).bindTo(reveiver); + } +} diff --git a/src/org/fenixsoft/jvm/chapter8/Overload.java b/src/org/fenixsoft/jvm/chapter8/Overload.java new file mode 100644 index 0000000..63b4328 --- /dev/null +++ b/src/org/fenixsoft/jvm/chapter8/Overload.java @@ -0,0 +1,41 @@ +package org.fenixsoft.jvm.chapter8; + +import java.io.Serializable; + +/** + * @author zzm + */ +public class Overload { + + public static void sayHello(Object arg) { + System.out.println("hello Object"); + } + + public static void sayHello(int arg) { + System.out.println("hello int"); + } + + public static void sayHello(long arg) { + System.out.println("hello long"); + } + + public static void sayHello(Character arg) { + System.out.println("hello Character"); + } + + public static void sayHello(char arg) { + System.out.println("hello char"); + } + + public static void sayHello(char... arg) { + System.out.println("hello char ..."); + } + + public static void sayHello(Serializable arg) { + System.out.println("hello Serializable"); + } + + public static void main(String[] args) { + sayHello('a'); + } +} diff --git a/src/org/fenixsoft/jvm/chapter8/StaticDispatch.java b/src/org/fenixsoft/jvm/chapter8/StaticDispatch.java new file mode 100644 index 0000000..acb1d49 --- /dev/null +++ b/src/org/fenixsoft/jvm/chapter8/StaticDispatch.java @@ -0,0 +1,37 @@ +package org.fenixsoft.jvm.chapter8; + +/** + * 方法静态分派演示 + * @author zzm + */ +public class StaticDispatch { + + static abstract class Human { + } + + static class Man extends Human { + } + + static class Woman extends Human { + } + + public void sayHello(Human guy) { + System.out.println("hello,guy!"); + } + + public void sayHello(Man guy) { + System.out.println("hello,gentleman!"); + } + + public void sayHello(Woman guy) { + System.out.println("hello,lady!"); + } + + public static void main(String[] args) { + Human man = new Man(); + Human woman = new Woman(); + StaticDispatch sr = new StaticDispatch(); + sr.sayHello(man); + sr.sayHello(woman); + } +} diff --git a/src/org/fenixsoft/jvm/chapter8/StaticResolution.java b/src/org/fenixsoft/jvm/chapter8/StaticResolution.java new file mode 100644 index 0000000..74b63a9 --- /dev/null +++ b/src/org/fenixsoft/jvm/chapter8/StaticResolution.java @@ -0,0 +1,19 @@ +package org.fenixsoft.jvm.chapter8; + +/** + * 方法静态解析演示 + * + * @author zzm + */ +public class StaticResolution { + + public static void sayHello() { + System.out.println("hello world"); + } + + public static void main(String[] args) { + StaticResolution.sayHello(); + } + +} + diff --git a/src/org/fenixsoft/jvm/chapter9/ByteUtils.java b/src/org/fenixsoft/jvm/chapter9/ByteUtils.java new file mode 100644 index 0000000..0aa80ad --- /dev/null +++ b/src/org/fenixsoft/jvm/chapter9/ByteUtils.java @@ -0,0 +1,44 @@ +package org.fenixsoft.jvm.chapter9; + +/** + * Bytes数组处理工具 + * + * @author + */ +public class ByteUtils { + + public static int bytes2Int(byte[] b, int start, int len) { + int sum = 0; + int end = start + len; + for (int i = start; i < end; i++) { + int n = ((int) b[i]) & 0xff; + n <<= (--len) * 8; + sum = n + sum; + } + return sum; + } + + public static byte[] int2Bytes(int value, int len) { + byte[] b = new byte[len]; + for (int i = 0; i < len; i++) { + b[len - i - 1] = (byte) ((value >> 8 * i) & 0xff); + } + return b; + } + + public static String bytes2String(byte[] b, int start, int len) { + return new String(b, start, len); + } + + public static byte[] string2Bytes(String str) { + return str.getBytes(); + } + + public static byte[] bytesReplace(byte[] originalBytes, int offset, int len, byte[] replaceBytes) { + byte[] newBytes = new byte[originalBytes.length + (replaceBytes.length - len)]; + System.arraycopy(originalBytes, 0, newBytes, 0, offset); + System.arraycopy(replaceBytes, 0, newBytes, offset, replaceBytes.length); + System.arraycopy(originalBytes, offset + len, newBytes, offset + replaceBytes.length, originalBytes.length - offset - len); + return newBytes; + } +} diff --git a/src/org/fenixsoft/jvm/chapter9/ClassModifier.java b/src/org/fenixsoft/jvm/chapter9/ClassModifier.java new file mode 100644 index 0000000..95e951e --- /dev/null +++ b/src/org/fenixsoft/jvm/chapter9/ClassModifier.java @@ -0,0 +1,74 @@ +package org.fenixsoft.jvm.chapter9; + +/** + * 修改Class文件,暂时只提供修改常量池常量的功能 + * + * @author zzm + */ +public class ClassModifier { + + /** + * Class文件中常量池的起始偏移 + */ + private static final int CONSTANT_POOL_COUNT_INDEX = 8; + + /** + * CONSTANT_Utf8_info常量的tag标志 + */ + private static final int CONSTANT_Utf8_info = 1; + + /** + * 常量池中11种常量所占的长度,CONSTANT_Utf8_info型常量除外,因为它不是定长的 + */ + private static final int[] CONSTANT_ITEM_LENGTH = {-1, -1, -1, 5, 5, 9, 9, 3, 3, 5, 5, 5, 5}; + + private static final int u1 = 1; + private static final int u2 = 2; + + private byte[] classByte; + + public ClassModifier(byte[] classByte) { + this.classByte = classByte; + } + + /** + * 修改常量池中CONSTANT_Utf8_info常量的内容 + * + * @param oldStr 修改前的字符串 + * @param newStr 修改后的字符串 + * @return 修改结果 + */ + public byte[] modifyUTF8Constant(String oldStr, String newStr) { + int cpc = getConstantPoolCount(); + int offset = CONSTANT_POOL_COUNT_INDEX + u2; + for (int i = 0; i < cpc; i++) { + int tag = ByteUtils.bytes2Int(classByte, offset, u1); + if (tag == CONSTANT_Utf8_info) { + int len = ByteUtils.bytes2Int(classByte, offset + u1, u2); + offset += (u1 + u2); + String str = ByteUtils.bytes2String(classByte, offset, len); + if (str.equalsIgnoreCase(oldStr)) { + byte[] strBytes = ByteUtils.string2Bytes(newStr); + byte[] strLen = ByteUtils.int2Bytes(newStr.length(), u2); + classByte = ByteUtils.bytesReplace(classByte, offset - u2, u2, strLen); + classByte = ByteUtils.bytesReplace(classByte, offset, len, strBytes); + return classByte; + } else { + offset += len; + } + } else { + offset += CONSTANT_ITEM_LENGTH[tag]; + } + } + return classByte; + } + + /** + * 获取常量池中常量的数量 + * + * @return 常量池数量 + */ + public int getConstantPoolCount() { + return ByteUtils.bytes2Int(classByte, CONSTANT_POOL_COUNT_INDEX, u2); + } +} diff --git a/src/org/fenixsoft/jvm/chapter9/DynamicProxyTest.java b/src/org/fenixsoft/jvm/chapter9/DynamicProxyTest.java new file mode 100644 index 0000000..7d0573b --- /dev/null +++ b/src/org/fenixsoft/jvm/chapter9/DynamicProxyTest.java @@ -0,0 +1,43 @@ +package org.fenixsoft.jvm.chapter9; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; + +/** + * @author zzm + */ +public class DynamicProxyTest { + + interface IHello { + void sayHello(); + } + + static class Hello implements IHello { + @Override + public void sayHello() { + System.out.println("hello world"); + } + } + + static class DynamicProxy implements InvocationHandler { + + Object originalObj; + + Object bind(Object originalObj) { + this.originalObj = originalObj; + return Proxy.newProxyInstance(originalObj.getClass().getClassLoader(), originalObj.getClass().getInterfaces(), this); + } + + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + System.out.println("welcome"); + return method.invoke(originalObj, args); + } + } + + public static void main(String[] args) { + IHello hello = (IHello) new DynamicProxy().bind(new Hello()); + hello.sayHello(); + } +} diff --git a/src/org/fenixsoft/jvm/chapter9/HackSystem.java b/src/org/fenixsoft/jvm/chapter9/HackSystem.java new file mode 100644 index 0000000..1588dcf --- /dev/null +++ b/src/org/fenixsoft/jvm/chapter9/HackSystem.java @@ -0,0 +1,54 @@ +package org.fenixsoft.jvm.chapter9; + +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.PrintStream; + +/** + * 为Javaclass劫持java.lang.System提供支持 + * 除了out和err外,其余的都直接转发给System处理 + * + * @author zzm + */ +public class HackSystem { + + public final static InputStream in = System.in; + + private static ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + + public final static PrintStream out = new PrintStream(buffer); + + public final static PrintStream err = out; + + public static String getBufferString() { + return buffer.toString(); + } + + public static void clearBuffer() { + buffer.reset(); + } + + public static void setSecurityManager(final SecurityManager s) { + System.setSecurityManager(s); + } + + public static SecurityManager getSecurityManager() { + return System.getSecurityManager(); + } + + public static long currentTimeMillis() { + return System.currentTimeMillis(); + } + + public static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length) { + System.arraycopy(src, srcPos, dest, destPos, length); + } + + public static int identityHashCode(Object x) { + return System.identityHashCode(x); + } + + // 下面所有的方法都与java.lang.System的名称一样 + // 实现都是字节转调System的对应方法 + // 因版面原因,省略了其他方法 +} diff --git a/src/org/fenixsoft/jvm/chapter9/HotSwapClassLoader.java b/src/org/fenixsoft/jvm/chapter9/HotSwapClassLoader.java new file mode 100644 index 0000000..0c8dd6b --- /dev/null +++ b/src/org/fenixsoft/jvm/chapter9/HotSwapClassLoader.java @@ -0,0 +1,20 @@ +package org.fenixsoft.jvm.chapter9; + +/** + * 为了多次载入执行类而加入的加载器 + * 把defineClass方法开放出来,只有外部显式调用的时候才会使用到loadByte方法 + * 由虚拟机调用时,仍然按照原有的双亲委派规则使用loadClass方法进行类加载 + * + * @author zzm + */ +public class HotSwapClassLoader extends ClassLoader { + + public HotSwapClassLoader() { + super(HotSwapClassLoader.class.getClassLoader()); + } + + public Class loadByte(byte[] classByte) { + return defineClass(null, classByte, 0, classByte.length); + } + +} diff --git a/src/org/fenixsoft/jvm/chapter9/JavaclassExecuter.java b/src/org/fenixsoft/jvm/chapter9/JavaclassExecuter.java new file mode 100644 index 0000000..e7aef13 --- /dev/null +++ b/src/org/fenixsoft/jvm/chapter9/JavaclassExecuter.java @@ -0,0 +1,34 @@ +package org.fenixsoft.jvm.chapter9; + +import java.lang.reflect.Method; + +/** + * Javaclass执行工具 + * + * @author zzm + */ +public class JavaclassExecuter { + + /** + * 执行外部传过来的代表一个Java类的Byte数组
+ * 将输入类的byte数组中代表java.lang.System的CONSTANT_Utf8_info常量修改为劫持后的HackSystem类 + * 执行方法为该类的static main(String[] args)方法,输出结果为该类向System.out/err输出的信息 + * + * @param classByte 代表一个Java类的Byte数组 + * @return 执行结果 + */ + public static String execute(byte[] classByte) { + HackSystem.clearBuffer(); + ClassModifier cm = new ClassModifier(classByte); + byte[] modiBytes = cm.modifyUTF8Constant("java/lang/System", "org/fenixsoft/classloading/execute/HackSystem"); + HotSwapClassLoader loader = new HotSwapClassLoader(); + Class clazz = loader.loadByte(modiBytes); + try { + Method method = clazz.getMethod("main", new Class[]{String[].class}); + method.invoke(null, new String[]{null}); + } catch (Throwable e) { + e.printStackTrace(HackSystem.out); + } + return HackSystem.getBufferString(); + } +}