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 }
View Code

 

3类加载器:

BootstrapClassLoader(启动类加载器<根类加载器>,负责jre\lib\rt.jar)

c++编写,加载java核心库 java.*,构造ExtClassLoaderAppClassLoader。由于引导类加载器涉及到虚拟机本地实现细节,开发者无法直接获取到启动类加载器的引用,所以不允许直接通过引用进行操作

ExtClassLoader (标准扩展类加载器<扩展类加载器>,负责jre\lib\ext\*.jar)

java编写,加载扩展库,如classpath中的jrejavax.*或者
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 }
View Code

 

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 }
View Code

 

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 }
View Code

 

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 }
View Code

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 }
View Code

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 }
View Code

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 }
View Code

注:刚开始弱引用c2和虚引用c4就被回收了.  最后当内存不够用时软引用c3被回收

 

JVM是否还可以// 直接访问到此对象)