1.jvm的生命周期
(1) JVM在它的生存周期中有一个明确的任务,那就是运行Java程序。因此当Java程序启动的时候,就产生JVM的一个实例;当程序运行结束的时候,该实例也跟着消失了
(2) Java虚拟机总是开始于一个main()方法,这个方法必须是公有、返回void、直接受一个字符串数组。
在程序执行时,你必须给Java虚拟机指明这个包含main()方法的类名。(可以在pom文件打包配置中指定)
Main()方法是程序的起点,他被执行的线程初始化为程序的初始线程。程序中其他的线程都由他来启动。
Java中的线程分为两种:守护线程 (daemon)和普通线程(non-daemon)。守护线程是Java虚拟机自己使用的线程,比如负责垃圾收集的线程就是一个守护线程。
2.java的内存模型 (JMM)
目的:解决Java多线程对共享数据的读写一致性问题
Java内存模型和jvm内存模型本没有关系,勉强对应,从变量,主内存,工作内存的定义看,主内存主要对应于java堆中的对象实例数据部分,工作内存则对应于虚拟机栈中的部分区域
主要对应于Java堆中的对象实例数据部分
(1) 主内存:
Java中所有变量都储存在主存中,对于所有线程都是共享的
(2) 工作内存:
每个线程都有自己的执行空间(即工作内存),线程执行的时候用到某变量,首先要将变量从主内存拷贝的自己的工作内存空间,然后对变量进行操作:读取,修改,赋值等,这些均在工作内存完 成,操作完成后再将变量写回主内存;
3.jvm的运行时数据区
(1) 线程私有区:每个线程私有的,线程私有数据区域生命周期与线程相同, 依赖用户线程的启动/结束而创建/销毁
| - 程序计数器:
i:当前线程所执行的行号指示器。(正在执行的虚拟机字节码指令的地址)
ii:通过改变计数器的值来确定下一条指令,比如循环,分支,跳转,异常处理,线程恢复等都是依赖计数器来完成。
| - Java虚拟机栈:用于执行Java方法
| - 本地方法栈:
i:一个Native Method就是一个java调用非java代码的接口
(2) 线程共享区
| - Java堆:
i:jvm管理的内存中最大的一块
ii:用于存放对象实例,数组等
iii:堆分代:新生代,老年代的默认比例是1:2
新生代(MinorGC次收集,频率高):包含Eden区,From Survivor区,To Survivor区 默认比例是8:1:1,所有新建的对象存放在Eden区,经过GC存活下来的对象存放在Survivor区
老年代(MajorGC 主收集,频率低):存放生命周期长的内存对象,新生代的对象经过N次GC存活下来,进入老年代
永久代: jdk1.7版本之后字符串常量池从永久代移除
1.8版本后永久代被移除,替代为元数据区Metaspace,元空间并不在虚拟机中,而是使用本地内存
GC不会在主程序运行期对永久区域进行清理
| - 方法区:
i:被JVM加载的类信息、常量、静态变量、即时编译器编译后的代码
ii:运行时常量池 ---------- 编译器和运行期(String 的 intern() )都可以将常量放入池中
== 直接内存
i:并不是虚拟机运行时数据区的一部分,也不是Java虚拟机规范中定义的内存区域,但是这部分内存也被频繁地使用
ii:受到本机总内存大小及处理器寻址空间的限制
== 直接内存(堆外内存)与堆内存比较
i:直接内存申请空间耗费更高的性能,当频繁申请到一定量时尤为明显
ii:直接内存IO读写的性能要优于普通的堆内存,在多次读写操作的情况下差异明显
4.GC算法
(1) 标记清除法/标记压缩法
标记阶段:通过可达性分析算法,遍历所有的GC Roots对象,对从GC Roots对象可达的对象都打上一个标识,一般是在对象的header中,将其记录为可达对象
清除阶段:清除的过程是对堆内存进行遍历,如果发现某个对象没有被标记为可达对象(通过读取对象header信息),则将其回收
缺点:
i:效率问题。标记和清除阶段的效率都不高(因为都需要遍历内存中的对象),而且GC时需要停止应用程序
ii:空间问题。标记清除后村产生大量不连续的内存碎片,会导致在程序运行过程中需要分配较大对象时,因无足够的连续内存会提前触发垃圾回收动作
(2)复制收集算法
原理:将可用内存按容量划分为大小相等的两块,每次使用其中的一块。当这一块的内存用完了,就将还存活的对象复制到另一块内存上,然后把这一块内存所有的对象一次性清理掉
优点:简单高效,优化了标记/清除算法的效率低,内存碎片问题。(将存活对象复制到保留区域时也只按地址顺序存储的)
缺点:
i:将内存缩小为原来的一半,浪费了一半的内存空间,代价太高;
ii:如果对象的存活率很高,极端一点的情况假设对象存活率为100%,那么我们需要将所有存活的对象复制一遍,耗费的时间代价也是不可忽视的。
(3)标记/整理算法
原理:先标记出存活对象,然后让多有存活的对象都向一端移动,之后直接清理掉端边线以外的内存。
缺点:标记/整理算法的缺点就是效率也不高,不仅要标记存活对象,还要整理所有存活对象的引用地址。
效率:复制算法 > 标记/整理算法 > 标记/清除算法
(4)分代收集算法
新生代:复制收集算法(但是划分为Eden,From Survivor,To Survivor三个区域,比例为8:1:1)
GC开始时,对象存放于Eden区和From Survivor区,To Survivor区是空的,GC时,Eden区所有对象会被复制到To区,
From区的对象会根据GC存活的年龄决定是转移到年老区还是复制到To区,GC后,Eden区和From区都会被清空,
然后From区和To区会交换位置,保证To区是空的,然后进行下一次GC;
老年代: 标记/整理算法或者标记/清除算法
当GC线程启动时(即进行垃圾收集),应用程序都要暂停(Stop The World)
5.Full GC触发条件:Full GC是针对整个堆
(1)年老代(Tenured)被写满
(2)持久代(Perm)被写满 ---- 垃圾回收不会发生在永久代,如果永久代满了或者是超过了临界值,会触发完全垃圾回收(FullGC)
(3)System.gc()被显示调用
(4)堆中分配很大的对象 ----- 所谓大对象,是指需要大量连续内存空间的java对象,例如很长的数组,此种对象会直接进入老年代,而老年代虽然有很大的剩余空间,但是无法找到足够大的连续空间来分配给当前对象,
此种情况就会触发JVM进行Full GC
(5)CMS GC时出现promotion failed和concurrent mode failure
(6)统计得到的Minor GC晋升到旧生代的平均大小大于老年代的剩余空间
6.垃圾回收器
(1)Serial 收集器:单线程收集器,GC采用的是分代收集算法。
(2)ParNew 收集器:多线程收集器,和Serial原理相同
(3)Parallel Scavenge 收集器:新生代收集器,也是使用复制算法实现,同时也是并行的多线程收集器
(4)Serial Old 收集器:收集器的老年代版本,单线程,使用 标记/整理算法
(5)Parallel Old 收集器: Parallel Scavenge 收集器的老年代版本。多线程,使用 标记 —— 整理
(6)CMS 收集器:以获取最短回收停顿时间为目标的收集器。基于 标记 —— 清除 算法实现
(7)G1 收集器:面向服务端的垃圾回收器
7.JVM参数配置
(1) JVM在它的生存周期中有一个明确的任务,那就是运行Java程序。因此当Java程序启动的时候,就产生JVM的一个实例;当程序运行结束的时候,该实例也跟着消失了
(2) Java虚拟机总是开始于一个main()方法,这个方法必须是公有、返回void、直接受一个字符串数组。
在程序执行时,你必须给Java虚拟机指明这个包含main()方法的类名。(可以在pom文件打包配置中指定)
Main()方法是程序的起点,他被执行的线程初始化为程序的初始线程。程序中其他的线程都由他来启动。
Java中的线程分为两种:守护线程 (daemon)和普通线程(non-daemon)。守护线程是Java虚拟机自己使用的线程,比如负责垃圾收集的线程就是一个守护线程。
2.java的内存模型 (JMM)
目的:解决Java多线程对共享数据的读写一致性问题
Java内存模型和jvm内存模型本没有关系,勉强对应,从变量,主内存,工作内存的定义看,主内存主要对应于java堆中的对象实例数据部分,工作内存则对应于虚拟机栈中的部分区域
主要对应于Java堆中的对象实例数据部分
(1) 主内存:
Java中所有变量都储存在主存中,对于所有线程都是共享的
(2) 工作内存:
每个线程都有自己的执行空间(即工作内存),线程执行的时候用到某变量,首先要将变量从主内存拷贝的自己的工作内存空间,然后对变量进行操作:读取,修改,赋值等,这些均在工作内存完 成,操作完成后再将变量写回主内存;
3.jvm的运行时数据区
(1) 线程私有区:每个线程私有的,线程私有数据区域生命周期与线程相同, 依赖用户线程的启动/结束而创建/销毁
| - 程序计数器:
i:当前线程所执行的行号指示器。(正在执行的虚拟机字节码指令的地址)
ii:通过改变计数器的值来确定下一条指令,比如循环,分支,跳转,异常处理,线程恢复等都是依赖计数器来完成。
| - Java虚拟机栈:用于执行Java方法
i:每个方法执行都会创建一个栈帧,用于存放局部变量表,操作数栈,动态连接,和方法返回地址等信息
当前线程 | ||||||||||
当前栈帧
|
| - 本地方法栈:
i:一个Native Method就是一个java调用非java代码的接口
(2) 线程共享区
| - Java堆:
i:jvm管理的内存中最大的一块
ii:用于存放对象实例,数组等
iii:堆分代:新生代,老年代的默认比例是1:2
新生代(MinorGC次收集,频率高):包含Eden区,From Survivor区,To Survivor区 默认比例是8:1:1,所有新建的对象存放在Eden区,经过GC存活下来的对象存放在Survivor区
老年代(MajorGC 主收集,频率低):存放生命周期长的内存对象,新生代的对象经过N次GC存活下来,进入老年代
永久代: jdk1.7版本之后字符串常量池从永久代移除
1.8版本后永久代被移除,替代为元数据区Metaspace,元空间并不在虚拟机中,而是使用本地内存
GC不会在主程序运行期对永久区域进行清理
| - 方法区:
i:被JVM加载的类信息、常量、静态变量、即时编译器编译后的代码
ii:运行时常量池 ---------- 编译器和运行期(String 的 intern() )都可以将常量放入池中
== 直接内存
i:并不是虚拟机运行时数据区的一部分,也不是Java虚拟机规范中定义的内存区域,但是这部分内存也被频繁地使用
ii:受到本机总内存大小及处理器寻址空间的限制
== 直接内存(堆外内存)与堆内存比较
i:直接内存申请空间耗费更高的性能,当频繁申请到一定量时尤为明显
ii:直接内存IO读写的性能要优于普通的堆内存,在多次读写操作的情况下差异明显
4.GC算法
(1) 标记清除法/标记压缩法
标记阶段:通过可达性分析算法,遍历所有的GC Roots对象,对从GC Roots对象可达的对象都打上一个标识,一般是在对象的header中,将其记录为可达对象
清除阶段:清除的过程是对堆内存进行遍历,如果发现某个对象没有被标记为可达对象(通过读取对象header信息),则将其回收
缺点:
i:效率问题。标记和清除阶段的效率都不高(因为都需要遍历内存中的对象),而且GC时需要停止应用程序
ii:空间问题。标记清除后村产生大量不连续的内存碎片,会导致在程序运行过程中需要分配较大对象时,因无足够的连续内存会提前触发垃圾回收动作
(2)复制收集算法
原理:将可用内存按容量划分为大小相等的两块,每次使用其中的一块。当这一块的内存用完了,就将还存活的对象复制到另一块内存上,然后把这一块内存所有的对象一次性清理掉
优点:简单高效,优化了标记/清除算法的效率低,内存碎片问题。(将存活对象复制到保留区域时也只按地址顺序存储的)
缺点:
i:将内存缩小为原来的一半,浪费了一半的内存空间,代价太高;
ii:如果对象的存活率很高,极端一点的情况假设对象存活率为100%,那么我们需要将所有存活的对象复制一遍,耗费的时间代价也是不可忽视的。
(3)标记/整理算法
原理:先标记出存活对象,然后让多有存活的对象都向一端移动,之后直接清理掉端边线以外的内存。
缺点:标记/整理算法的缺点就是效率也不高,不仅要标记存活对象,还要整理所有存活对象的引用地址。
效率:复制算法 > 标记/整理算法 > 标记/清除算法
(4)分代收集算法
新生代:复制收集算法(但是划分为Eden,From Survivor,To Survivor三个区域,比例为8:1:1)
GC开始时,对象存放于Eden区和From Survivor区,To Survivor区是空的,GC时,Eden区所有对象会被复制到To区,
From区的对象会根据GC存活的年龄决定是转移到年老区还是复制到To区,GC后,Eden区和From区都会被清空,
然后From区和To区会交换位置,保证To区是空的,然后进行下一次GC;
老年代: 标记/整理算法或者标记/清除算法
当GC线程启动时(即进行垃圾收集),应用程序都要暂停(Stop The World)
5.Full GC触发条件:Full GC是针对整个堆
(1)年老代(Tenured)被写满
(2)持久代(Perm)被写满 ---- 垃圾回收不会发生在永久代,如果永久代满了或者是超过了临界值,会触发完全垃圾回收(FullGC)
(3)System.gc()被显示调用
(4)堆中分配很大的对象 ----- 所谓大对象,是指需要大量连续内存空间的java对象,例如很长的数组,此种对象会直接进入老年代,而老年代虽然有很大的剩余空间,但是无法找到足够大的连续空间来分配给当前对象,
此种情况就会触发JVM进行Full GC
(5)CMS GC时出现promotion failed和concurrent mode failure
(6)统计得到的Minor GC晋升到旧生代的平均大小大于老年代的剩余空间
6.垃圾回收器
(1)Serial 收集器:单线程收集器,GC采用的是分代收集算法。
(2)ParNew 收集器:多线程收集器,和Serial原理相同
(3)Parallel Scavenge 收集器:新生代收集器,也是使用复制算法实现,同时也是并行的多线程收集器
(4)Serial Old 收集器:收集器的老年代版本,单线程,使用 标记/整理算法
(5)Parallel Old 收集器: Parallel Scavenge 收集器的老年代版本。多线程,使用 标记 —— 整理
(6)CMS 收集器:以获取最短回收停顿时间为目标的收集器。基于 标记 —— 清除 算法实现
(7)G1 收集器:面向服务端的垃圾回收器
7.JVM参数配置