1.获取类的字节码的三种方式:
1 public static void main(String[] args) 2 throws Exception{ 3 //1.获取类的字节码对象方式1 4 Class<Date> d1=Date.class; 5 //2.获取类的字节码对象方式2 6 Class<?> d2=Class.forName("java.util.Date"); 7 //3.获取类的字节码对象方式3 8 Class<?> d3=new Date().getClass(); 9 System.out.println(d1==d2); 10 System.out.println(d2==d3); 11 }
2.类何时被加载
1 /** 2 * 类何时被加载? 3 * 1)隐式加载 4 * a)构建类的实例对象 5 * b)访问类的静态成员(属性,方法) 6 * 2)显式加载 7 * a)Class.forName(....) 8 * b)loader.load(....) 9 */
/**
* 何时会触发类加载?(隐式加载)
* 1)访问类的静态属性?(分情况)
* 1.1)访问使用static final修饰的八种基本类型以及字符串类型时不会触发类加载
* 1.2)访问只有static修饰的任意属性时都会触发类加载.
* 2)访问类的静态方法.
* 3)构建对象时
*/
1 class ClassB{ 2 static int n=20;//外界访问时会触发类加载 3 static final int a=100;//外界访问时不会触发类加载 4 static final Integer b=200;//外界访问时会触发类加载 5 static final String LOCK="LOCK";//外界访问时不会触发类加载 6 static int[] array=new int[1024];//1024*4//外界访问时会触发类加载 7 static void doMethod01() {//外界访问时会触发类加载 8 System.out.println("ClassB.doMethod01"); 9 } 10 }
3类加载器:
BootstrapClassLoader(启动类加载器<根类加载器>,负责jre\lib\rt.jar)
c++编写,加载java核心库 java.*,构造ExtClassLoader和AppClassLoader。由于引导类加载器涉及到虚拟机本地实现细节,开发者无法直接获取到启动类加载器的引用,所以不允许直接通过引用进行操作
ExtClassLoader (标准扩展类加载器<扩展类加载器>,负责jre\lib\ext\*.jar)
java编写,加载扩展库,如classpath中的jre ,javax.*或者
java.ext.dir 指定位置中的类,开发者可以直接使用标准扩展类加载器。
AppClassLoader(系统类加载器<应用类加载器>:,负责加载我们自己写的类)
java编写,加载程序所在的目录,如user.dir所在的位置的class
CustomClassLoader(用户自定义类加载器)
java编写,用户自定义的类加载器,可加载指定路径的class文件
源码分析:
1 protected Class<?> loadClass(String name, boolean resolve) 2 throws ClassNotFoundException 3 { 4 synchronized (getClassLoadingLock(name)) { 5 // 首先检查这个classsh是否已经加载过了 6 Class<?> c = findLoadedClass(name); 7 if (c == null) { 8 long t0 = System.nanoTime(); 9 try { 10 // c==null表示没有加载,如果有父类的加载器则让父类加载器加载 11 if (parent != null) { 12 c = parent.loadClass(name, false); 13 } else { 14 //如果父类的加载器为空 则说明递归到bootStrapClassloader了 15 //bootStrapClassloader比较特殊无法通过get获取 16 c = findBootstrapClassOrNull(name); 17 } 18 } catch (ClassNotFoundException e) {} 19 if (c == null) { 20 //如果bootstrapClassLoader 仍然没有加载过,则递归回来,尝试自己去加载class 21 long t1 = System.nanoTime(); 22 c = findClass(name); 23 sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0); 24 sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1); 25 sun.misc.PerfCounter.getFindClasses().increment(); 26 } 27 } 28 if (resolve) { 29 resolveClass(c); 30 } 31 return c; 32 } 33 }
获取类加载器:
1 public static void main(String[] args) { 2 //获取AppClassLoader(系统类加载器) 3 ClassLoader loader01= 4 ClassLoader.getSystemClassLoader(); 5 System.out.println(loader01); 6 //获取 ExtClassLoader(标准扩展类加载器) 7 ClassLoader loader02=loader01.getParent(); 8 System.out.println(loader02); 9 //根类加载器(启动类加载器) BootstrapClassLoader (底层使用c完成) 10 ClassLoader loader03=loader02.getParent(); 11 System.out.println(loader03); 12 //除了这三个还有:CustomClassLoader(用户自定义类加载器) 13 //java编写,用户自定义的类加载器,可加载指定路径的class文件 14 //查一查类的双亲委派模型? 15 }
双亲委派机制的流程图


从上图中我们就更容易理解了,当一个Hello.class这样的文件要被加载时。不考虑我们自定义类加载器,首先会在AppClassLoader中检查是否加载过,如果有那就无需再加载了。如果没有,那么会拿到父加载器,然后调用父加载器的loadClass方法。父类中同理会先检查自己是否已经加载过,如果没有再往上。注意这个过程,知道到达Bootstrap classLoader之前,都是没有哪个加载器自己选择加载的。如果父加载器无法加载,会下沉到子加载器去加载,一直到最底层,如果没有任何加载器能加载,就会抛出ClassNotFoundException。
双亲委派机制的作用
1、防止重复加载同一个.class。通过委托去向上面问一问,加载过了,就不用再加载一遍。保证数据安全。
2、保证核心.class不能被篡改。通过委托方式,不会去篡改核心.clas,即使篡改也不会去加载,即使加载也不会是同一个.class对象了。不同的加载器加载同一个.class也不是同一个Class对象。这样保证了Class执行安全。
加载类时静态代码块一定会执行吗?
1 //类加载时一定会执行静态代码块吗?不一定 2 class ClassC{ 3 static int c=100; 4 static { 5 System.out.println("ClassC.static"); 6 } 7 } 8 class ClassD extends ClassC{ 9 static int d=200; 10 static { 11 System.out.println("ClassD.static"); 12 } 13 } 14 public class TestClassObject04 { 15 public static void main(String[] args)throws Exception { 16 ClassLoader loader=ClassLoader.getSystemClassLoader(); 17 //loader.loadClass("com.java.oop.ClassC");//不执行ClassC的静态代码块 18 //Class.forName("com.java.oop.ClassC");//执行ClassC的静态代码块 19 20 //Class.forName("com.java.oop.ClassC", 21 // true,//表示是否要对类变量初始化,是否执行静态代码块 22 // loader); 23 24 //如下输出,ClassD为被动加载,其内部静态代码块不会执行 25 //System.out.println(ClassD.c);//方法ClassC属性 26 27 //如下输出,ClassD为主动加载,其内部静态代码块会执行. 28 System.out.println(ClassD.d);//访问自身属性 29 } 30 31 }
静态代码块的执行顺序问题:
1 package com.java.oop; 2 import java.util.HashMap; 3 import java.util.Map; 4 class ClassE{ 5 //static Map<String,Object> pool=new HashMap<>(); 6 static ClassE instance=new ClassE(); 7 static Map<String,Object> pool=new HashMap<>(); 8 //如果先创建ClassE对象然后创建pool集合就会出现异常:java.lang.ExceptionInInitializerError 9 //因为在创建对象时会调用构造方法,构造中要用集合,但是此时还没执行到创建集合的代码 10 public ClassE() { 11 pool.put("A", "100"); 12 pool.put("B", "200"); 13 } 14 } 15 public class TestClassObject05 { 16 public static void main(String[] args) { 17 System.out.println(ClassE.pool); 18 } 19 }
4.用反射方式创建对象:
1 //构建一对象工厂对象,此工厂可以基于类的字节码对象构建类的实例对象. 2 class ObjectFactory {// 对象工厂 3 public static Object newInstance(Class<?> cls) throws Exception { 4 // 基于字节码对象创建类的实例对象 5 // 字节码对象是构建类的实例对象的模版对象 6 return cls.newInstance(); 7 } 8 9 // <bean id="" class="pkg.ClassName"> 10 public static Object newInstance(String className) throws Exception { 11 // 基于字节码对象创建类的实例对象 12 // 字节码对象是构建类的实例对象的模版对象 13 Class<?> cls = Class.forName(className); 14 return cls.newInstance(); 15 } 16 } 17 18 public class TestClassObject06 { 19 public static void main(String[] args) throws Exception { 20 Date date = (Date) ObjectFactory.newInstance(Date.class); 21 date = (Date) ObjectFactory.newInstance(Date.class); 22 System.out.println(date); 23 date = (Date) ObjectFactory.newInstance("java.util.Date"); 24 System.out.println(date); 25 26 } 27 }
类的延迟加载机制:
1 class ClassAA{//如果没有用到成员内部类(只使用外部类),就不加载成员内部类 2 static Integer count1=100; 3 static class InnerAA{ 4 static Integer[] count2=new Integer[200];//延迟加载 5 static { 6 System.out.println("InnerAA"); 7 } 8 } 9 } 10 public class TestClassObject07 { 11 public static void main(String[] args) { 12 System.out.println(ClassAA.count1);//此时不执行成员内部类的静态代码库(不加载成员内部类) 13 //System.out.println(Arrays.toString(ClassAA.InnerAA.count2)); 14 } 15 }
主动加载和被动加载类:
1 /** 2 * 案例:考察类的被动加载? 调用继承过来的属性和方法时子类属于被动加载,子类不执行静态代码块 3 */ 4 class ClassE{ 5 static int a=100; 6 static { 7 System.out.println("ClassE.static{}"); 8 } 9 } 10 class ClassF extends ClassE{ 11 static int b=200; 12 static { 13 System.out.println("ClassF.static{}"); 14 } 15 } 16 //-XX:+TraceClassLoading 17 public class TestClassObject08 { 18 public static void main(String[] args) { 19 //ClassE 为主动加载 20 //ClassF 为被动加载(不执行static初始化操作) 21 System.out.println(ClassF.a); 22 } 23 }
5.用CGlib为某类生成代理对象:
5.1、被代理类:
1 import java.lang.reflect.Method; 2 import net.sf.cglib.proxy.Enhancer; 3 import net.sf.cglib.proxy.MethodInterceptor; 4 import net.sf.cglib.proxy.MethodProxy; 5 class ConcreteClassNoInterface { 6 public String getConcreteMethodA(String str){ 7 System.out.println("ConcreteMethod A ... "+str); 8 return str; 9 } 10 }
5.2、拦截器:
1 class ConcreateClassInterceptor implements MethodInterceptor{ 2 //调用子类对象(代理对象)方法时自动执行 3 public Object intercept( 4 Object obj, //增强对象(目标对象对应的子类对象) 5 Method method,//方法对象(要执行的方法就是) 6 Object[] args,//目标方法执行时需要的参数 7 MethodProxy proxy) throws Throwable { 8 long start=System.currentTimeMillis(); 9 System.out.println("before: "+method); 10 //执行目标对象方法///调用代理类实例上的proxy方法的父类方法 11 //(就是ConcreteClassNoInterface类getConcreteMethodA) 12 Object result=proxy.invokeSuper(obj, args); 13 System.out.println("after: "+method); 14 long end=System.currentTimeMillis(); 15 System.out.println("execute time :"+(end-start)); 16 return result; 17 } 18 }
5.3、生成动态代理类:
1 public class TestClassObject09 { 2 public static void main(String[] args) { 3 //1.如何用cglib创建Object的子类? 4 // 1)任意定义类 5 // 2)借助字节码增强技术创建Object类的子类对象 6 //2.借助CGLIB库中的API为Object类创建子类对象 7 // 构建Enhancer增强对象(通过此对象为目标类创建子类对象) 8 Enhancer en=new Enhancer(); 9 //设置父类对象类型 10 en.setSuperclass(ConcreteClassNoInterface.class); 11 //设置不使用缓存(不使用缓存中的字节码) 12 en.setUseCache(false);//默认为true 13 //设置回调对象(在执行代理对象的方法时会自动调用ConcreateClassInterceptor中的intercept方法) 14 en.setCallback(new ConcreateClassInterceptor()); 15 //为Object类创建子类对象 16 ConcreteClassNoInterface obj=(ConcreteClassNoInterface)en.create(); 17 obj.getConcreteMethodA("xx"); 18 //3.调用Object类的方法时输出方法的执行时长. 19 } 20 }
6.未逃逸对象分配在栈中:
//JDK6以后的HotSpot虚拟机已经支持运行时
//对对象进行逃逸分析.
//如何理解对象逃逸?
//方法内创建的对象没有被外界引用称之为未逃逸对象.
//现阶段的JDK中未逃逸的小对象可能会分配在栈上
* 总结:
* 1.对象创建时有可能分配在堆上也有可能分配在栈上.
* 2.方法内部创建的小对象并且没有逃逸可能分配在栈上.
* 3.默认打开逃逸分析对JVM的执行会有性能上的提高(
* 因为栈上分配的对象,不需要启动GC进行回收)
* 4.设计对象时,假如对象不会被多线程共享,多个方法共享,
* 那么,此时对象的引用应尽量使用局部变量.
* 1)堆
* 2)栈(未逃逸的小对象)
* 2.如何检测对象分配在哪里了?JVM 参数配置
* -XX:+PrintGC 输出GC基本信息
* 3.JDK8中逃逸分析默认开启了吗?开启了
* -XX:+DoEscapeAnalysis
* 4.JDK中最大堆,最小堆配置?
* 1)最大堆-Xmx
* 2)最小堆-Xms
* 5.JVM参数测试:(在堆内存有限的情况下测试JVM逃逸分析)
* 1)-Xmx5m -Xms5m -XX:-DoEscapeAnalysis -XX:+PrintGC
* 2)-Xmx5m -Xms5m -XX:+DoEscapeAnalysis -XX:+PrintGC
* 6.如何查看当前JDK中逃逸分析默认状态
* 1)-XX:+PrintFlagsInitial
7.垃圾回收器GC的执行:
A.重写finalize方法(在ClassA的对象被回收时会自动执行finalize方法)
1 class ClassA { 2 /** 对象在回收之前会执行此方法. */ 3 protected void finalize() throws Throwable { 4 System.out.println("finalize()"); 5 } 6 }
B.测试类:
将对象的引用置为null则此对象会在很短的时间被回收(看起来像是手动调用垃圾回收器,实际上不是手动调用)
堆内存如果不够用,JVM就检测所有对象是否可达(JVM是否还可以直接访问到此对象)
1 public class X { 2 public static void main(String[] args) { 3 // new ClassA(); 4 ClassA a = new ClassA(); 5 // 1.手动启动GC(告诉JVM有时间回收一个内存) 6 // 系统底层会对对象进行可达性分析(JVM是否还可以 7 // 直接访问到此对象) 8 a = null; 9 System.gc();//催促GC 10 // 2.自动GC(一般是在内存不足时) 11 byte[] array01 = new byte[30*1024 * 1024];// 1M 12 byte[] array02 = new byte[30*1024 * 1024]; 13 byte[] array03 = new byte[30*1024 * 1024]; 14 byte[] array04 = new byte[30*1024 * 1024]; 15 } 16 }
8.强引用、弱引用、软引用、虚引用:
8.1.被引用的
1 /** 2 * 案例分析:对象引用(类似一个指针,指向具体的某个对象) Java中对象的引用类型 3 * 1)强引用(此引用的引用对象不会被回收) 4 * 2)弱引用(此引用引用的对象,只要出现GC就会被回收,) 5 * 3)软引用(此引用引用的对象在内存不足时会被回收,触发GC时不一定会回收) 6 * 4)虚引用(此引用没什么用,一般可以记录被回收的对象而已) 7 */ 8 class ClassB { ClassB(String name){this.name=name;} 9 String name; 10 public void doMethod() { 11 System.out.println("doMethod()"+name); 12 } 13 14 protected void finalize() throws Throwable { 15 System.out.println("finalize()---"+name); 16 } 17 }
8.2.测试类:
1 // -Xmx5m -Xms5m -XX:+PrintGCDetails 2 public class X { 3 public static void main(String[] args) { 4 // 1.强引用 5 ClassB c1 = new ClassB("c1"); 6 // 2.弱引用 7 WeakReference<ClassB> c2 = new WeakReference<ClassB>(new ClassB("c2")); 8 // 3.软引用 9 SoftReference<ClassB> c3 = new SoftReference<ClassB>(new ClassB("c3")); 10 // 4.虚引用(PhantomReference-记录被回收的对象) 11 ReferenceQueue<ClassB> rq = new ReferenceQueue<ClassB>(); 12 PhantomReference<ClassB> c4 = new PhantomReference<ClassB>(new ClassB("c4"), rq); 13 // 演示对象不足时的自动GC 14 List<byte[]> list = new ArrayList<>(); 15 for (int i = 0; i < 1000; i++) { 16 list.add(new byte[20*900*1024]); 17 // 通过对象get方法可以获取引用的对象 18 //if (c2.get() != null)c2.get().doMethod(); 19 //System.out.println(c2.get().getClass().getName()); 20 if (c3.get() != null)c3.get().doMethod(); 21 if (c4.get() != null)c4.get().doMethod(); 22 try { 23 Thread.sleep(300); 24 } catch (Exception e) { 25 26 } 27 } 28 } 29 }
注:刚开始弱引用c2和虚引用c4就被回收了. 最后当内存不够用时软引用c3被回收
JVM是否还可以// 直接访问到此对象)
浙公网安备 33010602011771号