前言
众所周知,Java最大的特性就是平台无关性,即一次编译,到哪都能执行,那么它是如何实现的呢?下图为java的编译过程:
Java源码首先被编译成字节码,再由不同平台的JVM进行解析,Java语言在不同的平台上运行不需要进行重新编译,Java虚拟机在执行字节码的时候,把字节码转换成具体平台上的机器指令。
也从以上过程中看出JVM的作用,那么如果JVM直接解析源码为机器码会怎么样?
答:每次执行都需要做语义分析、词法分析方面的检查;与别的语言兼容较差。
JVM体系结构
- class loader:依据特定格式,加载class文件到内存
- execution engine:对命令进行解析
- native interface:融合不同开发语言的原生库来提供Java使用
- runtime data area:JVM内存空间结构模型
反射(这里引入反射机制是通过反射机制能更好理解源码的结构)
概念:Java反射机制是在运行状态中,对任意一个类,都知道这个类的方法和属性;对任意一个对象,都能够调用它的任意方法和属性;动态获取信息以及动态调用对象方法的功能成为Java的反射机制。
常用的反射所用的函数:
- getDeclaredMethod()——获取方法(可以是私有的,但无法获取继承的)
- getMethod()——获取方法(可以是继承的,但不能是私有的)
- invoke()——调用
- getDeclaredField()——获取属性
- getInstance()——获取对象实例
了解了反射机制后下面我们来好好研究ClassLoader是何物?
概念:classloader主要工作在class装载的加载阶段,其主要作用是从系统外部获得class二进制数据流。它是Java的核心组件,所有的class都是由classloader进行加载的,classloader负责通过将class文件里的二进制流装载进系统,然后交给Java虚拟机进行连接、初始化等操作。
ClassLoader的种类:
- BootStrapClassLoader:C++编写,核心库java.*
- ExtClassLoader:Java编写,扩展库javax.*
- AppClassLoader:Java编写,加载程序所在目录
- 自定义classloader:Java编写,定制加载
findclass()、defineclass()
ClassLoader的双亲委派机制
顺序:自底向上检查是否加载,都没有被加载则自顶向下加载 (结合源码食用更佳)
类加载方式:隐式加载(new)、显式加载(loadclass,forname等)
类加载过程:加载——链接——初始化
- 加载:通过classloader加载class文件字节码,生成class对象
- 链接:校验:检查加载class的正确性和安全性
准备:为类变量分配存储空间并设置类变量初始值
解析:JVM将常量池内的符号引用转换为直接引用 - 初始化:执行类变量赋值和静态代码块
LoadClass和ForName的区别:ForName得到的class是已经初始化完成的,LoadClass得到的class还未链接
JVM内存模型(JVM Memory)
二话不说,先上图比较直观
线程私有:程序计数器、虚拟机栈、本地方法栈
线程共享:方法区、堆
程序计数器:
- 当前线程所执行的字节码行号指示器;
- 改变计数器的值来选取下一条需要执行的字节码指令;
- 和线程是一一对应的关系(私有的原因);
- 对Java方法计数,如果是native方法则计数器值为undefined;
- 只做计数所以不会发生内存泄露。
虚拟机栈:
- Java方法执行的内存模型;
- 包含多个栈帧(局部变量表+操作栈+动态连接+返回地址)。
StackOverFlowError:当前执行栈的深度大于jvm规定的栈的最大深度。
OutOfMemoryError:jvm内存严重不足。
本地方法栈:类似虚拟机栈,作用于标注了native的方法.
堆:对象实例的分配区域、GC管理的主要区域。
Java内存模型中堆和栈的区别:
- 管理方式:栈自动释放,堆需要GC;
- 空间大小:栈比堆小;
- 碎片相关:栈产生的碎片远小于堆;
- 分配方式:栈支持静态和动态分配,堆仅支持动态分配;
- 效率:栈效率比堆高。
内存分配策略:
- 静态存储:编译时确定每个数据目标在运行时的存储空间需求
- 栈式存储:数据区需求在编译时未知,运行时模块入口前确定
- 堆式存储:编译时或运行时模块入口都无法确定,动态分配
JVM三大性能调优参数-Xms -Xmx -Xss的含义?
-Xss:规定了每个线程虚拟机栈的大小
-Xms:堆的初始值
-Xmx:堆能达到的最大值
例子:
intern()方法在JDK6、JDK6后的版本间的区别:
-
JDK6:调用intern方法时,如果字符串常量池先前已创建出该字符串对象,则返回池中的该字符串的引用。否则,将此字符串对象添加到字符串常量池中,并且返回该字符串对象的引用。
-
JDK6以后:调用intern方法时,如果字符串常量池先前已创建出该字符串对象,则返回池中的该字符串的引用。否则,如果该字符串对象已经存在于Java堆中,则将堆中对此对象的引用添加到字符串常量池中,并且返回该引用;如果堆中不存在,则在池中创建该字符串并返回其引用。
转载:https://blog.csdn.net/weixin_43806636/article/details/104847796