JVM是什么
JVM是java程序的运行环境,虚拟机直接和操作系统进行交互,与硬件不直接交互,而操作系统可以帮我们完成和硬件进行交互的工作
什么是程序计数器
线程私有的,每个线程一份,内部保存字节码的行号。用于记录正在执行的字节码指令的地址。
详细介绍Java堆
java堆是一个线程共享的区域,主要用来保存对象实例,数组等,内存不够则抛出内存溢出异常,由年轻代和老年代组成
年轻代:划分为3各部分,eden区和两个大小严格的survivor区
老年代:主要保存生命周期长的对象,一般是一些老的对象
堆在jdk1.7与jdk1.8的区别:jdk1.7中有一个永久代,存储的是类信息、静态变量、常量、编译后的代码;jdk1.8中移除了永久代,把数据存储到了本地内存的元空间中,防止内存溢出
什么是虚拟机栈
1.每个线程运行时所需要的内存称为虚拟机栈,先进后出
2.每个栈由多个栈帧组成,对应着每次方法调用时所占用的内存
3.每个线程只能有一个活动栈帧,对应着当前正在执行的那个方法
垃圾回收是否涉及栈内存
垃圾回收主要指堆内存,当栈帧弹栈之后,内存就会释放
栈内存分配越大越好吗
未必,默认的栈内存通常为1024k
栈帧过大会使线程数变少,例如,机器的总内存为512M,目前能活动的线程数则为512个,若将栈内存改为2048k,那么能活动的栈帧就会减半
方法内的局部变量是否线程安全
若方法内的局部变量没有逃离方法的作用范围,那么他就是线程安全的
若是局部变量引用了对象,并逃离方法的作用范围,则需要考虑线程安全
栈内存溢出问题
1.栈帧过多导致栈内存溢出,典型问题时递归调用
2.栈帧过大导致栈内存溢出
堆栈的区别是什么
1.栈内存一般会用来存储局部变量和方法调用,但堆内存是用来存储java对象和数组的。堆会GC垃圾回收,栈不会
2.栈内存是线程私有的,堆内存时线程共有的
3.两者异常错误不同,但是如果堆内存或栈内存不足都会抛出异常
栈空间不足:java.Lang.StackOverFlowError
堆空间不足:java.Lang.OutOfMemoryError
方法区
解释一下方法区:
1.方法区是各个线程共享的内存区域
2.主要存储类的信息、运行时常量池
3.虚拟机启动时创建,关闭虚拟机时释放
4.若方法区中内存无法满足分配请求,则会抛出
OutOfMemoryError:MetaSpace异常
常量池:可以看作是一张表,虚拟机指令根据这张常量表找到要执行的类名、方法名、参数类型、字面量等信息
运行时常量池:常量池是*.class文件中的,当该类被加载时,它的常量池信息就会放入运行时常量池,并把里面的符号地址变为真实地址
直接内存:并不属于JVM中的内存结构,不由JVM进行管理。是虚拟机的系统内存,常见于NIO操作时,用于数据缓冲区,它分配回收成本较高,但读写性能高,不受JVM内存回收管理
类加载器
JVM只会运行二进制文件,类加载器的作用就是将字节码文件加载到JVM中,从而让Java程序能够启动起来
类加载器的类型:启动类加载器;扩展类加载器;应用类加载器;自定义类加载器
双亲委派机制:
加载某一个类,先委托上一级的加载器进行加载,如果上级加载器也有上级,则会继续向上委托,如果该类委托上级没有被加载,子加载器尝试加载该类
JVM为什么采用双亲委派机制
1.通过双亲委派机制可以避免某一个类被重复加载,当父类已经加载后则无需重复加载以保证唯一性
2.为了安全,保证类库API不会被修改
类的加载过程
加载—-验证—-准备—-解析—-初始化—-使用—-卸载
加载:
1.通过类的全名,获取类的二进制数据流
2.解析类的二进制数据流为方法区的数据结构
3.创建java.lang.class类的实例,表示该类型作为方法区这个类的各种数据的访问入口
验证:验证类是否符合JVM规范,安全性检查
准备:为类变量分配内存并设置类变量初始值
static final 基本类型 赋值在准备阶段 static final 引用类型 赋值在初始化阶段
解析:把类中的符号引用转化为直接引用
方法中调用了其他方法,方法名可以理解为符号引用,而直接引用就是使用指针直接指向方法
初始化:对类的静态变量,静态代码块执行初始化操作
1.如果初始化一个类的时候,其父类尚未初始化,则优先初始化其父类
2.如果同时包含多个静态变量和静态代码块,则按照自上而下的顺序依次执行
使用:JVM开始从入口开始执行用户的程序代码
1.调用静态成员信息(如:静态字段、静态方法)
2.使用new关键字为其创建对象实例
卸载:当用户程序代码执行完毕后,JVM便开始销毁创建的class对象
垃圾回收机制
对象什么时候可以被垃圾器回收:如果一个或多个对象没有任何的引用指向它了,那么这个对象现在就是垃圾,如果定位了垃圾,则有可能会被垃圾回收器回收
定位垃圾的方法:
1.引用计数法:当对象间出现循环引用时,引用计数法会失效
2.可达性分析算法:扫描堆中的对象,看是否能够沿着GCRoot对象为起点的引用链找到该对象,找不到表示可以回收
JVM垃圾回收算法有哪些
1.标记清除算法:
垃圾回收分为两个阶段,分别是标记和清除,效率高有磁盘碎片,内存不连续
2.标记整理算法:
前面部分与标记清除算法一样,将存活对象都向内存另一端移动,然后清理边界以外的垃圾、无碎片,对象需要移动,效率低
3.复制算法:
将原有的内存空间一分为二,每次只使用其中的一块,正在使用的对象复制到另一个内存空间中,然后将该内存空间清空,交换两个内存的角色,完成垃圾的回收;无碎片,内存使用率低
JVM中的分代回收
堆的区域划分:
1.堆被分为了两份:新生代和老年代(1:2)
2.对于新生代,内部又被分为了三个区域
Eden区、幸存者区和survivor区(分为from与to)
对象回收分代回收策略
1.新创建的对象都会被分配到eden区
2.当eden区内存不足时,标记eden与from中的存活对象
3.将存活对象采用复制算法复制到to中,复制完毕后,eden区和from内存都得到释放
4.经过一段时间后eden区的内存又出现不足,标记eden区to中的存活对象,将其复制到from区
5.当幸存者区对象熬过几次回收(最多15次),晋升至老年代(幸存者区内存不足或大对象会提前回收)
MinorGC、MixedGC、FullGC的区别是什么
1.MinorGC发生在新生代的垃圾回收、暂停时间短
2.MixedGC新生代+老年代部分区域的垃圾回收,G1垃圾回收器特有的
3.FullGC新生代+老年代完整的垃圾回收,暂停时间长,应尽力避免
STW指的是暂停所有应用程序线程,等待垃圾回收的完成
JVM中有哪些垃圾回收器
1.串行垃圾回收器:Serial GC,Serial Old GC
2.并行垃圾回收器:Parallel Old GC
3.CMS并发垃圾回收器:CMS GC老年代使用
4.重点G1垃圾回收器,作用在新生代和老年代
聊一下G1垃圾回收器:
1.应用于新生代和老年代,在jdk9之后默认使用G1
2.划分为多个区域,每个区域都可以充当eden、survivor,old、humongous,其中humongous专为大对象准备
3.采用复制算法
4.响应时间与吞吐量兼顾
5.分为三个阶段:新生代回收(STW)、并发标记(重新标记STW)、混合收集
6.如果并发失败(即回收速度赶不上创建新对象的速度,就会触发Full GC)
强引用、软引用、弱引用、虚引用的区别
强引用:只要所有GC Root能找到就不会被回收
软引用:需要配合soft Reference使用,当垃圾多次回收,内存依然不够时会回收软引用对象
弱引用:需要配合Weak Reference使用,只要进行了垃圾回收,就会把弱引用对象回收
虚引用:必须配合引用队列使用,被引用对象回收时,会将虚引用入队,由Reference Handler线程调用虚引用的相关方法释放直接内存