【1】JVM的内存模型
HotSpot虚拟机中将虚拟机栈和本地方法栈合二为一了。
线程私有区域:虚拟机栈、本地方法栈、程序计数器
线程共享区域:堆、方法区
HotSpot虚拟机内存模型:
程序计数器:用于记录当前执行字节码指令的地址。如果执行的native方法,则为null。
虚拟机栈:每个方法在执行的时候也会创建一个栈帧,存储了局部变量,操作数,方法返回地址。每个方法从调用到执行完毕,对应一个栈帧在虚拟机栈中的入栈和出栈。通常所说的栈,一般是指在虚拟机栈中的局部变量部分。如果线程请求的栈深度大于虚拟机所允许的深度,则StackOverflowError。如果虚拟机栈可以动态扩展,扩展到无法申请足够的内存,则OutOfMemoryError。
本地方法栈:和虚拟机栈类似,主要为虚拟机使用到的Native方法服务。也会抛出StackOverflowError和OutOfMemoryError。
堆:在虚拟机启动的时候创建,用于存放对象实例以及数组。当堆中没有内存可分配给实例,也无法再扩展时,则抛出OutOfMemoryError。
方法区:用于存储已经被虚拟机加载的常量,类信息,静态元素等。
【3】JVM内存
JVM内存可以划分为堆内存和非堆内存。堆内存可以细分为年轻代、老年代。
年轻代组成:Eden、From Survivor、To Survivor。年轻代内存比例为8:1:1。
老年代组成:Tentired。
非堆内存就是一个永久代。在JDK1.8版本废弃了永久代,替代的是元空间(MetaSpace),元空间与永久代类似,都是方法区的实现,他们最大区别是:元空间并不在JVM中,而是使用本地内存。
详细JVM内存图:
【4】堆内存和非堆内存的作用
堆内存的作用就是存放对象和数组。
非堆内存的作用就是存放程序运行过程中长期存在的对象,比如类信息、静态元素、常量。
【5】垃圾回收算法有哪些
1.标记-清除算法
2.复制算法
3.标记-整理算法
4.分代收集算法
该算法分为标记-清除算法
“标记”和“清除”阶段:首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象。它是最基础的收集算法,后续的算法都是对其不足进行改进得到。这种垃圾收集算法会带来两个明显的问题:
1、效率问题
2、空间问题(标记清除后会产生大量不连续的内存碎片)
复制算法
为了解决“标记-清除”算法的效率问题,“复制”收集算法出现了。它可以将内存分为大小相同的两块,每次使用其中的一块。当这一块的内存使用完后,就将还存活的对象复制到另一块去,然后再把使用的空间一次清理掉。这样就使每次的内存回收都是对内存区间的一半进行回收。
不足之处:
1、需要消耗一般的堆内存空间,内存利用率不高。
标记-整理算法
为了解决“标记-清除”算法的空间问题,提出了“标记-整理”算法,标记过程仍然与“标记-清除
”算法一样,但后续步骤不是直接对可回收对象回收,而是让所有存活的对象向一端移动,然后直接清理掉端边界以外的内存。
分代收集算法
当前虚拟机的垃圾收集都采用分代收集算法,这种算法没有什么新的思想,只是根据对象存活周期的不同将内存分为几块。一般将java堆分为新生代和老年代,这样我们就可以根据各个年代的特点选择合适的垃圾收集算法。
比如在新生代中,每次收集都会有大量对象死去,所以可以选择复制算法,只需要付出少量对象的复制成本就可以完成每次垃圾收集。而老年代的对象存活几率是比较高的,而且没有额外的空间对它进行分配担保,所以我们必须选择
“标记-清除”或“标记-整理”算法进行垃圾收集。
【7】HotSpot为什么要分为新生代和老年代?
根据上面的对分代收集算法的介绍回答。
【8】MinorGC、FullGC什么时候触发?
MinorGC触发(新生代GC,包含所有在新生代工作的收集器):
1.当Eden内存满了,需要将存活对象复制到S0内存中时。
2.当需要将Eden和S0内存中的存活对象复制到S1内存中时。
FullGC触发(老年代GC,包含所有在老年代工作的收集器):
1.当老年代空间不足。
2.当持久带空间不足。
3.堆中分配很大对象时。
4.程序显示调用System.gc()方法。
【9】什么是垃圾
内存中已经不再被使用到的空间就是垃圾
【10】要进行垃圾回收,如何判断一个对象是否可以被回收?
1.引用计数法
很难解决对象之间的循环引用问题
2.枚举根节点做可达性分析
通过一系列名为“GC Roots”的对象作为起始点,从“GC Roots”对象开始向下搜索,如果一个对象到“GC Roots”没有任何引用链相连,说明此对象可以被回收。
【11】哪些对象可以作为GC Roots的对象:
1.虚拟机栈中局部变量(也叫局部变量表)中引用的对象
2.方法区中类的静态变量、常量引用的对象
3.本地方法栈中JNI (Native方法)引用的对象