一 内存划分
1 finanlize() 方法工作原理
一个对象被回收要经过2次标记。如果对象没有执行过finalize方法且覆盖了finalize方法,则会进行第一次标记存放到F-Queue队列中;第二次标记是执行finalize()方法时,如果在方法中对象重新与类引用或者方法引用关联上,则可以逃离这次回收。
2 jdk1.8内存分代改进
永久代用元空间来替代
3 java内存划分
程序计数器: 保存每个线程下一个指令的指针,保证线程切换回来后能接着执行, 不会出现oom
虚拟机栈: 作用于方法层级,为每个线程分配局部变量副本, 存储基础数据类型和引用, 可能出现
stackofoverError oom
本地方法栈:和虚拟机栈类似,只是针对的是native方法
方法区: 永久代,存储类的变量、常量、静态块,可能出现oom
堆:对象存储的地方,gc一般在这里活动,可能出现oom异常,分成新生代和老年代
二 内存溢出
1 分别写出堆溢出和栈溢出的程序
栈溢出:
public void f() {
f();
}
堆溢出:
public void testd() {
List<String> list = new ArrayList<>();
int i = 0;
while (true) {
list.add(new String(i + ""));
i++;
}
}
三 垃圾回收机制
1 强引用、软引用、弱引用、虚引用的简介,他们和gc的关系?
强引用:最常见的,类似 Object obj = new Object(); 只要引用还存在,gc就不会回收掉引用的对象
软引用:还有用但并非是必要的对象,在系统内存溢出之前,会把这类对象列入到回收范围,进行第二次回收
弱引用:比软引用更弱一层,被弱引用关联的对象只能生存到下一次gc之前
虚引用:最弱的一种引用关系,完全不会对关联对象的生命周期造成影响。目的是对象回收时能收到系统的通知
2 jvm垃圾回收机制,何时出发MinorGC等操作?
eden区无法为一个新对象分配内存时
3 对象如何晋升为老年代
1)大对象直接进入老年代
2)年龄达到阀值也会进入老年代,虚拟机为每个在s区的对象分配一个年龄计数器,每熬过一次gc没被回收,年龄加1,达到年龄上限就会进入老年代
3)s区中相同年龄的对象大小超过了s区的一半以上,则大于等于这个年龄的对象都会进入老年代
4)垃圾回收后,存活的对象大小超过了s区的大小,会把一部分对象担保到老年代中
4 eden区和survivor区的比例分配
jdk1.7是8:1
jdk1.8是6:1
5 垃圾回收算法
标记-清除算法: 最基础的算法,其他算法依据此算法优化
复制算法: 适用于新生代
标记-整理算法:适用于老年代,把存活的对象整理到一端,清除掉边界外的内存
分代整理: 新生代和老年代分别使用适合的算法进行回收
6 System.gc() 和 Runtime.gc() 会做什么事情?
1)java.lang.System.gc()只是java.lang.Runtime.getRuntime().gc()的简写,两者的行为没有任何不同
2)System.gc()和runtime.gc()用于提示jvm进行垃圾回收,但是否立即回收还是延迟回收由java虚拟机决定
四 类加载机制
1 Class.forName()和ClassLoader.loadClass()区别
Class.forName()涉及了类加载过程的:加载、验证、准备、解析、初始化 5个过程
ClassLoader.loadClass()只涉及了类加载过程的加载动作,后面的动作都不会执行
2 什么是类的双亲委派模型
类加载时,先用父类加载器尝试加载,一层层往上,最后会让启动类加载器尝试加载,能加载就被父类加载器加载了。如果父类加载器不能加载,才会让子类加载器加载。
双亲委派模型的目的: 保证了同一个类只能被同一个类加载器加载
3 类加载过程
加载、验证、准备、解析、初始化
加载:把class转成二进制字节流
验证:保证字节流是虚拟机需要的,有:文件格式验证、类、方法验证、符号引用验证
准备:类变量设置初始值
解析:把符号引用转成直接引用
初始化:类变量和静态块初始成预期的值
4 描述一下 JVM 加载 Class 文件的原理机制?
JVM中类的加载是有ClassLoader和他的子类来实现的,负责把Class文件加载到内存中。
类加载方式有两种:
1)隐式加载:程序运行时遇到new生成对象时,隐式调用类加载器加载对应的类到jvm中
2)显示加载:通过Class.forName()等方式,显示加载需要的类
Java类的加载是动态的,它并不会一次性将所有类全部加载后再运行,而是保证程序运行的基础类(像是基类)完全加载到jvm中,至于其他类,则在需要的时候才加载。这当然就是为了节省内存开销。