JVM
JVM简介
虚拟机:通过软件模拟的具有完整硬件功能的、运行在一个完全隔离环境中的完整的计算机系统。
JVM:通过软件模拟Java字节码的指令集,JVM中只保留了PC寄存器
内存区域与内存溢出异常
1.运行时数据区域
线程私有区域
程序计数器、Java虚拟机栈、本地方法栈
线程私有:生命周期与具体线程相同,随着线程的创建而创建,随着线程销毁,对应空间回收
线程共享区域
java堆、方法区、运行时常量池
1.1程序计数器
记录程序当前执行地址
如果当前线程执行一个java方法,程序计数器记录正在执行的虚拟机字节码指令的地址;
如果当前线程执行一个native方法,计数器值为空;
1.2Java虚拟机栈
java方法执行的内存模型,例如:方法执行->入栈(局部变量表相当于虚拟机栈的一部分)
每一个方法执行时都会创建一个栈帧用于存储局部变量表、操作数栈、动态链接、方法出口等信息,每一个方法从调用到执行完成的过程中,对应一个栈帧在虚拟机栈中出栈入栈的过程;
局部变量表:8大基本数据类型、对象引用;局部变量表所需的内存在编译期间完成
产生两大异常:
(1)线程请求的栈深度大于虚拟机所允许的深度(-Xss设置栈容量),抛出StackOverFlowError异常
(2)虚拟机在动态扩展时无法申请到足够的内存,抛出OutOfMemoryError(OOM)异常
1.3本地方法栈:
为虚拟机使用的native方法服务;
在HotSpot虚拟机中,本地方法栈和虚拟机栈是同一块内存区域
1.4Java堆
存放内容:对象实例
所有对象的实例以及数组都要在堆上分配
Java堆是垃圾回收器管理的主要区域,也可称“GC”堆;
Java堆可以处于物理上不连续的内存空间中;
Java堆在主流的虚拟机上是可扩展的(-Xmx设置最大值,-Xms设置最小值)
产生异常:
如果在堆中没有足够的内存完成实例分配并且堆也无法再拓展时,抛出OOM异常
1.5方法区
存储内容:已被虚拟机加载的类信息、常量、静态变量、即时编译器编译的代码等数据
产生异常:
当方法区无法满足内存分配需求时,抛出OOM异常
1.6运行时常量池
方法区的一部分
存储内容:字面量、符号引用
字面量:字符串(JDK1.7后移动至堆中)、final常量、基本数据类型的值
符号引用:类和结构的完全限定名、字段的名称和描述符、方法的名称和描述符
2.Java堆溢出
2.1产生异常
即内存溢出异常(OOM),三个条件全部满足,产生该异常
(1)不断创建对象(2)保证GC Roots到对象之间有可达路径(3)对象数量达到最大堆容量
2.2异常处理分析
(1)java堆内存溢出,异常信息提示(Java heap space)
(2)判断是内存泄漏还是内存溢出
内存泄漏:泄露对象无法被GC
内存溢出:内存对象确实还应该活着
1.调大内存;2.检查对象的生命周期是否过长
3.虚拟机栈和本地方法栈溢出
虚拟机栈的两大异常
(1)线程请求的栈深度大于虚拟机所允许的深度(-Xss设置栈容量),抛出StackOverFlowError异常
(2)虚拟机在动态扩展时无法申请到足够的内存,抛出OutOfMemoryError(OOM)异常
如果因为多线程导致内存溢出问题,在不减少线程数的情况下,只能减少最大堆和减少栈容量的方法来换取更多线程
垃圾回收器与内存分配策略
1.判断对象已"死"
Java堆中的所有对象实例,垃圾回收器对堆进行垃圾回收之前。首先会判断对象是否存活
1.1引用计数法
使用分析:给对象加一个引用计数器,引用一次,计数器+1,当引用失效时,计数器-1;任何时刻计数器为0的对象已"死"
不足:在主流的JVM中没有选用引用计数器法来管理内存,最主要的原因是引用计数器法无法解决对象的循环引用问题
循环引用栗子:
public class Main{
public Object instance = null;
public static void testGC(){
Main testA = new Main();
Main testB = new Main();
//两个对象互相引用
testA.instance = testB;
testB.instance = testA;
testA = null;
testB = null;
System.gc();
}
public static void main(String[] args) {
testGC();
}
}
1.2可达性分析算法
核心思想:以"GC Roots"对象作为起始点,从这些节点开始向下搜索,搜索走过的路径称为"引用链",当一个对象到GC Roots没有任何的引用链相连(从GC Roots到这个对象不可达)时,证明此对象是不可达的;
GC Roots对象包含:
(1)虚拟机栈(栈帧中的本地变量表)中引用的对象;
(2)方法区中类静态属性引用的对象;
(3)方法区中常量引用的对象;
(4)本地方法栈中(Native方法)引用的对象;
引用的扩充:
(1)强引用:new出来的,只要JVM中存在任何一个强引用,即便内存不够用,也无法回收此对象
ÿ