zoukankan      html  css  js  c++  java
  • Java jvm 类加载 反射

    Java 底层

    jvm,类加载,反射

    Java语言是跨平台语言,一段java代码,经过编译成class文件后,能够在不同系统的服务器上运行;因为java语言中有虚拟机jvm,才有了跨平台,java为了实现跨平台,在jvm上投入了很大的研发开发资源。jvm是java的底层,本文学习探讨下java的jvm及关联的类加载和反射知识

    JVM

    JVM是Java Virtual Machine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。

    Java语言的一个非常重要的特点就是与平台的无关性。而使用Java虚拟机是实现这一特点的关键。一般的高级语言如果要在不同的平台上运行,至少需要编译成不同的目标代码。而引入Java语言虚拟机后,Java语言在不同平台上运行时不需要重新编译。Java语言使用模式Java虚拟机屏蔽了与具体平台相关的信息,使得Java语言编译程序只需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行。Java虚拟机在执行字节码时,把字节码解释成具体平台上的机器指令执行。 [1]

    jvm的构成

    jvm周期:是在java程序执行时运行,程序结束时停止

    jvm的基本结构有:类加载子系统、本地方法栈、Java栈、方法区、Java堆、pc寄存器,垃圾回收,执行引擎

    类加载子系统

    java是面向对象语言,逻辑代码中的类文件执行逻辑前,是需要jvm读取class文件并校验初始化后才能使用的,包括变量,方法,构造。

    类加载系统可以认为是在使用到java对像时(抽象),对java对象字节码的读取加载预编译(具体),之后不再加载(读取校验一次)。

    Java栈

    栈是先进后出的结构,java栈时一块线程私有的内存空间,可以理解为一个java线程对应一个java栈,栈和线程密切关联,栈包含线程运行的实时信息,如当前运行方法地址,方法中的瞬时变量等信息

    方法区

    在一个jvm实例的内部,类型信息被存储在一个称为方法区的内存逻辑区中。类型信息是由类加载器在类加载时从类文件中提取出来的。类(静态)变量也存储在方法区中。

    Java堆

    java堆是和应用程序关系最为密切的内存空间,几乎所有的对象都存放在堆上。并且java堆是完全自动化管理的,通过垃圾回收机制,垃圾对象会被自动清理,而不需要显示的释放。

    pc寄存器

    存放计算机下一步要执行的指令的地址,

    垃圾回收

    因为程序运行没创建一个对象都需要使用硬件的内存资源,不能无限使用,jvm的垃圾回收能够自动回收不再使用的java对象,使内存空间有效利用。垃圾回收线程是后台执行的,不需要认为回收内存垃圾,即使有垃圾回收方法调用,但并不能控制jvm如何去将一个对象失效回收。

    执行引擎

    Java 字节码指令指向特定逻辑得本地机器码,而JVM 解释执行Java字节码指令时,会直接调用字节码指向得本地机器码;

    java底层由C语言编写,执行java程序时,jvm每读取一个字节码指令动作,执行引擎就解析解释执行本地系统对应的本地机器码。

    类加载

    虚拟机把描述类的数据从 Class 文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的 Java 类型,这就是虚拟机的类加载机制。

    在Java语言里面,类型的加载、连接和初始化过程都是在程序运行期间完成的

    双亲委派机制

    作为软件开发语言,java在安全方面也有很高的要求,所以类加载是有一套规则的,jre是java运行时的环境,包括很多基本类,如java.lang.String 是字符串类,这个类很基础也很重要,那在加载的时候不能允许加载的String类被篡改,java保证类加载安全,首先看是否已经加载,如果没有查看核心库是否有此类,没有此类才会去扩展环境找类文件加载,这种机制保证了类在加载时的唯一性和安全性。

    java类加载一般来说是询问自己的parentClassLoader 加载,如果没有加载成功,才自己加载,加载顺序是自上而下

    java.lang.ClassLoader 加载类方法

    protected Class<?> loadClass(String name, boolean resolve)
            throws ClassNotFoundException
        {
            synchronized (getClassLoadingLock(name)) {
                // First, check if the class has already been loaded
                Class<?> c = findLoadedClass(name); //0.是否已加载
                if (c == null) {
                    long t0 = System.nanoTime();
                    try {
                        if (parent != null) {
                            c = parent.loadClass(name, false); //1.没有加载,首先通过父类加载器加载
                        } else {
                            c = findBootstrapClassOrNull(name); //1.没有父类加载器时加载方式
                        }
                    } catch (ClassNotFoundException e) {
                        // ClassNotFoundException thrown if class not found
                        // from the non-null parent class loader
                    }
    
                    if (c == null) { 
                        // If still not found, then invoke findClass in order
                        // to find the class.
                        long t1 = System.nanoTime();
                        c = findClass(name);
    					//如果父类没有加载到类,则使用findClass方法去加载类(这个方法可以重写自定义)
                        // this is the defining class loader; record the stats
                        sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                        sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                        sun.misc.PerfCounter.getFindClasses().increment();
                    }
                }
                if (resolve) {
                    resolveClass(c);
                }
                return c;
            }
        }
    

    反射

    反射是Java重要的技术点,在框架开发,AOP切面编程代理等方面都需要反射方面的技术去实现。

    Java反射机制主要提供了以下功能:

    • 在运行时判断任意一个对象所属的类。
    • 在运行时构造任意一个类的对象。
    • 在运行时判断任意一个类所具有的成员变量和方法。
    • 在运行时调用任意一个对象的方法。
    • 生成动态代理。

    反射相关的类

    Class 类的字节码对象

    Field 类的属性

    Method 类的方法

    Constructor 类的构造方法

    Annotation 类(方法字段)的注解

    反射的使用

    一般使用

    模拟事务的注解

    @Target({ElementType.TYPE,ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    public @interface defTransaction {
    
    }
    

    普通封装对象

    public interface People {
    
    	String getName();
    	void setName(String name);
    	Integer getAge();
    	void setAge(Integer age);
    	BigDecimal getMoney();
    	void setMoney(BigDecimal money);
    	@defTransaction
    	void addMoney(BigDecimal addNum);
    	@defTransaction
    	void subTractMoney(BigDecimal subNum);
    	
    }
    
    
    public class TestPeople implements People{
    	
    	// 姓名
    	public String name;
    	
    	// 年龄
    	private Integer age;
    	
    	// 钱
    	private BigDecimal money;
    	
    	public String getName() {
    		return name;
    	}
    	
    	public void setName(String name) {
    		this.name = name;
    	}
    	public Integer getAge() {
    		return age;
    	}
    	public void setAge(Integer age) {
    		this.age = age;
    	}
    	public BigDecimal getMoney() {
    		return money;
    	}
    	public void setMoney(BigDecimal money) {
    		this.money = money;
    	}
    
    	@Override
    	public void addMoney(BigDecimal addNum) {
    		this.money = this.money.add(addNum);
    		
    	}
    
    	@Override
    	public void subTractMoney(BigDecimal subNum) {
    		this.money = this.money.subtract(subNum);
    	}		
    }
    

    反射测试类

    public class ReflectTest {
    
    	public static void main(String[] args) {
    		// 普通对象创建 使用new
    		People testPeople = new TestPeople();
    		testPeople.setName("Frank");
    		testPeople.setAge(18);
    		testPeople.setMoney(new BigDecimal(10));
    		System.out.println("json:" + JsonUtil.objectToJson(testPeople));
    		
    		// 反射创建对象 class.newInstance()
    		ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
    		try {
    			Class<?> clazz = contextClassLoader.loadClass("com.domoment.leaves.common.util.reflect.TestPeople");
    			if(clazz != null) {
    					Object people = clazz.newInstance();
    					System.out.println("newInstance start json:" + JsonUtil.objectToJson(people));
    					
    					// 通过反射执行方法
    					Method setName = clazz.getMethod("setName", String.class);
    					setName.invoke(people, "inoverFrank");
    					
    					
    					System.out.println("newInstance end json:" + JsonUtil.objectToJson(people));
    					
    			}
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    	}
    }
    

    使用反射实现代理

    代理类DefProxy (People是被代理类)

    
    public class DefProxy implements InvocationHandler{
    
    	 // 这个就是我们要代理的真实对象
        private Object subject;
        
        //    构造方法,给我们要代理的真实对象赋初值
        public DefProxy(Object subject){
            this.subject = subject;
        }
        
    	@Override
    	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    		Annotation[] annotations = method.getDeclaredAnnotations();
    		boolean transactionOpen = false;
    		for (Annotation annotation : annotations) {
    			if(annotation instanceof defTransaction) {
    				transactionOpen = true;
    				break;
    			}
    		}
    		if(transactionOpen) { //当方法上有 defTransaction 注解时,执行方法前开启事务
    			System.out.println("open Transaction");
    		}
    		System.out.println("proxy:" + method.getName());
    		Object result = method.invoke(subject, args);
    		
    		if(transactionOpen) { //当方法上有 defTransaction 注解时,执行方法后关闭事务
    			System.out.println("close Transaction");
    		}
    		return result;
    	}
    
    }
    

    代理测试代码

    public class ReflectTest {
    
    	public static void main(String[] args) {
    		// 反射创建对象
    		ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
    		try {
    			Class<?> clazz = contextClassLoader.loadClass("com.domoment.leaves.common.util.reflect.TestPeople");
    			if(clazz != null) {
    					Object people = clazz.newInstance();
    					System.out.println("newInstance start json:" + JsonUtil.objectToJson(people));
    					
    					
    					Method setName = clazz.getMethod("setName", String.class);
    					setName.invoke(people, "inoverFrank");
    					
    					System.out.println("newInstance end json:" + JsonUtil.objectToJson(people));
    					
    					InvocationHandler handler = new DefProxy(people);
    					
                    	// 构造代理对象
    					People proxyPeople = (People)Proxy.
    							newProxyInstance(handler.getClass().getClassLoader(), people.getClass().getInterfaces(), handler);
    
    					proxyPeople.setName("proxySetFrank");
    					proxyPeople.setAge(20);
    					proxyPeople.setMoney(new BigDecimal(999));
    					System.out.println("proxyPeople end json:" + JsonUtil.objectToJson(people));
    					proxyPeople.addMoney(new BigDecimal(20));
    					System.out.println("proxyPeople add json:" + JsonUtil.objectToJson(people));
    					proxyPeople.subTractMoney(new BigDecimal(17));
    					System.out.println("proxyPeople end json:" + JsonUtil.objectToJson(people));
    					
    			}
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    	}
    }
    

    控制台打印

    newInstance start json:{}
    newInstance end json:{"name":"inoverFrank"}
    proxy:setName
    proxy:setAge
    proxy:setMoney
    proxyPeople end json:{"name":"proxySetFrank","age":20,"money":999}
    open Transaction
    proxy:addMoney
    close Transaction
    proxyPeople add json:{"name":"proxySetFrank","age":20,"money":1019}
    open Transaction
    proxy:subTractMoney
    close Transaction
    proxyPeople end json:{"name":"proxySetFrank","age":20,"money":1002}
    

    可以看到方法上有 defTransaction 注解的时候,

    方法执行前 打印 open Transaction

    方法执行后 打印 close Transaction

    这是模拟,真实场景就可以将打印改为代理时扩展方法,如数据库操作时候,开启关闭事务

  • 相关阅读:
    SpringBoot配置文件数据格式三种写法
    实施工程师
    Linux命令大全
    PDCA原则
    cmd命令下载maven管理需要的依赖jar包
    java集合体系结构总结
    回溯
    红黑树详解
    Java 垃圾回收算法
    MySQL优化:如何避免回表查询?什么是索引覆盖
  • 原文地址:https://www.cnblogs.com/Narule/p/13870562.html
Copyright © 2011-2022 走看看