Java进阶之JVM虚拟机(Java Virtual Machine)
Java进阶之JVM虚拟机(Java Virtual Machine)
JVM标准(JDK) JRokecthotsport C/S B/S
- JDK1.0 解释型语言
- JDK1.2 JIT(即时编译器-热点追踪技术)--解释+编译综合语言
- java -Xcomp File 本次使用编译型
- java -Xint File 本次使用解释型
- java -Djava.compiler 关闭编译器
- mixed mode 混合型
- 热点追踪技术
- 单纯的计数器模式
- 用的多的程序就是热点程序
- CompileThreshold=10000 调整热点追踪方法的上限值
- 方法内联
- 高内聚低耦合
- 取决于调用方法的方法,和被调用方法之间的代码量(大小)35个字节内,MaxInlineSize = 35
- 方法内联后方法的最大容量,FreqInlineSize = 325
虚拟机内存结构
- 错误:OutOfMemoryError
- 虚拟机内存大小(1.6以前63M 1.8一般获取系统内存的25%)
- JVM内存结构--面试官最喜欢系列
- 类的装载子系统(类的加载器,不算虚拟机内存结构):将类从硬盘装载到内存的一个主要的应用程序
- 应用类加载器 AppClassLaoader 用来加载我们在当前工程下自己编写的类
- 扩展加载器 ExtClassLoader 用来加载ext文件夹下所有的类
- 启动类加载器 BootClassLoader 用来加载JDK里所有系统使用到的类
- 自定义的类加载器 rt.jar
- 双亲委派机制工作过程:加载路径(某一个加载器只加载固定路径的类)
- 首先使用应用类加载器(AppClassLoader进行判定,如果此类已经被加载,则直接返回,如果未加载则进行步骤2
- 使用扩展类加载器(ExtClassLoader)从当前规定路径下进行判定,如果已加载则直接返回,如果未加载则向上委托给启动类加载器进行加载
- 启动类加载器从rt.jar中进行查找,如果已加载则直接返回,如果未加载则从启动类加载器规定路径下查找该类,如果找到则进行加载直接返回,如果未找到则指派给扩展类加载器进行加载。
- 扩展类加载器从当前规定路径下进行加载,如果加载成功直接返回,如果未成功则指派给应用类加载器进行加载。
- 应用类加载器从规定路径下加载该类,若加载成功则返回该类,如果未成功则产生ClassNotFoundException异常,终止查找步骤
- 双亲委派机制优缺点
- 优点:可以最大限度避免因为用户自定义类的全限定名和系统提供的核心类的权限定名冲突而导致的重复加载出错的问题。
- 缺点:从加载过程不难看出双亲委派机制虽然提升了安全性,但是缺降低的类的加载速度并且提升了加载过程的复杂程度,不过相对于安全性考虑,这些性能的牺牲是值得的。
- JVM加载过程 加载、链接(验证、准备、解析)、初始化、使用、卸载
- 全盘委托
- “全盘负责”是指当一个ClassLoader装载一个类时,除非显示地使用另一个ClassLoader,否则该类所依赖及引用的类也由这个ClassLoader载入。
- 类的加载器继承结构(找文件方法 1、找。2读)
- loadClass:加载类 寻找
- findClass:找到类
- defineClass:把类的内容加载到内存中
- 共享区 --负责存储
- 方法区 1.7以前 1.8后改名元数据空间
- 1.7以前方法区和堆区一起都是堆,为了区分后叫做方法区
- 主要存放,类,方法,静态属性
- 1.7以前,方法区和堆区共享虚拟机内存
- 1.8以后,堆区独享虚拟机内存,方法区使用物理内存
- 堆区
- 错误:OutOfMemoryError
- 创建的对象
- 数组对象
- GC -Xmx 设置内存最大容量
- GC -Xms 设置内存最小容量
- 非共享区 --负责运行
- 虚拟机栈
- 错误:StackOverflowError 栈内存泄露
- 方法(栈帧 局部变量的定义)
- 栈:线性表->先进后出 压栈 弹栈
- 栈帧:局部变量表、操作数栈、动态链接(方法指针、内存地址)、方法出口
- -Xss 调整虚拟机栈最大栈内存(当出现了方法调用出现栈内存泄露时)
- 本地方法栈
- 调用native修饰的方法 HotSpot中将虚拟机栈和本地方法栈合并了
- 程序计数器
- 程序计数器是一小块内存空间,可以看做是当前线程所执行的字节码的行号指示器
- 记录程序执行过程中某些情况被中断的位置
- 特点
- 如果线程正在执行Java方法,则计数器记录正在执行的虚拟机字节码指令地址
- 如果正在执行Native方法,则计数器值为空
- 此内存区域是唯一一个在虚拟机中没有规定OutOfMemoryError情况的区域
- 在任意确定的时刻,一个核心只会执行一条线程的指令,为了线程切换后能恢复到正确的执行位置,每条线程都要有一个独立的程序计数器,他们互不影响,独立存储
垃圾回收机制(major GC(full GC) | minor GC)
- Garbage Collection
- 工作:对象的创建和回收(内存的占用和释放)
- 错误:OutOfMemoryError(堆内存溢出)
public static void main(String[] args) { long l1 = Runtime.getRuntime().maxMemory();//JVM堆内存中最大容量 单位:字节B long l2 = Runtime.getRuntime().freeMemory();//空余空间大小 long l3 = Runtime.getRuntime().totalMemory();//在最大内存中当前最大允许占用内存 System.out.println(l1+" "+l2+" "+l3); System.out.println("占用内存:" + (l3-l2));
- 如何判断一个对象是可以被回收的: 当对象不再存在任何形式的引用指向时,即可被标记成可回收对象
- GC回收分手动回收和自动回收
- 手动回收 将引用指向null
- 自动回收 可以使用System.gc()提示GC尽快启动垃圾回收(major GC minor GC)
- 区别:
- 手动回收,回收效率高
- 自动回收,性能更好
- 可回收对象
- 程序计数法
- 可达性分析法
- 判断一个对象是否可以进行回收
- 判断一个对象是否存在在任何一条可达路径到GC Roots(GC根路径),如果可以达到,则说明不可回收,反之,可以回收
- 可以当做GC Roots的对象
- 虚拟机栈中存储的关于某个对象引用-局部变量表,操作数栈
- 方法区中的类静态属性引用的对象(static),常量引用的对象(final)
- 静态对象永远无法自动回收,是内存溢出最主要的问题产生点
- native方法本身也是一种
- 如何回收
- GC垃圾回收算法
- 标记清除法
- 复制算法
- 标记压缩法
- 分代算法
- 标记清除法
- 通过可达性分析
- 标记可回收对象
- 优点: 回收效率高:
- 缺点: 内存碎片-无效占用内存,内存利用率不高 启动major GC效率不高,虽然回收效率高,但是不适合频繁的垃圾回收
- 适合于:删除操作比较少的情况
- 复制算法
- 先标记,在内存中申请一个一模一样大的空间,将有效元素按照连续的顺序复制到新空间,接着,将原空间清除
- 优点: 解决了标记清除法中地址不连续的问题,提高内存的利用率
- 缺点: 相对于标记清除法来说,效率略有下降,空间需要二倍消耗
- 适合于:单次删除对象数量比较多的情况
- 标记压缩法
- 先标记,将没有标记的放到一起,被标记的放到一边,回收时,将所有已标记的回收
- 优点:解决了复制算法内存二倍消耗的问题,同时也解决了标记清除法内存利用率不高的问题
- 缺点:效率略低
- 适合于单次删除元素较少的情况
- 分代算法(年轻代 老年代)
- 空间配比 年轻代 : 老年代 = 1 : 2
- 年轻代 YOUNG GENERATION
- eden
- survial(s0[from] s1[to])[幸存者区]
- 空间配比 eden : s0 : s1 = 8 : 1 : 1
- 使用复制算法
- 老年代 OLD GENERATION
- 任何一个空间,在占满后都会产生一次垃圾回收,年轻代GC用的是minor GC(轻GC),老年代用major(FULL GC) STW(STOP THE WORLD)
- 使用标记压缩算法
- 默认判断
- 新生代存活周期大于15
- 动态年龄代判断
- 如果同年龄代的对象的总容量,如果超过了s0或s1的百分之五十,则全部进入老年代
- 追踪虚拟机工具 jvisualvm