目录
GC 的三种收集方法:标记清除、标记整理、复制算法的原理与特点,分别用在什么地方,如果让你优化收集方法,有什么思路?
内存模型以及分区,需要详细到每个区放什么
- 方法区:主要是存储类信息,常量池(static 常量和 static 变量),编译后的代码(字节码)等数据
- 堆:初始化的对象,成员变量 (那种非 static 的变量),所有的对象实例和数组都要在堆上分配
- 栈:栈的结构是栈帧组成的,调用一个方法就压入一帧,帧上面存储局部变量表,操作数栈,方法出口等信息,局部变量表存放的是 8 大基础类型加上一个应用类型,所以还是一个指向地址的指针
- 本地方法栈:主要为 Native 方法服务
- 程序计数器:记录当前线程执行的行号
Java 中会存在内存泄漏吗,请简单描述。
垃圾回收器的基本原理是什么?垃圾回收器可以马上回收内存吗?
有什么办法主动通知虚拟机进行垃圾回收?
可以。程序员可以手动执行 System.gc(),通知 GC 运行,但是 Java 语言规范并不 保证 GC 一定会执行。
System.gc() 和 Runtime.gc() 会做什么事情?
如果对象的引用被置为 null,垃圾收集器是否会立即释放对象占
用的内存?
不会,在下一个垃圾回收周期中,这个对象将是可被回收的。
在 Java 中,对象什么时候可以被垃圾回收?
GC 的两种判定方法
引用计数法:指的是如果某个地方引用了这个对象就+1,如果失效了就-1,当为 0 就会回收但是 JVM 没有用这种方式,因为无法判定相互循环引用(A 引用 B,B 引用 A)的情况引用链法: 通过一种 GC ROOT 的对象(方法区中静态变量引用的对象等-static 变量)来判断,如果有一条链能够到达 GC ROOT 就说明,不能到达 GC ROOT 就说明可以回收
SafePoint 是什么
- 循环的末尾 (防止大循环的时候一直不进入 safepoint,而其他线程在等待它进入
- safepoint)
- 方法返回前
- 调用方法的 call 之后
- 抛出异常的位置
GC 的三种收集方法:标记清除、标记整理、复制算法的原理与特点,分别用在什么地方,如果让你优化收集方法,有什么思路?
特点:
- 效率问题,标记和清除的效率都不高;
- 空间的问题,标记清除以后会产生大量不连续的空间碎片,空间碎片太多可能会导致程序运行过程需要分配较大的对象时候,无法找到足够连续内存而不得不提前触发一次垃圾收集。
适用场景 :适合在老年代进行垃圾回收,比如CMS收集器就是采用该算法进行回收的。
复制算法
原理:它先将可用的内存按容量划分为大小相同的两块,每次只是用其中的一块。当这块内存用完了,就将还存活着的对象复制到另一块上面,然后把已经使用过的内存空间一次清理掉。
特点:没有内存碎片,只要移动堆顶指针,按顺序分配内存即可。代价是将内存缩小位原来的一半。
地方:适合新生代区进行垃圾回收。serial new,parallel new和parallel scanvage收集器,就是采用该算法进行回收的。
标记整理
原理:分为标记和整理两个阶段:首先标记出所有需要回收的对象,让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。
特点:不会产生空间碎片,但是整理会花一定的时间。
地方:适合老年代进行垃圾收集,parallel Old(针对parallel scanvange gc的) gc和Serial old收集器就是采用该算法进行回收的。
优化收集方法的思路
分代收集算法
原理:根据对象存活的周期的不同将内存划分为几块,然后再选择合适的收集算法。
一般是把java堆分成新生代和老年代,这样就可以根据各个年待的特点采用最适合的收集算法。在新生代中,每次垃圾收集都会有大量的对象死去,只有少量存活,所以选用复制算法。老年代因为对象存活率高,没有额外空间对他进行分配担保,所以一般采用标记整理或者标记清除算法进行回收。
GC 收集器有哪些?
- 并行收集器:串行收集器使用一个单独的线程进行收集,GC 时服务有停顿时间
- 串行收集器:次要回收中使用多线程来执行
CMS 收集器与 G1 收集器的特点
- CMS 收集器是基于“标记—清除”算法实现的,经过多次标记才会被清除
- G1 从整体来看是基于“标记—整理”算法实现的收集器,从局部(两个 Region 之间)上来看是基于“复制”算法实现的
Minor GC 与 Full GC 分别在什么时候发生?
如和判断一个对象是否存活?(或者 GC 对象的判定方
法)
引用计数法
所谓引用计数法就是给每一个对象设置一个引用计数器,每当有一个地方引用这个对象时,就将计数器加一,引用失效时,计数器就减一。当一个对象的引用计数器为零时,说明此对象没有被引用,也就是“死对象”,将会被垃圾回收.引用计数法有一个缺陷就是无法解决循环引用问题,也就是说当对象 A 引用对象 B,对象B 又引用者对象 A,那么此时 A,B 对象的引用计数器都不为零,也就造成无法完成垃圾回收,所以主流的虚拟机都没有采用这种算法。
可达性算法(引用链法)
该算法的思想是:从一个被称为 GC Roots 的对象开始向下搜索,如果一个对象到 GCRoots 没有任何引用链相连时,则说明此对象不可用。在 java 中可以作为 GC Roots 的对象有以下几种:
- 虚拟机栈中引用的对象
- 方法区类静态属性引用的对象
- 方法区常量池引用的对象
- 本地方法栈 JNI 引用的对象
虽然这些算法可以判定一个对象是否能被回收,但是当满足上述条件时,一个对象比 不一定会被回收。当一个对象不可达 GC Root 时,这个对象并不会立马被回收,而是出于一个死缓的阶段,若要被真正的回收需要经历两次标记如果对象在可达性分析中没有与 GC Root 的引用链,那么此时就会被第一次标记并且进行一次筛选,筛选的条件是是否有必要执行 finalize()方法。当对象没有覆盖 finalize()方法或者已被虚拟机调用过,那么就认为是没必要的。如果该对象有必要执行 finalize()方法,那么这个对象将会放在一个称为 F-Queue 的对队列中,虚拟机会触发一个 Finalize()线程去执行,此线程是低优先级的,并且虚拟机不会承诺一直等待它运行完,这是因为如果 finalize()执行缓慢或者发生了死锁,那么就会造成 FQueue 队列一直等待,造成了内存回收系统的崩溃。GC 对处于 F-Queue 中的对象进行第二次被标记,这时,该对象将被移除”即将回收”集合,等待回收。
简述 java 垃圾回收机制?
java内存模型
简述类加载的几个过程
java 类加载过程
加载时类加载的第一个过程,在这个阶段,将完成一下三件事情:1. 通过一个类的全限定名获取该类的二进制流。2. 将该二进制流中的静态存储结构转化为方法去运行时数据结构。3. 在内存中生成该类的 Class 对象,作为该类的数据访问入口。
验证的目的是为了确保 Class 文件的字节流中的信息不回危害到虚拟机.在该阶段主要完成以下四钟验证:1. 文件格式验证:验证字节流是否符合 Class 文件的规范,如主次版本号是否在当前虚拟机范围内,常量池中的常量是否有不被支持的类型.2. 元数据验证:对字节码描述的信息进行语义分析,如这个类是否有父类,是否集成了不被继承的类等。3. 字节码验证:是整个验证过程中最复杂的一个阶段,通过验证数据流和控制流的分析,确定程序语义是否正确,主要针对方法体的验证。如:方法中的类型转换是否正确,跳转指令是否正确等。4. 符号引用验证:这个动作在后面的解析过程中发生,主要是为了确保解析动作能正确执行。
准备阶段是为类的静态变量分配内存并将其初始化为默认值,这些内存都将在方法区中进行分配。准备阶段不分配类中的实例变量的内存,实例变量将会在对象实例化时随着对象一起分配在 Java 堆中。
该阶段主要完成符号引用到直接引用的转换动作。解析动作并不一定在初始化动作完成之前,也有可能在初始化之后。
初始化时类加载的最后一步,前面的类加载过程,除了在加载阶段用户应用程序可以通过自定义类加载器参与之外,其余动作完全由虚拟机主导和控制。到了初始化阶段,才真正开始执行类中定义的 Java 程序代码。