JVM
JVM是Java Virtual Machine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的
Java语言的一个非常重要的特点就是与平台的无关性;而使用Java虚拟机是实现这一特点的关键。一般的高级语言如果要在不同的平台上运行,至少需要编译成不同的目标代码;而引入Java语言虚拟机后,Java语言在不同平台上运行时不需要重新编译
Java语言使用Java虚拟机屏蔽了与具体平台相关的信息,使得Java语言编译程序只需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行;Java虚拟机在执行字节码时,把字节码解释成具体平台上的机器指令执行;这就是Java的能够“一次编译,到处运行”的原因

JVM的生命周期
生命周期分为3个阶段,这三个阶段分别是: 初始化 、 运行中 、 销毁
初始化阶段
● 有4个钩子函数,触发条件是自动的 beforeCreate
● 组件即将创建,进行组件事件和生命周期的初始化
项目中一般不使用
● 有的开发者也会在这里进行数据请求created
组件创建完成
● 项目中:
1) 异步请求接口数据
2) 数据修改beforeMount
● 组件即将挂载
● 判断根实例组件是否有el选项,如果没有,那么手动挂载,判断根实例组件中的其他子组件是否有template选项,如果没有,那么使用outerHTML插入
● 项目中:
1) 异步请求接口数据
2) 数据修改mounted
● 组件挂载结束
● 使用真实DOM替换VDOM
● 项目中
1) 异步请求接口数据
2) 数据修改
3) 真实DOM操作可以了( Vue一般情况下不要直接操作真实DOM, 一般可以进行第三方库的实例化(静态数据渲染来的) )
总结: 初始化过程中在项目中使用
● 数据请求:created
● DOM操作: mounted
● 数据修改: beforeMount created mounted
运行中阶段
● 有2个钩子函数 : 触发条件是: 当data选项中的数据发生改变时 beforeUpdate
● 内部操作 : vdom生成和diff的对比 updated
● Rdom已经生成,可以进行异步数据请求得到的dom渲染的第三方库实例化 销毁阶段: 有2个钩子函数 beforeDestory: 组件即将销毁,准备调用 $destroy() 方法
● destoryed: 组件销毁结束
这两个钩子函数没有什么区别,功能很相似
● 项目中:
● 这两个钩子函数都可以用来做善后,把一些计时器,第三方库实例化出来的实例
组件销毁的两种方式
● 使用$destroy()方法(内部销毁)
● 缺点: 会将组件的模板也保留下来
● 使用开关销毁(外部销毁)
● v-if 这种类型的销毁不会留有模板
有一个钩子函数可以不写,这个钩子函数就是 render 函数
名词解释
钩子函数:
● 钩子函数就是options配置项中的一个方法,在特定的触发条件( 时机 )下会自动触发
● 除了根实例组件以外,其他的组件中的数据项 data 必须是一个函数,而且这个函数必须返回一个对象
为什么是函数呢?
● 原因:我们希望组件的数据是独立的,有一定作用域的
为什么返回值是对象?
● 原因: 是为了符合数据驱动原理设计,让observer将data选项中的数据设置 getter 和 setter
JVM内存模型
程序的运行最为重要的一块区域就是运行时数据区,其中运行时数据区可以分为如下几个子区域:

程序计数器
程序计数器是用于存放下一条指令所在单元的地址的地方
● 程序执行必须知道指令(计算机要通过指令才能执行),此时就需要从程序计数器中读取即将执行的指令的地址,通过这个地址就可以得到指令从而执行
● 比如线程即将执行一个Java方法,此时要先从程序计数器中取得即将执行的虚拟机字节码指令的地址。
虚拟机栈
● 该区域是线程私有的,虚拟机栈的生命周期和线程相同,在执行一个Java方法的时候会在该区域创建一个栈帧,栈帧用来保存局部变量表(在方法中定义的变量或者方法的参数都是方法的局部变量)、动态链接、方法出口、操作数栈等等信息
● 其实每个方法从执行到执行完毕就是一个入栈与出栈的过程。当入栈的速度大于出栈的速度的时候会出现栈内存溢出( Exception in thread "main" java.lang.StackOverflowError)。
本地方法栈
● 本地方法栈和虚拟机栈的作用很相似,不同点是虚拟栈是为Java方法服务的
● 本地方法栈是为native方法服务的(这种本地方法是没有方法体的,可以算是一个抽象方法,具体的实现是本地操作系统的c语言写的一些函数库中的方法实现的)
堆
● 堆是运行时数据区中最大的一块子区域
● 创建的实例对象和数组都需要在堆内存开辟空间,堆空间是线程共享的区域,该区域也是gc (垃圾回收)重要从狸的一个区,所以堆又被称为“GC堆”。
方法区
● 永久代实现了方法区,永久代是在JDK1.8之前出现的概念
● 在JDK1.8开始就没有了永久代,而是使用了元空间代替了。
● 方法区也是一块线程共享的区域,该区域主要保存的是已经加载到jvm 中的类的信息、常量、静态变量等等
类加载过程
当程序主动使用某个类时,如果该类还未被加载到内存中,则JVM会通过加载、连接、初始化3个步骤来对该类进行初始化;如果没有意外,JVM将会连续完成3个步骤,所以有时也把这3步骤统称为类加载或类初始化
类被加载到 JVM 开始,到卸载出内存,整个生命周期]如图:

加载
● 通过类名(地址)获取此类的二进制字节流
● 将这个字节流所代表的静态存储结构转换为方法区(元空间)的运行时结构
● 在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据访问入口
链接
就是将已经读入内存的类的二进制数据合并到JVM运行时环境中去,包含以下步骤:
验证
● 检验被加载的类是否有正确的内部结构,并和其他类协调一致
准备
● 准备阶段则负责为类的静态属性分配内存,并设置默认初始值;不包含final修饰的static实例变量,在编译时进行初始化。不会为实例变量初始化
解析
● 将类的二进制数据中的符号引用替换成直接引用。
初始化
类什么时候初始化?
● 创建类的实例,new对象
● 访问某个类或接口的静态变量,或者对该静态变量赋值
● 调用类的静态方法
● 反射(Class.forName(" "))
● 初始化一个类的子类(首先会初始化子类的父类)
● JVM启动时标明的启动类,即文件名和类名相同的那个类
● 注意:对于一个final类型的静态变量,如果该变量的值在编译时就可以确定下来,那么这个变量相当于“宏变量”。Java编译器会在编译时直接把这个变量出现的地方替换成它的值,因此即使程序使用该静态变量,也不会导致该类的初始化
● 反之,如果final类型的静态Field的值不能在编译时确定下来,则必须等到运行时才可以确定该变量的值,如果通过该类来访问它的静态变量,则会导致该类被初始化。
类的初始化顺序
对static修饰的变量或语句块进行赋值
● 如果同时包含多个静态变量和静态代码块,则按照自上而下的顺序依次执行。
● 如果初始化一个类的时候,其父类尚未初始化,则优先初始化其父类。
● 顺序是:父类static -> 子类static -> 父类构造方法 -> 子类构造方法
JVM自身独特的优点
HotSpot(Java 虚拟机),是较新的Java虚拟机技术,用来代替JIT(just-in-time compilation,及时编译)技术,可以大大提高Java运行的性能。Java原先是把源代码编译为字节码在虚拟机执行,这样执行速度较慢。而该技术将常用的部分代码编译为本地(原生,native)代码,这样显著提高了性能
对于初学者来说,尽管Hotspot有些神秘,但是性能方面它确实很棒,因为动态建模(dynamic profiling)是优良性能的捷径。HotSpot从运行应用中采样数据,从而可以优化代码,进而得到良好性能;它相当于以模仿人工的方法进行优化
在程序运行的开始,Java代码仍然解释执行,但HotSpot引擎开始进行采样(Profiling)。HotSpot引擎可以集中精力来对HotSpot代码进行深度优化,从而使这部分代码的执行更加迅捷。因此当HotSpot优化时,它为优化设立了一层保护来确保优化的基本原理有效;但当这层保护失效时,优化就会很慢
尾述
技术是无止境的,你需要对自己提交的每一行代码、使用的每一个工具负责,不断挖掘其底层原理,才能使自己的技术升华到更高的层面
Android 架构师之路还很漫长,与君共勉
PS:有问题欢迎指正,可以在评论区留下你的建议和感受; 欢迎大家点赞评论,觉得内容可以的话,可以转发分享一下
网友评论