JVM由两个子系统和两个组件组成

两个子系统分别是类加载器和执行引擎,两个组件分别是运行时数据区和本地接口。

了解JVM_加载

类加载器:将类加载到方法区。

五个步骤:

    1、加载:将class载入内存,并在堆中生成java.lang.class对象,作为方法区数据入口
   2、验证:验证class是否符合JVM规范
3、准备:给静态变量分配内存空间和初始化,此时的初始化只是给一个默认值还不是准确的值。
   4、解析:将符号引用解析成直接引语,直接指向内存地址
   5、初始化:给静态变量和静态代码块赋初始值

加载机制:双亲委派模型

了解JVM_加载_02

启动类加载器→扩展类加载器→系统类加载器→自定义类加载器。当加载器接收到加载请求时,会先将加载任务委派给父加载器,同时父加载器也会总 同样的操作。当发现父加载器已经加载了对应的类时,直接使用父加载器加载的类,若父加载器无法加载到对应的类,则再将加载任务返回给子加载器 进行加载。目的是,防止有人想替换系统级别的类,比如String.class等,防止危险代码的植入。

扩展知识(处理相同限定名的类):系统会优先加载jdk下的jar包文件,而第二优先级会加载项目中编译成的class文件,而第三优先级则是加载我自定义 的jar包。或者可以把优先级高的放在lib或者ext目录下交由父加载器加载。

执行引擎:解释器+即时编译器+垃圾回收

解释器:翻译成本地机器指令

即时编译器:编译成机器码

运行时数据区:

了解JVM_加载器_03

程序计数器:记录程序执行字节码的行号。

虚拟机栈:执行方法时,创建一个栈帧,保存方法的局部变量、动态链接、操作数栈以及方法出口

本地方法栈:功能和虚拟机栈一样,服务于本地方法。

方法区:也叫永久代,保存常量、静态变量和加载的class文件,JDK1.8之后叫元数据区,使用本地内存。

堆:保存了几乎所有的对象和数组。

 

堆栈区别:

1、堆的物理内存分配不联系,因此效率比较低;栈的物理内存分配连续,效率较高。

2、堆保存对象实例和数组;栈保存局部变量、操作数栈、动态链接和方法出口等。

3、堆线程共享;栈线程私有,生命周期和线程一致。

4、堆的内存运行时分配,大小不固定,一般远大于栈;栈编译时分配,大小固定。

 

本地方法区:保存本地方法,即native修饰的方法,不可见。

 

垃圾回收

垃圾判断方式:引用计数法(无法解决循环引用问题)、可达性分析法。

可达性分析法:JVM设置了一些GC Roots,由GC Roots向下搜索,搜索的路径叫引用链,当一个对象没有任何引用链链接时,即判断为垃圾。

GC Roots:局部变量的引用、常量的引用、静态变量的引用、本地方法中的引用。这些都是活对象,GC Roots本质是通过找出所有活对象来把其余空间认定为“无用”。

 

垃圾收集算法:

标记清除:实现简单,不需要移动对象;效率慢,会产生内存碎片。

了解JVM_类加载器_04

标记整理:解决了标记清除的内存碎片问题,效率更慢。

了解JVM_加载_05

复制算法:将内存分为两半,一半放置对象一半空闲,垃圾收集的时候,对放置对象的存活对象复制到空闲那一半,然后清空原来放置对象的那一半。效率 高,不会产生内存碎片,但是内存空间使用率低。

了解JVM_加载器_06

分代收集:根据对象的生命周期,将JVM分为新生代和老年代不同的代使用不同的算法,新生代为复制算法,老年代为标记清除或者标记整理。

minor GC:发生在新生代的GC,新生代分为一个Edon区和两个survivor区。使用的是复制算法,大概流程分为,Edon区内存不足时触发minorGC时,会对 Edon区和放置存活对象的survivor区进行垃圾回收,垃圾回收之后存回的对象,会放置到刚才空闲的survivor区,同时会给存活对象设置年龄+1, 不同的垃圾收集 器会有不同的阈值,当年龄达到阈值时,对象会进入老年代。

major GC:发生在老年代的GC,触发条件包括:1、minor GC存活的对象大于老年代剩余空间;2、每次晋升老年代的平均大小大于老年大剩余空间;3、永久 代满了或者超过临界值;4、放置很大的对象时。

Full GC:minor GC+minor GC

空间担保机制:原因是因为新生代采用复制算法,加入大量对象在minor GC之后依然存活,而sirvivor的空间是比较小的,这时候就需要老年到进行空间担保。

担保过程:在发送minor GC之前,JVM会先检查老年代的最大空闲空间是否大于新生代所有对象所占空间总和,如果大于,则此次minor GC安 全,如果小于,则查看是否允许担保失败的设置值是否为true,如果为true,则检查下老年代空间是否大于历次进入老年代的平均 大小如果大于,则进行有风险的minor GC,如果小于或者是否允许担保失败的设置值为false,则进行Full GC。

垃圾收集器:serial、serial old 、parNew、CMS、G1

CMS收集器

1、初始标记:标记GCroots 能关联的对象,速度快,需要暂停所有工作线程。 2、并发标记:GC ROOTs 跟踪过程,不需要暂停其他线程 3、重新标记:标记那些那些并发标记过程中,状态发生变化的对象,需要暂停所有工作线程 4、并发清除:清理GC ROOTS不可达的对象,不需要暂停工作线程

G1收集器 1、标记整理算法,不会有内存碎片 2、准确控制停顿时间,在不牺牲吞吐量的情况下,实现低停顿垃圾回收(将堆分为几个大小固定的区域,跟踪这些区域垃圾收集进度,维护一个优先级列 表。区域和优先级的划分,确保了G在有限的时间内高效地回收垃圾)

GVM调优

设置新生代大小,新生代老年代比例,edon区和survivor区的比例等;

秒杀系统,大量对象同时产生,这些对象的生命周期很短,可以适当扩大新生代的Edon区 Edon也不是越大越好,太大的话垃圾收集的时间会变长

EDON区和survivor区的比例也不能设置太大 不然会导致survivor放不下,进而进入老年代

常见问题解决定位方式

1、top命令、vmstat命令查看CPU、内存使用情况→jps命令查看java进程号→ps -m查看线程号→jstack命令查看堆栈信息,查看具体代码是不是死锁等;

2、通过一些工具比如阿里巴巴的阿尔萨斯arthas定位具体问题。

3、打印GC日志,查看下是否频繁触发full GC。

引用类型:

强引用-不会被垃圾回收器回收的引用

软引用-内存溢出之前被回收的引用

弱引用-下次垃圾回收的时候需要被回收的引用

虚引用-垃圾回收返回通知

内存泄漏: 当长生命周期的对象引用短生命周期的引用,有可能发生内存泄漏。比如讲一个对象置入缓存当中,这个对象虽然没有再被调用,但是由于缓存一直在被使用, 因此该对象的内存得不到释放。