一、JVM内存区域八股文
1、介绍下JVM的内存区域
JVM的内存区域分为线程私有和线程共享的,线程私有的是程序计数器、虚拟方法栈、本地方法栈,线程共享的是堆、方法区和直接内存
程序计数器的作用是指示下一条需要执行的字节码指令
虚拟方法栈的作用是用来运行java方法,虚拟方法栈里有局部变量表,操作数栈,返回地址和动态连接
本地方法栈的作用是用来运行native方法
堆的作用是用来存放对象实例,堆分了三个区域,eden区,survivor区和老年代区,比例为6:3:1,survivor区又细分了from区和to区
方法区的作用是用来存放静态变量,常量,类信息,方法区里有个字符串常量池,不过方法区只是个概念,具体实现在jdk1.8之前是永久代,jdk1.8是元空间,元空间在本地内存里
jdk1.8永久区移出了堆外变成了元空间,保留在堆内的是常量池和StringTable
直接内存不受jvm管理,直接由操作系统管理
2、Java 对象的创建过程
类加载检查:检查类是否被加载了,是否被解析,是否被初始化了
分配内存:给要创建的对象分配内存,分配方式有指针碰撞和空闲列表两种,分配内存会有线程安全问题,使用tlab和cas解决
初始化零值:给字段赋初始值
设置对象头:对象头有是哪个类的实例,gc分代信息,hashcode,以及锁相关的一些信息
执行初始化方法:代码里的构造函数
3、字符串相关
使用双引号创建的字符串对象" "会放在字符串常量池里
使用new关键字创建的字符串对象会先在堆创建一个对象,然后再在字符串常量池里创建一个
intern方法会把堆的字符串对象放在字符串常量池里
二、JVM内存回收八股文
1、如何判断对象是否死亡(两种方法)
引用计数法
随着GCRoot找不到这个对象的引用
可以成为GCRoot的对象,虚拟方法栈和本地方法栈的对象,方法静态属性,常量引用的对象,被同步锁持有的对象
2、如何判断一个常量是废弃常量
例如字符串常量没有被引用到就是废弃常量
3、如何判断一个类是无用的类
这个类的所有实例被回收,这个类的类加载器被回收,这个类的class对象被回收
4、垃圾收集有哪些算法,各自的特点?
标记清除算法:
把需要回收的对象标记出来,然后再进行回收,会导致内存碎片
标记整理算法:
把不需要回收的对象整理到一边,然后再把需要回收的对象回收掉,不会导致内存碎片不过效率低点
标记复制算法
把内存区域分成两片大小一样的区域,垃圾回收时把不用回收的对象回收复制到另一片区域,然后在把原来的区域回收
5、HotSpot 为什么要分为新生代和老年代?
因为对象有大有小,有些小对象朝生息灭,回收时间短,大对象就存活很长,回收时间长,回收的时候用户线程是要停顿的,所以分成两个区域可以避免回收大对象对小对象造成了不必要的停顿
6、常见的垃圾回收器有哪些?
serial垃圾收集器、Parallel收集器、cms收集器
7、介绍一下 CMS,G1 收集器。
cms不分区,回收过程是初次标记标记了gcroot对象,并发标记标记了从gcroot出发找到的对象,最终标记标记并发标记阶段被修改的引用、并发清除,使用标记清除回收算法
g1分区,会优先回收价值大的区域,使用分代回收和标记整理回收算法
8、 Minor Gc 和 Full GC 有什么不同呢
minor gc回收新生代的对象
Full GC回收老年代的对象
9、简单的介绍一下强引用、软引用、弱引用、虚引用(虚引用与软引用和弱引用的区别、使用软引用能带来的好处)。
强引用:内存不足不会回收报OutOfMemory异常
软引用:内存不足时会回收
弱引用:发生gc时就会回收
虚引用:形同虚设,只是用来跟踪对象是否被回收了
三、类加载过程
类加载的过程分为加载,验证,准备,解析,初始化,使用,卸载
加载:根据全类名获取二进制字节流,把静态数据区转化成运行时数据区,生成class对象作为方法区访问数据的入口
验证:验证class文件格式是否合法,验证元数据,验证字节码,验证符号引用
准备:给类常量分配内存并赋初始零值
解析:把符号引用解析成直接引用,目的是得到类、方法、字段的指针
初始化:执行构造函数
卸载:使用完这个类就可以进行卸载,实例被回收,类加载器实例被回收,没有对象引用实例
四、双亲委派模型
双亲委派模型就是先看上级加载器是否加载了这个类,上级加载器没有加载这个类才会自己加载,
Bootstrap加载器是最*的加载器,加载lib目录的类
Extension加载器是第二的加载器,加载lib/ext目录的类
Application加载器是第三的加载器,加载classpath目录的类
双亲委派模型的作用是避免类被重复加载,因为一个类和一个加载器才能指定一个类,避免了jdk的类被重复加载