一、符号引用
1.1 符号引用
符号引用以一组符号来描述所引用的目标,符号可以是任何形式的字面量,只要使用时能够无歧义的定位到目标即可。例如,在Class文件中它以CONSTANT_Class_info、CONSTANT_Fieldref_info、CONSTANT_Methodref_info等类型的常量出现。符号引用与虚拟机的内存布局无关,引用的目标并不一定加载到内存中。在Java中,一个java类将会编译成一个class文件。在编译时,java类并不知道所引用的类的实际地址,因此只能使用符号引用来代替。比如org.simple.People类引用了org.simple.Language类,在编译时People类并不知道Language类的实际内存地址,因此只能使用符号org.simple.Language(假设是这个,当然实际中是由类似于CONSTANT_Class_info的常量来表示的)来表示Language类的地址。各种虚拟机实现的内存布局可能有所不同,但是它们能接受的符号引用都是一致的,因为符号引用的字面量形式明确定义在Java虚拟机规范的Class文件格式中。
1.2 直接引用:
直接引用可以是
(1)直接指向目标的指针(比如,指向“类型”【Class对象】、类变量、类方法的直接引用可能是指向方法区的指针)
(2)相对偏移量(比如,指向实例变量、实例方法的直接引用都是偏移量)
(3)一个能间接定位到目标的句柄
直接引用是和虚拟机的布局相关的,同一个符号引用在不同的虚拟机实例上翻译出来的直接引用一般不会相同。如果有了直接引用,那引用的目标必定已经被加载入内存中了。
1.3 符号引用替换为直接引用
在JVM中类加载过程中,在解析阶段,Java虚拟机会把类的二级制数据中的符号引用替换为直接引用。
参考:https://blog.csdn.net/wenthkim/article/details/80155817
1.4 jvm面试专题
参考: https://blog.csdn.net/wenthkim/article/details/80155817
1.5 元数据
所谓的元数据是指用来描述数据的数据,更通俗一点就是描述代码间关系,或者代码与其它资源(例如数据库表)之间内在联系得数据,
1.6 元数据的特点
第一, 元数据以标签的形式存在于Java代码中。
第二, 元数据描述的信息是类型安全的,即元数据内部的字段都是有明确类型的。
第三, 元数据需要编译器之外的工具额外的处理用来生成其它的程序部件。
第四, 元数据可以只存在于Java源代码级别,也可以存在于编译之后的Class文件内部。
参考:https://www.cnblogs.com/liuqk/articles/2115778.html
1.7 java heap 和 native memory
JVM管理的内存可以总体划分为两部分:Heap Memory和Native Memory。前者我们比较熟悉,是供Java应用程序使用的;后者也称为C-Heap,是供JVM自身进程使用的。Heap Memory及其内部各组成的大小可以通过JVM的一系列命令行参数来控制,在此不赘述。Native Memory没有相应的参数来控制大小,其大小依赖于操作系统进程的最大值(对于32位系统就是3~4G,各种系统的实现并不一样),以及生成的Java字节码大小、创建的线程数量、维持java对象的状态信息大小(用于GC)以及一些第三方的包,比如JDBC驱动使用的native内存。
参考:https://blog.csdn.net/u013721793/article/details/51204001
1.8 GC详解及Minor GC和Full GC触发条件总结
参考:https://blog.csdn.net/yhyr_ycy/article/details/52566105
二、内存分配与回收策略
2.1 对象优先在Eden分配
对象优先在eden分配,当eden没有足够的空间时,就会进行一次Minor GC
2.2 大对象直接进入老年代
(需要很大连续空间的对象)大对象所需空间的值大于-XX:PretenureSizeThreshold时,直接进入老年代
2.3 长期存活的对象将进入老年代
jvm对每个对象定义一个年龄计数器,当出生在eden的对象经过第一Minor GC,且可以被servior容纳,将被移动到servior中,且age被赋值1,每经过一次Minor GC,age的值就增加1,当age的值大于-XX:MaxTenuringThreshold时,就进入老年代。一般-XX:MaxTenuringThreshold设置为15。
2.4 动态对象年龄设定
Servior中相同年龄的对象大于等于一半时,大于等于该年龄的对象进入老年代,
2.5 空间分配担保
老年代的连续空间大于新生代对象时,就可以进行Minor GC,如果小于,看HandlePromotionFailure设置值是否允许担保失败,若允许且老年代的所剩空间大于历次进入到老年代的对象的平均,可以进行Minor GC,否则可以进行Full GC。
三、垃圾收集算法
3.1 标记清除算法
- 要回收的对象标记
- 清除标记的对象
缺点:
会产生不连续的碎片空间
3.2 复制算法
空间分成两部分,将第一部分存活的对象按照顺序复制到第二部分,将第一部分空间清空
优点:
不会产生不连续的空间
缺点:
对象存活率高的话,需要进行大量复制,效率低
空间可利用率低
依赖老年代进行分配担保
3.3标记-整理算法
- 将要回收的对象标记
- 将存活的对象复制到一端
- 清除要回收的对象
优点:
空间利用率高
缺点:
对象存活率高需要大量复制
3.4 分代收集算法
根据对象存活周期将对象一般分为新生代和老年代,新生代每次有大量对象死去,使用复制算法,老年代存活率高,使用标记-清除或者标记-整理算法。
四、判断对象已死
4.1 引用计数器
给对象添加一个引用计数器,每当有一个地方引用时,引用计数器的值就加1,引用失效 时,计数器的值就减一,当引用计数器的值为0时,代表该对象不再被引用,就可以清除。
缺点:
对循环引用的情况不起作用
4.2 可达性分析算法
以GC ROOT作为起点,向下搜索,能达到的对象,则该对象还存活着,否则,该对象已死,GC ROOT向下搜索的路径称为引用链。
4.3 引用的四种类型
-
强引用
只要强引用还在,对象就不会被回收。类似A a = new A(); -
软引用
描述有用但非必须的对象,系统将要发生溢出异常之前,会被列为第二次回收的范围 -
弱引用
描述非必须对象,被弱引用关联的对象只能生存到下一次垃圾收集发生之前。 -
虚引用
存不存在虚引用对是否不回收没任何影响。
4.4 最终判断
可达性算法只是第一步,可达性未达标记一次,并进行筛选,筛选的条件是此对象是否必要执行finalize()方法,当对象未覆盖finalize()方法,或者finalize()方法已经被虚拟机调用过,则没必要执行筛选。
若有必要筛选吧,则放在F-Queue队列中,并稍后在由一个虚拟机自动建立的,低优先级的Finalizer线程去执行他。执行只是触发该方法,并不会等他结束。防止finalize()执行太慢,形成死循环。
在F-Queue队列中,会进行第二次标记,如果重新建立引用链,则不会进行标记,否则就要被进行回收了。
任何一个对象的finalize()方法只会被系统执行一次哦。
4.5 判断是否为无用的类
- 该类所有实例已经被回收
- 加载该类的ClassLoader已经被回收
- 该类对应的java.long.Class没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。
4.6 垃圾回收的内容
- 废弃常量
- 无用的类
4.7 回收方法区
效率太低