JAVA虚拟机及内存介绍
java虚拟机的介绍
- hotspot 至今 1.8版本做了一次大的修改!永久代(方法区,元空间) 存储位置发生改变!解决了oom的发生。
- 什么是OOM? OOM,全称“Out Of Memory”,翻译成中文就是“内存用完了”
Java内存分布介绍
- 程序计数器:线程私有:当前线程字节码执行的执行器!当分支 循环 异常的时候跳转的指示!线程的执行时间片轮转算法!同一时刻一个处理器只能执行一段字节码!当恢复到当前线程的时候需要靠程序计数器还原到之前的执行位置!
- jvm栈:线程私有:他的生命周期和线程是同步的!他是存储每个方法的!将方法转成一个栈针!栈针中有局部变量表!操作表!方法的出口等信息!
- 本地方法栈: 线程私有! 和虚拟机栈一样存储的是native修饰的方法!
- 堆:线程共享区域!可以说是虚拟机中最大的内存空间!用于存储对象!
堆中又分为新生代和老年代区域!更合理的控制gc回收扫描的频率!
- 方法区(永久代 1.8以后元空间):存储类的元数据(classm模板对象) 常量池!
- 注意:1.8之前永久代和方法区存在jvm虚拟机的运行空间!容易发生oom
- 注意:1.8以后永久代–》元空间!存储在jvm运行空间外,不容易发生oom
- 类的模板对象!类加载完毕以后 会在虚拟机的元空间中创建一个类的模板对象
只会存在一个!他与实体对象的区别 数量、存储位置!类的模板对象是你对类一切操作的起始资源!
类加载过程
- 过程:
- 加载 :将字节加载到虚拟机的元空间
- 将类的字节文件通过流的形式流向流向虚拟机
- 将流的数据转换成元空间要求的类的存储数据的格式
- 在元空间创建一个类的模板对象 注意:模板对象属于特殊的类型,存储在元空间中,而不是堆中。
- 连接
- 验证:验证模板对象是否符合Java的规则,是否影响虚拟机的安全。
- 文件格式验证:OXcofebaby
- 元数据的验证:
- 检查类有没有父类
- 检查类是不是final修饰的
- 有没有实现接口或者继承抽象类
- 有没有实现接口的方法和实现抽象方法
- 引用符号的验证:验证是否跨域引用变量或者调用方法
- 准备:就是给类的模板的对象属性设置默认值!这里的属性必须是用static修饰的!非static修饰实体属性属性将会在创建实体对象的时候在堆中一起初始化。
- 数字型为0
- Boolean为false
- char为’/u0000’
- 解析:更新元空间内部的模板类对象的信息
- 初始化:给模板对象进行赋值。
- 使用
- 卸载
- 双亲委派
- java有很多的类加载器!最顶级的启动加载器 BootstrapClassLoder,下面可能有很多层次!
- 当子加载器加载类的时候!向上反馈进行验证!最后到达启动加载器判断是否通过!
- 启动类加载器向下委派加载类!
对象创建过程
对象的创建方法:
- new关键字(指令):虚拟机检测到new关键字!去元空间查询类的模板信息!如果不存在走类加载过程!如果存在在堆中开辟对象空间(类加载以后就已经明确需要的空间大小)、并设置默认值!
- 优势:简单明了
- 缺点:一旦编码器确定了。运行时就固态
- 通过反射(reflection):一种创建对象!操作对象的技术(操作属性、方法)!运行期动态操作的技术!只需要有类的模板对象即可;
- 优点:动态操作,运行时可以操作
- 缺点:效率较慢!速度较慢!软调用!
堆内存的分配
指针碰撞:堆中内存分配比较均匀!有一个指针标签空闲和占用空间!创建对象占有指定的空间
标记预占有空间!将标记空闲和占有的指针移向预占有的指针!两个指针碰撞了空间分配完毕!
空闲列表:堆空间分配不均匀的时候!使用空闲列表!虚拟机会维护一个堆内存的空闲列表!当你创建对象的时候!去空闲列表中查询空间!有占有
分配的常见多线程问题
反射
反射的介绍
反射的目的:动态的创建对象,操作对象(方法、属性)
反射的前提:获取得到类的模板对象
反射的常用方法
- 如何获取类的模板对象:
- Class.forName(全限定名)
- 通常用于不知道具体的类,在执行过程中需要动态的操作这个类
- 类.class 这个class不是方法
- 对象.getClass()
- 根据类的模板对象获取类的信息
- getName() 获取类的全限定名
- getPackage()
- getSimpleName() 获取类名
- getInterfaces() 获取当前类实现的类或者接口
- 根据类的模板对象获取类的构造方法
- GetConstructor()
- getDeclaredConstructor()
- setAccessible(true) 受限制的构造方法需要暴力反射,打破条件修饰的限制
- 反射获取类的方法
- getMethod(方法名 ,参数类型.class)
- getDeclaredMethod(方法名 ,参数类型.class)
- setAccessible(true) 受限制的方法需要暴力反射,打破条件修饰的限制
- 反射获取类的属性
- getField(属性名)
- getDeclaredField(属性名)
- setAccessible(true) 受限制的方法需要暴力反射,打破条件修饰的限制
- 如何创建类的实例
- 类的模板对象调用newInstance();该方法必须包含无参构造
- 通过构造方法.newInstance():
- 如何执行类的方法
- method对象.invoke(实例对象,方法传入参数,方法传入参数…)
- 如何执行类的属性对象
- field对象.set(实例对象,属性值) 属性设置值!
- Object ret = field对象.get(实例对象) 获取属性值!
设计模式
设计模式介绍
设计模式(Design pattern )代表了最佳的实践 ,通常被有经验的面向对象的软件开发人员采用,设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的。
工厂模式
- 介绍:统一构建对象的一种方式,可以整体进行构建类型的切换!
- 实现的步骤:
- 定义一个构建对象的方式、可以进行构建类型的切换
- 创建一个工厂类的步骤
- 定义一个静态的方法
- 返回值是接口
- 方法中构建对应接口的实现类
- 在类中使用工厂调用工厂方法即可
- 利用工厂模式简化配置文件的设置:
- 创建一个工具类
- 创建一个工具类的工厂类
- 工厂类中创建一个实例化工具类的方法
- 给工具类设置属性
单例模式
- 介绍:
- 运行期只创建一个实例!(构造方法私有化)注意:单例模式对反射无效
- 分类:
- 饿汉:在刚开时就创建一个对象
- 私有化构造方法
- 实例化静态对象 这个对象属性私有化
- 创建静态方法返回实例对象
- 懒汉:在刚开始的时候,不创建对象,给他一个null值
- 私有化构造方法
- 声明一个null实例对象
- 创建一个静态的内部判断创建实例化的对象
- 优缺点比较:
- 饿汉式:天生的线程安全,但是额外的占用内存
- 懒汉式:节省额外的内存!但是线程非安全!需要自己加同步手段!
- 使用的场景:
- 一般情况都是用于工具类有非静态方法的场景!
枚举
魔法变量
- 是一种不规范的编码范式
- 随地声明一个变量值代表某一个含义
- 过了一段时间 你忘了 别人也不懂!
- 解决方法:变量常量化、枚举
变量常量化
- 创建一个常量类(Constants)
- 创建常量
- 注意一定要加好注释
枚举类型
创建一个枚举类
enum 枚举类{ 类型,类型; }
引用枚举类型
注解
介绍
- 注解技术jdk1.5版本更新!元数据!注解和注释非常相似!都是对类 属性 方法进行描述的一种手段!
- 但是不同点是 注释是为了给程序员看的信息!不会被加载到虚拟机中!也不会通过反射手段获取!
- 注解:也是描述!但是可以指定存活的周期!活到程序的运行时!可以通过反射的技术动态的获取注解的内容!注解可以在不改变原有的代码逻辑和结构的情况下额外的添加信息!
常见的注解
- Override
注解的学习重点
- 创建自定义注解
- 使用注解
- 反射得到注解
注解的使用场景
替代外部的配置文件!
注解的声明
自定义注解
创建一个注解类 @interface 注解的名称
添加一些属性
以方法的形式 类型 属性名();
注解的属性类型可以注解 声明属性: 注解名 属性名()
使用的时候 属性名 = @注解名()
属性一旦声明!就必须赋值除非设置默认值 类型 属性名() default 默认值!
如果声明是数组类型 类型 [] 属性名();
建议value属性!因为只有value属性的时候可以直接不用指明属性名进行复制
例如:@注解(值) 赋值给value
元注解
注解上的注解 加载注解上的注解就叫元注解!元注解一共有5个!
作用:限制注解的使用
三个常用的并且重要的元注解:
- @retention(RetentionPolicy.RUNTIME) Source Class(默认值) 自带注解 Runtime 运行过程中可以反射获取
- @Target({ElementType.TYPE,ElementType.FIELD}) 运行的位置限制
不常用:
@Document 可以提示注解的注释
@Inherited继承传递!子类可以继承父类的注解
@Repeatable 一个注解可以再同一个位置写多遍!本意就是将多个子注解转成容器注解
要求注解有容器注解,并且容器注解中有注解的集合value属性容器注解
ElementType.ANNOTATION_TYPE 可以给一个注解进行注解 ElementType.CONSTRUCTOR 可以给构造方法进行注解 ElementType.FIELD 可以给属性进行注解 ElementType.LOCAL_VARIABLE 可以给局部变量进行注解 ElementType.METHOD 可以给方法进行注解 ElementType.PACKAGE 可以给一个包进行注解 ElementType.PARAMETER 可以给一个方法内的参数进行注解 ElementType.TYPE 可以给一个类型进行注解,比如类、接口、枚举
- 反射注解:
- 如果想获得注解
- 注解的元注解retention 要求 runtime
- 反射获取 类模板对象(获得类上的注解) 属性对象(可以获取属性上的注解)
- 属性模板对象的方法getAnnotation(Param.class)可以获取对应的注解(方法上注解)!
- isAnnotationPresent(Param.class);判断是否存在runtime类型的注解
- getAnnotation(Param.class);获取具体的注解对象注解对象获取属性
- 注解对象.属性名();
//反射注解 public static void main(String[] args) throws Exception { //通过反射技术 将注解的属性设置给执行eat的属性 //1.反射获取类的方法 // 1、获取类的模板对象 Class<TestPerson> testPersonClass = TestPerson.class; // 2、实例化对象 TestPerson testPerson = testPersonClass.newInstance(); // 3、 获取方法的模板对象 Method eat = testPersonClass.getMethod("eat", String.class, int.class); //2.获取方法上的注解 //1、判断是否存在该注解 , 注意,Person.class是注解的模板对象 boolean is = eat.isAnnotationPresent(Person.class); Person annotation = null ; //2、获取注解 if (is) { annotation = eat.getAnnotation(Person.class); } //3.反射执行方法并将注解的属性获取到设置到方法的参数列表! //1、获取注解的属性值 int age = annotation.age(); String value = annotation.value(); //2、执行方法 Object invoke = eat.invoke(testPerson, value, age); System.out.println(invoke); }