通过一系列 GCRoots 对象作为起始点,向下搜索形成引用链,以此证明对象是否可用。
GC Roots对象:
1. 虚拟机栈的对象
2. 方法区的静态变量和常量引用的对象
3. Native方法引用的对象
ps:在大量使用反射、动态代理、CGLib等ByteCode框架,动态生成JSP以及OSGi这类频繁自定义ClassLoader的场景,需要虚拟机具备类卸载的功能,以保证永久代不会溢出。
判断为“无用的类”的条件:
(1)该类的所有实例都已经被回收
(2)加载该类的ClassLoader已经被回收
(3)该类对应的java.lang.Class对象没有在任何地方被引用
标记-清除算法
复制算法(新生代常用)
Eden :Survivor1 :Survivor2 =8 :1 :1
每次使用Eden 和其中一个Survivor,当用于存放存活对象的Survivor内存不足时,会被移至老生代
标记-整理算法
Serial收集器
最基本,历史最悠久,Stop the world,client模式下默认的新生代收集器ParNew收集器
Serial收集器的多线程版本,server模式下首选的新生代收集器(除了Serial收集器以外,目前只能和CMS收集器配合使用)Parallel Scavenge收集器
新生代收集器,”吞吐量优先”的收集器,并行的多线程收集器
Serial Old收集器
Serial收集器的老年代版本
Parallel Old收集器
ParallelScavenge收集器的老年代版本,多线程,可以和Parallel Scavenge收集器搭配,适用于注重于吞吐量以及CPU敏感的场合
CMS收集器
CMS(ConcurrentMark Sweep)以最短回收停顿时间为目标,标记-清除算法实现分为四个阶段:
1. 初始标记 : 标记GC Roots直接关联的对象,stop the world,时间短
2. 并发标记:GC Roots Tracing
3. 重新标记:修正并发标记期间状态变化的对象
4. 并发清除
G1收集器
面向服务端应用的收集器,HotSpot团队赋予它的使命是,代替JDK1.5版本的CMS收集器,不需要其他收集器配合,可独立管理GC堆,并行和并发,把Java堆分为多个Region,化整为零的模式
jps (JVM Process Status Tool) 虚拟机进程状况工具
jstat (JVM Statistics Monitoring Tool) 虚拟机统计信息监视工具,监视类加载,内存,垃圾收集等运行数据
jmap (Memory Map for Java) 生成堆转储快照
jstack (Stack Trace for Java) 用于生成虚拟机当前时刻的线程快照,一般称为threaddump或javacore文件,以定位线程长时间停顿的原因
JConsole 基于JMX的可视化监视、管理工具
VisualVM 多合一故障处理工具,非常强大的运行监视和故障处理程序
7.类加载的周期①通过类的全限定名获取二进制字节流
②将字节流代表的静态存储结构转化成方法区的运行时数据结构
③在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口
确保Java文件的信息符合当前虚拟机的要求
为类变量分配内存,并设置初始值
虚拟机将常量池中的符号引用替换为直接引用的过程(符号引用在Class文件中以CONSTANT_Class_info等常量形式出现)
执行类构造器
一个类加载器收到类加载的请求,它首先不会自己尝试加载这个类,而是把这个请求委派给父类加载器去完成,每个层次的类加载器都是如此,因此所有的请求最终都应该送到顶层的启动类加载器中,只有当父类加载器反馈无法完成加载请求时,子加载器才会尝试自己去加载。
泛型是JDK 1.5的一项新特性,本质是参数化类型(Parametersized Type)的应用,也就是说所操作的数据类型是一种参数.
Java语言的泛型只在源码中存在,编译后的字节码文件中被替换为原生类型,并在相应地方插入强制转换代码,因此对于运行期的Java来说,ArrayList
语法糖(编译期变化):
自动拆装箱发生在运算操作是触发;
遍历循环则把代码还原成了迭代器的实现,这也是为何遍历循环需要被遍历的类实现Iterable接口的原因;
①内存可见性;②禁止指令重排序优化
volatile变量读操作的性能消耗与普通变量几乎没有什么差别,但是写操作则可能会慢一些,因为它需要在本地代码中插入内存屏障指令来保证处理器不发生乱序执行。不过即便如此,大多数场景下volatile的总开销仍然要比锁低,我们在volatile与锁之中选择的唯一依据仅仅是volatile的语义能否满足使用场景的需求。
判断数据是否存在竞争、线程是否安全的主要依据,依照这个原则,可以通过以下几个规则解决在并发环境下两个操作是否存在冲突的所有问题。
① 程序次序规则:在一个线程内,按照程序代码顺序,书写在前面的操作先行发生于书写在后面的操作。
② 管程锁定规则:一个unlock操作先行于后面对同一个锁的lock操作。
③ volatile变量规则:对一个volatile的写操作先行于后面对这个变量的读操作。
④ 程序启动规则:Thread的start()方法先行发生于此线程的每个操作。
⑤ 程序终止规则:线程的所有操作先行于对此线程的终止检测。
⑥ 线程中断规则:对线程的interrupt()方法的调用先行发生于被中断线程的代码检测中断事件的发生。
⑦ 对象终结规则:一个对象的初始化完成先行于这个对象的finalize()方法的开始。
ps:时间上的先后顺序与先行发生原则之间基本上没有太大的关系。
新建(New)、运行(Runable)、无限期等待(Waiting)、限期等待(Timed Waiting)、阻塞(Blocked)、结束(Terminated)
等待状态和阻塞状态的区别:
阻塞指的是等待获取一个排它锁,等待指的是等待一段时间或者等待唤醒动作的发生。
进入等待状态的事件包括Thread.sleep(),Object.wait(),Object.join()等
互斥同步(阻塞同步):
悲观的同步策略。在Java中,最基本的就是使用synchronized关键字,同步块前后形成monitorenter和monitorexit指令,这两个指令的具体操作是使锁计数器加1和减1,当计数器为0,锁为释放状态。
其次,可以通过ReentrantLock实现,比synchronized多了一些特性,包括等待可中断、可实现公平锁、锁可以绑定多个条件。公平锁指的是按照时间顺序公平获取锁,synchronized是非公平锁,ReentrantLock默认也是非公平锁,可通过带布尔值的构造实现公平锁。
JDK 1.5以下版本,在多线程环境下,synchronized的吞吐量下降严重,JDK1.6以上性能上两者基本相同,未来性能改进会更偏向原生的synchronized,而且ReentrantLock需要手动释放锁,所以优先使用synchronized进行同步。
非阻塞同步:
基于冲突检测的乐观同步策略。在JDK1.5以上版本,实现CAS机制,通过sun.misc.Unsafe类来操作。