JVM知识点整理
JVM
一 JVM架构图(从图中理解JVM)
1.1 运行时数据区域
- 方法区(线程共享)常量,静态变量JJT(即使编译器)编译后代码也在方法区存放
- 堆内存(线程共享)垃圾回收的主要场地
- 程序计数器 当前线程执行的字节码的位置指示器
- Java虚拟机栈(栈内存):保存局部变量,基本数据类型以及堆内存中对象的引用变量
- 本地方法栈
1.2 执行引擎
1.3 类加载子系统
1.4 堆的回收区域
1.4 GC算法
1.4.1 虚拟机栈
每一个方法执行都会创建一个栈帧用于存储局部变量,操作数栈,动态链接,方法出口等信息
1.4.2 本地方法栈
native接口等
1.4.3 堆与非堆(方法区)
相同点:存放的对象是线程之间共享的内存区域
不同点:
非堆(永久代):类信息,常量,静态变量
堆(新生代+老年代):存放对象实例
1.4.4 判断对象是否存活
因为GC频繁活动于堆中,所以在GC是否要回收的时候,必须使用到是否回收的算法,GC Roots 链路可以较好的判断此对象是否还在执行链路中,是否有存在的必要
- 引用计数法和可达性分析算法,可达性分析算法主要通过引用链来判断。
- GC Roots 四种对象:(虚拟机栈中引用的对象,类静态属性的引用对象,常量引用的对象,本地方法栈中JNI引用的对象)
- 因为在查找引用链路中,需要保证引用链路的一致性,就是在分析过程中需要对象的引用关系不在发生变化,否则分析准确性则无法得到保证,所以需要安全点和安全区域,可以让GC 进行 stop the word操作,发出中断事件。
1.4.5 GC回收算法
- 标记/清除算法:
标记阶段:从根节点出发,为所有可达对象标记,一般是在对象的header中,将其记录为可达对象、
清楚阶段:在GC时,发出线程中断事件,停止了所有的java线程,根据可达性分析来清楚不可达对象 - 复制算法:
将可用内存按容量划分为大小相等的两块,每次使用其中的一块。当这一块的内存用完了,就将还存活的对象复制到另一块内存上,然后把这一块内存所有的对象一次性清理掉。新的一块只需要按顺序分配内存。
缺点:浪费了一半的内存区域
适用于新生代存活率低的对象。按序分配对象 - 标记/整理算法:
存活的对象按规则排列存放在内存中。这样一来,当我们给新对象分配内存时,jvm只需要持有内存的起始地址即可。标记/整理算法不仅弥补了标记/清除算法存在内存碎片的问题,也消除了复制算法内存减半的高额代价。
因为经过了整理以后,时一块连续性的内存地址,所以在分配的时候只要知道开始结束两个地址就可以进行合理的内存分配了。
适用老年代。因为不需要额外的空间进行担保
1.4.6 垃圾收集器年轻代收集器
- 年轻代收集器:Serial、ParNew、Parallel Scavenge
- 老年代收集器:Serial Old、Parallel Old、CMS收集器
- 特殊收集器:G1收集器[新型,不在年轻、老年代范畴内]
1.4.7 对象如何进入老年代
每熬过一次GC,年龄加一,默认是15,超过15阈值进入老年代
二 类加载顺序
重点:
- 加载过程中会检查类是否已经被加载,检查顺序自底向上,保证某个classloader已加载就视为已加载此类。而加载的顺序是自顶向下,由上层来逐层尝试加载此类
- 在加载类的过程当中,每个类加载器首先都会将加载任务上交给其父类,如果其父找不到,在由自己去加载(其实就是双亲委派的模型)
三 自定义类加载器
:
1、遵守双亲委派模型:继承ClassLoader,重写findClass()方法。
2、破坏双亲委派模型:继承ClassLoader,重写loadClass()方法
- 利用当前类加载器
Class.forName();
- 通过系统类加载器
Classloader.getSystemClassLoader().loadClass();
- 通过上下文类加载器
Thread.currentThread().getContextClassLoader().loadClass();
- 利用URLClassLoader进行加载:
URLClassLoader loader=new URLClassLoader();
loader.loadClass();