1、 类加载机制:
- 引导类(Bootstrap )加载器:负责加载支撑JVM运行的位于JRE的lib目录下的核心类库,比如 rt.jar、charsets.jar等
- 扩展类(Extension )加载器:负责加载支撑JVM运行的位于JRE的lib目录下的ext扩展目录中的JAR 类包
- 应用程序类(APP)加载器:负责加载ClassPath路径下的类包,主要就是加载你自己写的那些类
- 自定义加载器(如Tomcat自定义加载器):负责加载用户自定义路径下的类包
2、双亲委派机制:
加载某个类时会先委托父加载器寻找目标类,找不到再 委托上层父加载器加载,如果所有父加载器在自己的加载类路径下都找不到目标类,则在自己的 类加载路径中查找并载入目标类
图中是JVM的内存模型,主要包括:堆、虚拟机栈、本地方法栈、程序计数器、方法区
JVM内存区域总共分为两种类型
- 线程私有区域:程序计数器、本地方法栈和虚拟机栈
- 线程共享区域:堆(heap)和方法区
3、垃圾回收:
Gcroot:程序执行过程中所有的对象都可以称为gcroot,运用可达性分析算法一直去找gcroot的引用知道最后,然后把这些都标记为非垃圾对象。其他的标记为垃圾对象
程序执行中当Eden放满之后会出发minor gc,清理内存,此时会清理所有区的内存,如果eden中的对象被标记为非垃圾,则分代年龄+1,并移到suivivor区, 本身就在suivivor区的如果依旧存或,分代年龄+1。当在S区的对象分代年龄>15时,会转移到老年代。
对象的分代年龄是放在对象头中的。
4、栈->堆:栈帧中局部变量位置定义的对象,实际时存放在堆中的,所以此时局部变量表是对堆的引用即栈和对的关系就是对栈帧中对象的引用
5、方法区->堆:方法区中的常量+静态常量+类信息也有可能是对象,所以对象也存放在堆中,也是对堆中的引用
6、JVM运行时内存分配
Java堆内存可以分为:新生代(Enden、SurvivorFrom和SurvivorTo)老年代、元空间
- 新生代与老年代默认比例是1:2
- 新生代中Enden SurvivorFrom SurvivorTo 默认比例是:8:1:1
7、在Java8中,永久代已经被移除,被一个称为“元数据区”(元空间)的区域所取代。元空间的本质和永久代类似,元空间与永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存。因此,默认情况下,元空间的大小仅受本地内存限制。类的元数据放入 native memory, 字符串池和类的静态变量放入 java 堆中,这样可以加载多少类的元数据就不再由MaxPermSize 控制, 而由系统的实际可用空间来控制。
8、如何确定当前对象是个垃圾
(1).引用计数法
在 Java 中,引用和对象是有关联的。如果要操作对象则必须用引用进行。因此,很显然一个简单的办法是通过引用计数来判断一个对象是否可以回收。简单说,即一个对象如果没有任何与之关联的引用,即他们的引用计数都不为 0,则说明对象不太可能再被用到,那么个对象就是可回收对象。
(2).可达性分析
为了解决引用计数法的循环引用问题,Java 使用了可达性分析的方法。通过一系列的“GC roots”对象作为起点搜索。如果在“GC roots”和一个对象之间没有可达路径,则称该对象是不可达的。要注意的是,不可达对象不等价于可回收对象,不可达对象变为可回收对象至少要经过两次标记过程。两次标记后仍然是可回收对象,则将面临回收。
9、GCrooot 包括哪些?
- 虚拟机栈(栈桢中的局部变量表操作数栈)中的引用的对象
- 方法区中的类静态属性引用的对象
- 方法区中的常量引用的对象
- 本地方法栈中的引用的对象
10、GC算法有哪些?
(1)标记-清除算法( Mark-Sweep )
分为两个阶段,标注和清除。标记阶段标记出所有需要回收的对象,清除阶段回收被标记的对象所占用的空间。该算法最大的问题是:内存碎片化严重,效率低后续可能发生大对象不能找到可利用空间的问题
(2)标记-复制算法(Mark-Copying)
按内存容量将内存划分为等大小的两块。每次只使用其中一块,当这一块内存满后将尚存活的对象复制到另一块上去,把已使用的内存清掉,这种算法虽然实现简单,内存效率高,不易产生碎片,但是最大的问题是:可用内存被压缩到了原本的一半。且存活对象增多的话,Copying 算法的效率会大大降低。
(3)标记-整理算法(Mark-Compact)-------整合了上面两种
结合了以上两个算法,为了避免缺陷而提出。标记阶段和 Mark-Sweep 算法相同,标记后不是清理对象,而是将存活对象移向内存的一端。然后清除端边界外的对象。
11、内存泄漏和内存溢出有什么区别
(1). 内存泄漏 memory leak
是指程序在申请内存后,无法释放已申请的内存空间,一次内存泄漏似乎不会有大的影响,但内存泄漏堆积后的后果就是内存溢出。
(2)、内存溢出 out of memory
指程序申请内存时,没有足够的内存供申请者使用,或者说,给了你一块存储int类型数据的存储空间,但是你却存储long类型的数据,那么结果就是内存不够用,此时就会报错OOM,即所谓的内存溢出。
12、垃圾回收器
- UserSerialGC:串行垃圾收集器
- UserParallelGC:并行垃圾收集器
- UseParNewGC:年轻代的并行垃圾回收器
- UserSerialOldGC:串行老年代垃圾收集器(已经被移除)
- UseParallelOldGC:老年代的并行垃圾回收器
- UseConcMarkSweepGC:(CMS)并发标记清除
- UseG1GC:G1垃圾收集器
13、CMS 和 G1垃圾回收器
- 1. G1能充分利用多CPU、多核环境硬件优势,尽量缩短STW。
- 2. G1整体上采用标记-整理算法,局部是通过复制算法,不会产生内存碎片。
- 3. 宏观上看G1之中不再区分年轻代和老年代。把内存划分成多个独立的子区域(Region),可以近似理
- 解为一个围棋的棋盘。
- 4. G1收集器里面讲整个的内存区都混合在一起了,但其本身依然在小范围内要进行年轻代和老年代的
- 区分,保留了新生代和老年代,但它们不再是物理隔离的,而是一部分Region的集合且不需要
- Region是连续的,也就是说依然会采用不同的GC方式来处理不同的区域。
- 5. G1虽然也是分代收集器,但整个内存分区不存在物理上的年轻代与老年代的区别,也不需要完全独
- 立的survivor(to space)堆做复制准备。G1只有逻辑上的分代概念,或者说每个分区都可能随G1的
- 运行在不同代之间前后切换。
- G1不会产生内碎片
- 是可以精准控制停顿。该收集器是把整个堆(新生代、老年代)划分成多个固定大小的区域,每次
- 根据允许停顿的时间去收集垃圾最多的区域。
#### CMS收集器
官网: https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/cms.html#concurrent_mark_sweep_cms_collector
CMS(Concurrent Mark Sweep)收集器是一种以获取 最短回收停顿时间为目标的收集器。
采用的是"标记-清除算法",整个过程分为4步
(1)初始标记 CMS initial mark 标记GC Roots直接关联对象,不用Tracing,速度很快
(2)并发标记 CMS concurrent mark 进行GC Roots Tracing
(3)重新标记 CMS remark 修改并发标记因用户程序变动的内容
(4)并发清除 CMS concurrent sweep 清除不可达对象回收空间,同时有新垃圾产生,留着下次清理称为浮动垃圾
由于整个过程中,并发标记和并发清除,收集器线程可以与用户线程一起工作,所以总体上来说,CMS收集器的内存回收过程是与用户线程一起并发地执行的。
优点:并发收集、低停顿
缺点:产生大量空间碎片、并发阶段会降低吞吐量
#### G1(Garbage-First)
官网: https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/g1_gc.html#garbage_first_garbage_collection
**使用G1收集器时,Java堆的内存布局与就与其他收集器有很大差别,它将整个Java堆划分为多个大小相等的独立区域(Region),虽然还保留有新生代和老年代的概念,但新生代和老年代不再是物理隔离的了,它们都是一部分Region(不需要连续)的集合。 **
1、每个Region大小都是一样的,可以是1M到32M之间的数值,但是必须保证是2的n次幂
2、如果对象太大,一个Region放不下[超过Region大小的50%],那么就会直接放到H中
3、设置Region大小:-XX:G1HeapRegionSize=M
4、所谓Garbage-Frist,其实就是优先回收垃圾最多的Region区域
其优点为:
(1)分代收集(仍然保留了分代的概念)
(2)空间整合(整体上属于“标记-整理”算法,不会导致空间碎片)
(3)可预测的停顿(比CMS更先进的地方在于能让使用者明确指定一个长度为M毫秒的时间片段内,消耗在垃圾收集上的时间不得超过N毫秒)
工作过程可以分为如下几步
1、初始标记(Initial Marking) 标记以下GC Roots能够关联的对象,并且修改TAMS的值,需要暂停用户线程
2、并发标记(Concurrent Marking) 从GC Roots进行可达性分析,找出存活的对象,与用户线程并发执行
3、最终标记(Final Marking) 修正在并发标记阶段因为用户程序的并发执行导致变动的数据,需暂停用户线程
4、筛选回收(Live Data Counting and Evacuation) 对各个Region的回收价值和成本进行排序,根据用户所期望的GC停顿时间制定回收计划