就是你小子悄悄咪咪把bug写出来的?
BeanDefinition BeanDefinition 是 Spring Framework 中定义 Bean 的配置元信息接口 创建 BeanDefinition 通过 BeanDefinitionBuilder 1private static void createdByBeanDefinitionBuilder() { 2 BeanDefinitionBuilder beanDefinitionBuilder 3 = BeanDefinitionBuilder.genericBeanDefinition(User.class); 4 beanDefinitionBuilder 5 .addPropertyValue("id", 123) 6 .addPropertyValue("name", "dylan"); 7 BeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition(); 8 } 使用 AbstractBeanDefinition 以及其派生类 1private static void....
垃圾回收器的分类 按线程数分 串行垃圾回收器 适用于单 CPU 或者较小内存的硬件平台 在 Client 模式下的默认回收器 并行垃圾回收器 适用于多 CPU 同时执行垃圾回收,提升应用吞吐量 按工作模式分 并发式垃圾回收器 回收线程与应用程序交替工作,尽可能减少应用程序的停顿时间 独占式垃圾回收器 - STW 一旦运行,暂停用户线程,直到垃圾回收过程完全结束 按碎片处理方式分 压缩式垃圾回收器 在回收完成之后,对存活对象进行压缩整理,消除内存碎片 对象空间分配方式:指针碰撞 非压缩式垃圾回收器 无压缩整理过程 对象空间分配方式:空闲列表 按工作的内存区间分 年轻代垃圾回收器 Serial ParNew Parallel Scavenge 老年代垃圾回收器 Serial Old Parallel Old CMS 整堆垃圾回收期 G1 评估 GC 的性能指标 吞吐量:用户线程运行时间占总运行时间的比例 吞吐量 = 用户线程运行时间 / 用户线程运行时间 + 垃圾回收线程运行时间 暂停时间:执行垃圾....
基本概念 什么是垃圾(Garbage) 垃圾是指在运行程序中没有任何指针指向的对象,这个对象就是需要被回收的垃圾。 为什么需要 GC 不进行 GC,内存迟早会被消耗完 GC 的过程中,会进行碎片整理 没有 GC 无法保证应用程序的正常进行 Java 自动内存管理 优点: 降低内存泄漏和内存溢出的风险 使得开发人员更专心地专注于业务开发 缺点: 弱化开发人员在程序出现内存溢出时定位问题和解决问题的能力 对 JVM 进行必要的监控和调节 GC 作用的区域 GC 主要作用于 Java 堆和方法区(永久代/元空间) 频繁收集新生代 较少收集老年代 基本不懂永久代/元空间 System.gc() 的理解 显示触发 Full GC 无法保证 GC 的执行时间 内存溢出 Java 虚拟机堆内存设置不够 代码中创建大量大对象且长时间不能被垃圾收集器收集 抛出异常之前,通常会触发 GC 有些场景不会触发 GC:分配一个超大对象,超出了堆内存的最大值,会直接抛出异常 内存泄漏 严格上的定义:有对象不会被程序用到了,但是 GC 又不能回收他们的情况,叫内存泄漏 宽泛上的定义:对象生命....
String 基本特性 定义方式 1String s1 = "字面量定义"; //字符串 "字面量定义" String对象直接进入字符串常量池中 2String s2 = new String("构造器构造"); final 修饰,不可继承 实现 Serializable、Comparable 接口,支持序列化和可以比较大小 底层结构 jdk 8:final char [] value 存储数据 jdk 9:final byte[] value 存储数据 字符串的不可变性 内存结构 1public class StringTest { 2 3 public static void main(String[] args) { 4 /* 5 对象1:new String对象 6 对象2:常量池中对象 "abc",对应字节码指令 'ldc' 7 */ 8 String s = new String("abc"); 9 10 /* 11 对象1:new StringBuilder() 12 对象2:new String()对象 13 对象3:常量....
概述 执行引擎的任务就是将字节码指令解释/编译为对应平台上的本地的机器指令 Java 编译和执行的过程 由于 JVM 同时存在解释器和即时编译器,所以 Java 是半编译半解释型语言 前端编译器 把 .java 文件编译成 .class 文件 Sun 的 javac - 全量编译 Eclipse JDT 的 ECJ - 增量式编译 解释器 - Interpreter 对字节码采用逐行解释的方式执行:将每条字节码命令 翻译 成本地机器语言指令,再执行。 字节码解释器 通过纯软件代码模拟字节码的执行,效率低下 模板解释器 将每一条字节码和一个模板函数相关联,模板函数中直接产生这条字节码执行时的机器码 即时编译器 - JIT(Just In Time Compiler) 虚拟机将字节码代码直接编译成与本地机器平台相关的机器语言 **即时编译的目的:**将整个函数体编译成机器码,每次函数执行时,只执行编译之后的机器码即可。 C1 编译器 Client 模式下的 JIT 编译器。 简单可靠的优化,耗时短 C2 编译器 Server 模式下的 JIT 编....
概述 不是虚拟机运行时数据区的一部分,也不是 《Java 虚拟机规范》 中定义的内存区域。 直接内存是在 Java 堆外的、直接向系统申请的内存区间。 来源于 NIO ,通过 DirectByteBuffer 操作本地内存 直接内存的访问速度优于 Java 堆。(读写性能高) 不受最大堆内存参数(-Xmx)的影响,但是 Java 堆 + 直接内存 受限于操作系统能给出的最大内存 也会出现 OOM 1java.lang.OutOfMemoryError: Direct buffer memory MaxDirectMemorySize 设置直接内存大小,不设置时默认与 —Xmx 的值相同 1-XX:MaxDirectMemorySize=20m 缺点: 分配收回成本高 不受 JVM 内存回收管理 Java 进程占用的内存 = Java 堆 + 本地内存
对象实例化的方式 new 关键字 new Object(); 反射机制 java.lang.Class#newInstance:只能调用 public 的空参构造器 java.lang.reflect.Constructor#newInstance:可以调用任何权限的空参、带参构造器 实现 Cloneable 接口,调用 clone() 复制对象(浅复制) 反序列化 第三方库 Objenesis 创建对象的步骤 加载对象对应的类 判断 new 指令的参数能否在 Metaspace 常量池中定位到一个类的符号引用,并且检查其类元信息是否存在(类是否被类加载器加载) 有类元信息则进行下一步 无,则在双亲委派模式下,使用当前类加载器进行加载 若找不到 .class 文件,则抛出异常 ClassNotFoundException 为对象分配内存 处理并发安全问题 每个线程预先分配私有的 TLAB 采用 CAS 失败重试、区域加锁 计算空间大小 在堆中分配内存空间 内存规整,使用 指针碰撞 使用过的内存在一边,空间的内存在....
方法区 对于 Hotspot VM 来说,方法区有一个别名:Non-Heap (非堆),以此和堆分开。方法区可以看作是一块独立于 Java 堆 的内存空间。 方法区是线程共享的 方法区在 JVM 启动时创建,其物理内存空间可以是不连续的,但是逻辑上是连续的。 方法区大小可以选择固定大小或者可扩展 方法区内存溢出会报异常 JDK 7 及其之前:java.lang.OutOfMemoryError:PermGen space JDK 8:java.lang.OutOfMemoryError:Metaspace 关闭 JVM 会释放方法区内存 JDK8 用元空间替代了永久代,并且元空间不在设置在虚拟机的内存中,而是使用本地内存。 设置方法区大小 JDK 7 及其之前 1-XX:PermSize=size #设置永久代初始分配空间 默认值20.75M 2-XX:MaxPermSize=size #设置永久代最大可分配空间。32位是64M,64位是82M JDK 8 1-XX:MetaspaceSize #设置元空间初始分配空间 windows下,默认21M 2-XX:Max....
堆 - Heap 每一个 JVM 实例只存在一个堆空间,堆也是 Java 内存管理的核心区域 Java 堆在 JVM 启动时创建,其大小也就确定了。 1-Xmx16g # 最大堆内存 16G = -XX:MaxHeapSize 2-Xms8g # 起始堆内存 8G = -XX:InitialHeapSize 3-XX:+PrintGCDetails # 打印GC的详细信息 一般会把 -Xms 和 -Xmx 的值配置相同的值,为了在垃圾回收机制清理完堆区后不需要重新分隔计算堆区的大小,从而提高性能 默认大小 -Xms = 物理内存 / 64 -Xmx = 物理内存 / 4 Java 堆空间物理上可以不连续,逻辑上必须连续。 所有线程共享 Java 堆空间,但是堆中有线程私有得缓冲区(TLAB - Thread Local Allocation Buffer) 规范:所有得对象实例和数据都应当在运行时分配在堆上。 实际使用中,“几乎”所有的对象实例都在堆上分配 栈得栈帧中保存引用,引用指向对象或数据在堆中得位置。 方法结束后,堆中得对象不会....
本地方法 - Native Method 一个 Native Method 就是一个 Java 调用非 Java 代码的接口。 本地方法栈 - Native Method Stack 本地方法栈用于管理本地方法的调用 当一个线程调用一个本地方法时,它就进入了一个全新的并且不再受 Java 虚拟机限制的世界。它和虚拟机拥有同样的权限。 本地方法可以通过本地方法接口访问虚拟机内部的运行时数据区 直接使用本地处理器的寄存器 直接从本地内存的堆中分配内存 Java 虚拟机规范不强制要求本地方法栈使用的语言、具体实现方法、数据结构等,所以 JVM 产品可以不支持本地方法。 Hotspot JVM 中,本地方法栈与虚拟机栈合二为一
虚拟机栈 什么是栈 Java 虚拟机栈(Java Virtual Machine Stack),每个线程在创建时都会创建一个虚拟机栈,其内部保存一个个栈帧,对应着一次次的 Java 方法调用。 线程私有的 生命周期和线程一致 栈帧是基本的存储单位 作用 主管 Java 程序的运行,保存方法的局部变量、部分结果,并参与方法的调用与返回。 特点 栈式一种快速有效的分配存储方式,访问速度仅次于程序计数器(PC 寄存器) JVM 直接对 Java 栈的操作只有两个: 方法执行 - 入栈 方法执行完毕 - 出栈 不存在垃圾回收问题,存在 OOM 栈中可能出现的异常 Java 虚拟机规范允许 Java 栈的大小设置两种模式:固定大小 或者 动态扩展 StackOverflowError 采用固定大小模式,在线程创建时固定容量,如果线程请求分配的栈容量超过了 Java 虚拟机栈允许的最大容量,则抛出 StackOverflowError 异常 OutOfMemoryError 采用动态扩展模式,在尝试扩展或者创建新线程时没有足够的内存区创建对应的 Java 虚拟机栈,则抛....
JVM 中的程序计数寄存器(Program Counter Register),又叫 PC 计数器或者指令计数器,是对物理 PC 寄存器的一种抽象模拟。 作用 PC 寄存器用来存储指向下一条指令的地址(将要执行的指令代码)。由执行引擎读取下一条指令。 特点 一块很小的内存空间,也是运行速度最快的存储区域 线程私有,与线程的生命周期保持一致 存储当前线程正在执行的 Java 方法的 JVM 指令地址。如果是 native 方法,则是 undefined 唯一一个在 JVM 规范中没有规定任何 OutOfMemoryError 情况的区域
类加载子系统结构 作用 类加载子系统 负责从 文件系统 或者 网络 中加载 Class 文件(Class 文件开头有特定的文件标识)。 ClassLoader 只负责 class 文件的加载 Execution Engine 决定 class 文件是否可以运行 类的加载过程 加载 - Loading 通过一个类的全限定名获取定义此类的二进制字节流 字节流的静态存储结构 ----> 方法区的运行时数据结构 在内存中生成一个代表这个类的 java.lang.Class 对象,作为方法区这个类的各种数据的访问入口 .class 文件的来源 本地文件系统 通过网络获取 Web Applet 从 zip 压缩包中读取 jar、war 格式 运行时计算生成 动态代理 由其他文件生成 JSP 应用 从专有数据库中提取 .class 文件 从加密文件中获取 典型的防 Class 文件被反编译的保护措施 链接 - Linking 验证 - Verify 确保 Class 文件的字节流中包含信息符合当前虚拟机要求,保证被加载类的正确性,....
JVM 的整体结构 (HotSpot VM) JVM 的架构模型 JVM 采用 基于栈的指令集架构。 指令集架构 基于栈的指令集架构 设计和实现更简单,适用于资源受限的系统(嵌入式) 使用零地址指令方式分配,执行过程依赖操作栈。指令集更小,但是实现同样的功能,所需指令更多。 零地址指令:只有操作数,没有地址 不需要硬件支持,可以移植性更好,更好实现跨平台。 基于寄存器的指令集架构 典型应用:传统 PC、Android 的 Davlik Virtual Machine。 完全依赖硬件,可移植性差 性能优秀,执行高效 指令集更大,但是实现同样的功能,所需指令更少。 JVM 的生命周期 启动 Java 虚拟机的启动是通过引导类加载器(bootstrap class loader)创建的一个初始类(initial class)来完成的,这个类是由虚拟机的具体实现执行的。 执行 执行一个所谓的 Java 程序的时候,其本质是执行一个叫做 Java 虚拟机的进程 使用 jps 命令查看正在执行的 jvm 进程 1PS C:\Users\XXX> jps ....
Spring 核心注解场景分类 Spring 模式注解 Spring 注解 场景说明 起始版本 @Repository 数据仓储模式注解 2.0 @Component 通用组件模式注解 2.5 @Service 服务模式注解 2.5 @Controller Web 控制器模式注解 2.5 @Configuration 配置类模式注解 3.0 装配注解 Spring 注解 场景说明 起始版本 @ImportResource 替换 XML 元素 2.5 @Import 导入 Configuration 类 2.5 @ComponentScan 扫描指定 package 下标注 Spring 模式注解的类 3.1 @ComponentScan 默认扫描 当前类所在的包 源码分析如下:ComponentScanAnnotationParser.parse (line:123) 1public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentS....
依赖注入的模式和类型 自动绑定 (Autowiring) 自动绑定的限制和不足 Core Technologies (spring.io) Setter 方法注入 构造器注入 字段注入 方法注入 接口回调注入 Aware 系列接口回调 自动模式 內建接口 说明 BeanFactoryAware 获取 IoC 容器 - BeanFactory ApplicationContextAware 获取 Spring 应用上下文 - ApplicationContext 对象 EnvironmentAware 获取 Environment 对象 ResourceLoaderAware 获取资源加载器 对象 - ResourceLoad BeanClassLoaderAware 获取加载当前 Bean Class 的 ClassLoader BeanNameAware 获取当前 Bean 的名称 MessageSourceAware 获取 MessageSource 对象,用于 Spring 国际化 ApplicationEven....
依赖查找的来源 查找来源 Spring 內建 BeanDefintion Spring 內建单例对象 依赖注入的来源 注入来源 Spring 容器管理和游离对象(Resolvable Dependency) Spring BeanDefinition 作为依赖 元数据 BeanDefinition 存储元信息 注册 BeanDefinitionRegistry#registerBeanDefinition 类型 延迟和非延迟 顺序 Bean 生命周期顺序按照注册顺序 1// Still in startup registration phase 2this.beanDefinitionMap.put(beanName, beanDefinition); 3this.beanDefinitionNames.add(beanName); 源码中,通过 Map 结构保证唯一性, List 结构保证有序性 单例对象作为依赖来源 来源 外部普通 Java 对象 (不一定是 POJO ) 注册 SingletonBeanRegistry....
Java 原生依赖查找 单一类型依赖查找 JNDI javax.naming.Context#lookup(javax.naming.Name) JavaBean java.beans.beancontext.BeanContext 集合类型依赖查找 java.beans.beancontext.BeanContextServices#getCurrentServiceSelectors 层次性依赖查找 java.beans.beancontext.BeanContext Spring IoC 依赖查找 单一类型依赖查找 单一类型依赖查找接口: org.springframework.beans.factory.BeanFactory 根据 Bean 名称查找 org.springframework.beans.factory.BeanFactory#getBean(java.lang.String) org.springframework.beans.factory.BeanFactory#getBean(java.lang.St....
Spring Environment 抽象 统一的 Spring 配置属性管理 Spring Framework 3.1 开始引入 Environment 抽象,它统一 Spring 配置属性的存储,包括占位符处理和类型转换,不仅完整地替换 PropertyPlaceholderConfigurer,而且还支持更丰富的配置属性源(PropertySource) 条件化 Spring Bean 装配管理 通过 Environment Profiles 信息,帮助 Spring 容器提供条件化地装配 Bean Spring Environment 接口使用场景 ⽤于属性占位符处理 用于转换 Spring 配置属性类型 用于存储 Spring 配置属性源(PropertySource) 用于 Profiles 状态的维护 Environment 占位符处理 Spring 3.1 前占位符处理 组件:org.springframework.beans.factory.config.PropertyPlaceholderConfigurer 接口:org.spri....
Spring Bean 作用域 来源 说明 singleton 默认 Spring Bean 作用域,一个 BeanFactory 有且仅有一个实例 prototype 原型作用域,每次依赖查找和依赖注入生成新 Bean 对象 request 将 Spring Bean 存储在 ServletRequest 上下文 session 将 Spring Bean 存储在 HttpSession 中 application 将 Spring Bean 存储在 ServletContext 中 Singleton 作用域 Prototype 作用域 Singleton VS Prototype Singleton Bean 无论依赖查找还是依赖注入,均为同一个对象;Prototype Bean 无论依赖查找还是依赖注入,均为新生成的对象 如果依赖注入集合类型的对象,Singleton Bean 和 Prototype Bean 均会存在一个;Prototype Bean 有别于其他地方的依赖注入 Prototype Bean 无论是 Singleton....