JVM组成
1.方法区(线程共享)
方法区解释
方法区是线程共享的内存区域,用于存储被JAVA虚拟机加载的类信息,常量,静态变量即时编译后的数据
什么是即时编译
即时编译: 为了平衡启动和执行的效率,JVM结合解释执行和编译执行的特点,进行解释执行并对热点代码进行编译优化,这样的执行过程叫即时编译
异常
当方法区的内存无法满足内存分配时,将抛出OutOfMemoryError错误
注意事项
1.8及之后叫元空间,使用物理内存,不受JVM内存限制 常量池移到了堆中
2.堆(线程共享)
堆解释
堆是虚拟机中内存分配最大的一块区域被所有线程共享,所有对象和数组都需要在堆上分配内存,堆因此是GC主要活动的区域,被称为"GC堆"
GC 指垃圾回收
常量池1.8之后也在堆中
堆的内存分配
堆由新生代和老年代组成 内存比例为 1 : 2 其中新生代分为 伊甸园区 和 2个 幸存者区 内存比例为 8 : 1 : 1
也就是说 如果 堆的内存空间为 30G 那么 新生代默认的内存大小为 10G 老年代的默认大小为 20G
新生代中的伊甸园区占 8G 两个 幸存者区分别占1 G 内存
为什么需要两个幸存者区
这就跟GC算法有关了 在新生代中 使用的是 复制回收算法 避免了使用标记算法带来的内存碎片化问题
堆为什么要分代
最大的好处可以根据不同的代的特点存储不同的对象 分别进行管理 并且使用不同的回收算法
新生代中,每次垃圾收集时都发现大批对象死去,只有少量对象存活,便采用了复制算法,只需要付出少量存活对象的复制成本就可以完成收集。
而老年代中因为对象存活率高、没有额外空间对它进行分配担保,就必须采用“标记-清理”或者“标记-整理”算法。
3.栈(线程私有)
栈解释
JVM会为每个线程创建一个栈,所以栈是线程私有的,
每个方法在创建的时候都会有一个专属的栈帧,方法进栈是先进后出
栈帧的组成
栈帧分别由四部分组成 分别是 局部变量表 操作数栈 动态连接 出口
- 操作数栈可理解为java虚拟机栈中的一个用于计算的临时数据存储区。
- 局部变量表存放的是方法中的局部数据,所以局部数据会随着方法的执行完毕而丢失
- 动态链接在程序运行期间将符号引用转为直接引用
- 出口即方法执行完毕返回的位置
4.本地方法区(线程私有)
本地方法区解释
本地方法区与方法区类似,不过是C和C++的代码编译的方法,不是JAVA语音编写的
5.程序计数器(线程私有)
程序计数器解释
保存当前线程所正在执行的字节码指令的地址(行号)
类的加载过程
编译.java文件
当我们编写了一个.java后缀的文件时,首先由编译器编译成java虚拟机能识别的.class字节码文件 再由类加载器加载进内存
加载字节码文件
加载字节码进内存的过程分为三步
加载
将.class文件加载进内存
链接
初始化阶段又分为三步
验证
验证阶段验证字节码文件是否符号虚拟机规范的要求
准备
为类的静态变量分配内存,并设置默认值
解析
将符号引用转为直接引用
初始化
为静态成员变量设置默认值,并执行静态代码块方法和普通代码块方法,最后执行初始化方法
类加载器
类加载器负责将字节码文件加载进内存
JVM有三个类加载器 分别是 应用程序类加载器 AppClassLoader, 扩展类加载器 ExtClassLoader,引导类加载器 BootStrapClassLoader
应用程序类加载器 AppClassLoader
使用java语言编写 ,负责加载环境变量classpath或系统属性java.class.path指定的类库 自己写的类都是由应用程序加载类加载的
扩展类加载器 ExtClassLoader
使用java语言编写 ,从java.ext.dirs指定的路径下加载类库;或者从JDK安装目录的jre/lib/ext目录下加载类库 如果用户自定义的jar包放在jre/lib/ext下,也会自动由扩展类加载器加载
引导类加载器 BootStrapClassLoader
引导类加载器使用C/C++语言实现,在JVM内部,用于加载核心库,只加载包名为java,javax,sun开头的类
双亲委派模型
什么是双亲委派模型
当一个类收到类加载请求,它首先把类加载请求交给父类(如果还有父类,继续往上递交请求).如果父类无法加载该类,再交给子类加载
双亲委派模型的目的
防止内存中出现多份同样的字节码对象 ,保证核心.class不被篡改(出于安全性考虑)
|