JVM简介
JVM概念
1)JVM(Java Virtual Machine。意为Java虚拟机)
虚拟机:指通过软件模拟的具有完整硬件功能的、运行在一个完全隔离的环境中的完整计算机系统
常见的虚拟机:JVM、VMware、Virtual、Virtual Box。JVM是被裁减过的虚拟机,执行字节码指令集的软件
Java代码——Java进程——JVM之间的关系:
- Java代码经过Javac编译生成class字节码
- Java虚拟机中:解释器+jit即时编译器 解释翻译字节码,得到可执行文件(Java进程)
- Java进程启动时,就会创建一个Java虚拟机,解释执行字节码指令,还存在JIT即时编译器,最终是申请系统调度CPU执行机器码
类加载:如果一个类没有发生类加载,要先进行类加载
类加载的时期:
(1)new 对象
(2)读、写静态变量
(3)调用静态方法
(4)父类的加载:子类初始化加载,如果父类还没有类加载,则先执行父类的类加载
(5)主函数类:Java类启动入口类
类加载做的事:
(1)把字节码(二进制数据)加载到Java进程的方法区(类型的信息,方法信息等等)
(2)在堆中,生成一个class类对象。
运行时数据区域
线程私有区域:
- 程序计数器:当前线程正在执行的字节码的行号指示器
- Java虚拟机栈:每个方法执行时会创建一个栈帧用于存储局部变量表、操作数栈、动态链接、方法出口等信息。
局部变量表存放各种基本数据类型(8大数据类型)、对象引用。 - 本地方法栈:本地方法栈为虚拟机使用的native方法服务。
线程共享区域:
- Java堆:存放对象实例以及数组
- 方法区:存储已经被虚拟机加载的类信息、常量、静态变量、即时编译器后的代码等数据。
- 运行时常量池(方法区的一部分):存放字面常量与符号引用
字面常量:字符串、final常量、基本数据类型的值
符号引用:类和结构的完全限定名,字段的名称和描述符、方法的名称和描述符
内存溢出
创建变量/对象/类加载,需要先在对应的内存区域,分配一块内存空间,如果该区域内存空间不足,需要在堆或者方法区执行GC(垃圾回收),如果执行GC以后内存还不足,就会出现内存溢出(OOM)
结果:严重的OOM会使整个Java进程挂掉
解决方案:
(1)优化代码(空间复杂度):复用变量等
(2)Java进程启动时,加大对应区域对应的内存空间
(3)如果系统内存不满足内存的扩大,还可以加大系统内存
内存泄漏
内存中,随着进程运行的时间越来越长,存放的无用数据(变量/常量值,对象,类型)越来越多,可用的内存空间越来越少
结果:进程一直运行,有用户一直使用系统,随着使用时间的增长,最终一定会出现某个内存区域空间不足的问题(OOM)
解决:
(1)程序代码上优化,如设置超时时间,定时清理不用的数据(可以使用JVM检测工具)
(2)临时方案:定时重启,加内存
垃圾回收
Garbage Collection(GC),Java进程在启动后会创建垃圾回收线程,来对内存中无用的对象进行回收。
什么是垃圾?
无用的对象(堆),常量(常量池)。类型(方法区的类信息)。Java进程启动——创建GC线程,垃圾回收器回收垃圾。
(1)基于垃圾回收算法来执行回收工作
(2)不同的垃圾回收器,使用的算法不同
(3)回收方法区、堆中的垃圾
什么时候回收垃圾?
创建对象/常量/类加载,先需要在某个区域分配内存空间,如果空间不足,则执行GC,如果GC后还不够,就是OOM,执行其他的垃圾回收机制
判定垃圾回收
(1)引用计数算法:有增加 某个对象的引用,该对象的计数器+1,减少引用则-1
缺陷:无法解决循环引用的问题。
(2)可达性分析算法:通过一系列的称为“GC Root”的对象作为起始点,从这些节点从上往下搜索,搜索走过的路径称为“引用链”,当一个对象GC Root没有任何引用链相连时(到GC不可达)时,证明对象不可用。
可作为GC Root的对象包含下面几种:
(1)虚拟机栈(栈帧中的引用变量表)中引用的对象
(2)方法区中静态属性引用的对象
(3)方法区中常量引用的对象
(4)本地方法栈中JN(Native方法)引用的对象
回收的内存区域
-
回收方法区
Java1.7叫方法区(Method Area),Java1.8称为元空间,GC 中称为永久代。
回收的内容:废弃常量和无用的类,虽然叫永久代,但是并不是不执行GC,而是执行的频率和效率都比较低。 -
回收堆
GC 中把堆也称为堆
垃圾回收算法 -
标记-清除算法(老年代的回收算法)
分为两个阶段
(1)标记:标记无用对象(不可达)
(2)清楚:清楚标记垃圾
缺陷:
(1)效率不高
(2)内存碎片问题:存在内存碎片时GC后,再次创建大对象时需要连续的内存空空间,整个内存空间是够的,只是连续的内存空间不够,提前触发一下次的GC -
复制算法(新生代的回收算法)
内存分为两个一样大小的空间,每次只使用其中一块(创建对象),gc时,存活的对象复制到另一个空的内存空间
优点:
(1)新生代的对象,大部分都是朝生夕死(创建出来之后,很快就不可达),复制存活的对象,性能很高(存活对象很少),方法栈帧出栈(方法返回),堆中的对象就是不可达
(2)没有内存碎片的问题
缺陷:内存利用率不高,50%
-
标记整理算法(老年代回收)
先标记,标记存活对象,让存活对象都想一端移动,然后直接清理掉端边以外的内存
优点:没有内存碎片问题 -
分代收集算法
属于一种算法思想,没有算法实现。把堆划分为好几块。新生代:Eden区、S0、S1,使用复制算法的升级版;老年代:使用标记清除算法或者标记整理算法。
对于GC堆中,新生代GC又称为Young GC,minor GC (对用户线程影响较小),对象的存活率较低,回收的频率非常高,效率也很高。老年代GC ,又称Old GC ,major GC(指对用户线程影响较大),回收速度要比新生代GC慢10 以上。
复制算法的优化方案:提高新生代的内存利用率。
将新生代内存分为一块较大的Eden空间和较小的Survivor(幸存区),每一次使用Eden和其中的一块survivor(两个survivor区域一个称为From区,一个称为To区)。HotSpot默认Eden区和Survivor区的大小比例是8:1。也就是说Eden:Survivor From : Survivor To = 8:1:1。
所以每次新生代可用内存空间为整个新生代容量的90%,而剩下的10%用来存放回收后存活的对象。