你不得不知道的 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}` </pre>

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

方法执行前 打印 open Transaction

方法执行后 打印 close Transaction

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


最新2020整理收集的一些高频面试题(都整理成文档),有很多干货,包含mysql,netty,spring,线程,spring cloud、jvm、源码、算法等详细讲解,也有详细的学习规划图,面试题整理等,需要获取这些内容的朋友请加Q君样:11604713672

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,686评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,668评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,160评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,736评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,847评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,043评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,129评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,872评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,318评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,645评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,777评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,470评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,126评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,861评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,095评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,589评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,687评论 2 351

推荐阅读更多精彩内容